Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenslang_preprocess.c
Go to the documentation of this file.
00001 /* 00002 * Mesa 3-D graphics library 00003 * 00004 * Copyright (C) 2005-2008 Brian Paul All Rights Reserved. 00005 * Copyright (C) 2009 VMware, Inc. All Rights Reserved. 00006 * 00007 * Permission is hereby granted, free of charge, to any person obtaining a 00008 * copy of this software and associated documentation files (the "Software"), 00009 * to deal in the Software without restriction, including without limitation 00010 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00011 * and/or sell copies of the Software, and to permit persons to whom the 00012 * Software is furnished to do so, subject to the following conditions: 00013 * 00014 * The above copyright notice and this permission notice shall be included 00015 * in all copies or substantial portions of the Software. 00016 * 00017 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00018 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00019 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 00020 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 00021 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 00022 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00023 */ 00024 00031 #include "main/imports.h" 00032 #include "shader/grammar/grammar_mesa.h" 00033 #include "slang_preprocess.h" 00034 00035 LONGSTRING static const char *slang_pp_directives_syn = 00036 #include "library/slang_pp_directives_syn.h" 00037 ; 00038 00039 LONGSTRING static const char *slang_pp_expression_syn = 00040 #include "library/slang_pp_expression_syn.h" 00041 ; 00042 00043 LONGSTRING static const char *slang_pp_version_syn = 00044 #include "library/slang_pp_version_syn.h" 00045 ; 00046 00047 static GLvoid 00048 grammar_error_to_log (slang_info_log *log) 00049 { 00050 char buf[1024]; 00051 GLint pos; 00052 00053 grammar_get_last_error ((byte *) (buf), sizeof (buf), &pos); 00054 if (buf[0] == 0) { 00055 _mesa_snprintf(buf, sizeof(buf), "Preprocessor error"); 00056 } 00057 slang_info_log_error (log, buf); 00058 } 00059 00060 GLboolean 00061 _slang_preprocess_version (const char *text, GLuint *version, GLuint *eaten, slang_info_log *log) 00062 { 00063 grammar id; 00064 byte *prod, *I; 00065 unsigned int size; 00066 00067 id = grammar_load_from_text ((const byte *) (slang_pp_version_syn)); 00068 if (id == 0) { 00069 grammar_error_to_log (log); 00070 return GL_FALSE; 00071 } 00072 00073 if (!grammar_fast_check (id, (const byte *) (text), &prod, &size, 8)) { 00074 grammar_error_to_log (log); 00075 grammar_destroy (id); 00076 return GL_FALSE; 00077 } 00078 00079 /* there can be multiple #version directives - grab the last one */ 00080 I = &prod[size - 6]; 00081 *version = (GLuint) (I[0]) + (GLuint) (I[1]) * 100; 00082 *eaten = (GLuint) (I[2]) + ((GLuint) (I[3]) << 8) + ((GLuint) (I[4]) << 16) + ((GLuint) (I[5]) << 24); 00083 00084 grammar_destroy (id); 00085 grammar_alloc_free (prod); 00086 return GL_TRUE; 00087 } 00088 00089 /* 00090 * The preprocessor does the following work. 00091 * 1. Remove comments. Each comment block is replaced with a single space and if the 00092 * block contains new-lines, they are preserved. This ensures that line numbers 00093 * stay the same and if a comment block delimits two tokens, the are delitmited 00094 * by the space after comment removal. 00095 * 2. Remove preprocessor directives from the source string, checking their syntax and 00096 * executing them if appropriate. Again, new-lines are preserved. 00097 * 3. Expand macros. 00098 * 4. Tokenize the source string by ensuring there is at least one space between every 00099 * two adjacent tokens. 00100 */ 00101 00102 #define PP_ANNOTATE 0 00103 00104 static GLvoid 00105 pp_annotate (slang_string *output, const char *fmt, ...) 00106 { 00107 #if PP_ANNOTATE 00108 va_list va; 00109 char buffer[1024]; 00110 00111 va_start (va, fmt); 00112 _mesa_vsprintf (buffer, fmt, va); 00113 va_end (va); 00114 slang_string_pushs (output, buffer, _mesa_strlen (buffer)); 00115 #else 00116 (GLvoid) (output); 00117 (GLvoid) (fmt); 00118 #endif 00119 } 00120 00121 /* 00122 * The expression is executed on a fixed-sized stack. The PUSH macro makes a runtime 00123 * check if the stack is not overflown by too complex expressions. In that situation the 00124 * GLSL preprocessor should report internal compiler error. 00125 * The BINARYDIV makes a runtime check if the divider is not 0. If it is, it reports 00126 * compilation error. 00127 */ 00128 00129 #define EXECUTION_STACK_SIZE 1024 00130 00131 #define PUSH(x)\ 00132 do {\ 00133 if (sp == 0) {\ 00134 slang_info_log_error (elog, "internal compiler error: preprocessor execution stack overflow.");\ 00135 return GL_FALSE;\ 00136 }\ 00137 stack[--sp] = x;\ 00138 } while (GL_FALSE) 00139 00140 #define POP(x)\ 00141 do {\ 00142 assert (sp < EXECUTION_STACK_SIZE);\ 00143 x = stack[sp++];\ 00144 } while (GL_FALSE) 00145 00146 #define BINARY(op)\ 00147 do {\ 00148 GLint a, b;\ 00149 POP(b);\ 00150 POP(a);\ 00151 PUSH(a op b);\ 00152 } while (GL_FALSE) 00153 00154 #define BINARYDIV(op)\ 00155 do {\ 00156 GLint a, b;\ 00157 POP(b);\ 00158 POP(a);\ 00159 if (b == 0) {\ 00160 slang_info_log_error (elog, "division by zero in preprocessor expression.");\ 00161 return GL_FALSE;\ 00162 }\ 00163 PUSH(a op b);\ 00164 } while (GL_FALSE) 00165 00166 #define UNARY(op)\ 00167 do {\ 00168 GLint a;\ 00169 POP(a);\ 00170 PUSH(op a);\ 00171 } while (GL_FALSE) 00172 00173 #define OP_END 0 00174 #define OP_PUSHINT 1 00175 #define OP_LOGICALOR 2 00176 #define OP_LOGICALAND 3 00177 #define OP_OR 4 00178 #define OP_XOR 5 00179 #define OP_AND 6 00180 #define OP_EQUAL 7 00181 #define OP_NOTEQUAL 8 00182 #define OP_LESSEQUAL 9 00183 #define OP_GREATEREQUAL 10 00184 #define OP_LESS 11 00185 #define OP_GREATER 12 00186 #define OP_LEFTSHIFT 13 00187 #define OP_RIGHTSHIFT 14 00188 #define OP_ADD 15 00189 #define OP_SUBTRACT 16 00190 #define OP_MULTIPLY 17 00191 #define OP_DIVIDE 18 00192 #define OP_MODULUS 19 00193 #define OP_PLUS 20 00194 #define OP_MINUS 21 00195 #define OP_NEGATE 22 00196 #define OP_COMPLEMENT 23 00197 00198 static GLboolean 00199 execute_expression (slang_string *output, const byte *code, GLuint *pi, GLint *result, 00200 slang_info_log *elog) 00201 { 00202 GLuint i = *pi; 00203 GLint stack[EXECUTION_STACK_SIZE]; 00204 GLuint sp = EXECUTION_STACK_SIZE; 00205 00206 while (code[i] != OP_END) { 00207 switch (code[i++]) { 00208 case OP_PUSHINT: 00209 i++; 00210 PUSH(_mesa_atoi ((const char *) (&code[i]))); 00211 i += _mesa_strlen ((const char *) (&code[i])) + 1; 00212 break; 00213 case OP_LOGICALOR: 00214 BINARY(||); 00215 break; 00216 case OP_LOGICALAND: 00217 BINARY(&&); 00218 break; 00219 case OP_OR: 00220 BINARY(|); 00221 break; 00222 case OP_XOR: 00223 BINARY(^); 00224 break; 00225 case OP_AND: 00226 BINARY(&); 00227 break; 00228 case OP_EQUAL: 00229 BINARY(==); 00230 break; 00231 case OP_NOTEQUAL: 00232 BINARY(!=); 00233 break; 00234 case OP_LESSEQUAL: 00235 BINARY(<=); 00236 break; 00237 case OP_GREATEREQUAL: 00238 BINARY(>=); 00239 break; 00240 case OP_LESS: 00241 BINARY(<); 00242 break; 00243 case OP_GREATER: 00244 BINARY(>); 00245 break; 00246 case OP_LEFTSHIFT: 00247 BINARY(<<); 00248 break; 00249 case OP_RIGHTSHIFT: 00250 BINARY(>>); 00251 break; 00252 case OP_ADD: 00253 BINARY(+); 00254 break; 00255 case OP_SUBTRACT: 00256 BINARY(-); 00257 break; 00258 case OP_MULTIPLY: 00259 BINARY(*); 00260 break; 00261 case OP_DIVIDE: 00262 BINARYDIV(/); 00263 break; 00264 case OP_MODULUS: 00265 BINARYDIV(%); 00266 break; 00267 case OP_PLUS: 00268 UNARY(+); 00269 break; 00270 case OP_MINUS: 00271 UNARY(-); 00272 break; 00273 case OP_NEGATE: 00274 UNARY(!); 00275 break; 00276 case OP_COMPLEMENT: 00277 UNARY(~); 00278 break; 00279 default: 00280 assert (0); 00281 } 00282 } 00283 00284 /* Write-back the index skipping the OP_END. */ 00285 *pi = i + 1; 00286 00287 /* There should be exactly one value left on the stack. This is our result. */ 00288 POP(*result); 00289 pp_annotate (output, "%d ", *result); 00290 assert (sp == EXECUTION_STACK_SIZE); 00291 return GL_TRUE; 00292 } 00293 00294 /* 00295 * Function execute_expressions() executes up to 2 expressions. The second expression is there 00296 * for the #line directive which takes 1 or 2 expressions that indicate line and file numbers. 00297 * If it fails, it returns 0. If it succeeds, it returns the number of executed expressions. 00298 */ 00299 00300 #define EXP_END 0 00301 #define EXP_EXPRESSION 1 00302 00303 static GLuint 00304 execute_expressions (slang_string *output, grammar eid, const byte *expr, GLint results[2], 00305 slang_info_log *elog) 00306 { 00307 GLint success; 00308 byte *code; 00309 GLuint size, count = 0; 00310 00311 success = grammar_fast_check (eid, expr, &code, &size, 64); 00312 if (success) { 00313 GLuint i = 0; 00314 00315 while (code[i++] == EXP_EXPRESSION) { 00316 assert (count < 2); 00317 00318 if (!execute_expression (output, code, &i, &results[count], elog)) { 00319 count = 0; 00320 break; 00321 } 00322 count++; 00323 } 00324 grammar_alloc_free (code); 00325 } 00326 else { 00327 slang_info_log_error (elog, "syntax error in preprocessor expression.");\ 00328 } 00329 return count; 00330 } 00331 00332 /* 00333 * The pp_symbol structure is used to hold macro definitions and macro formal parameters. The 00334 * pp_symbols strcture is a collection of pp_symbol. It is used both for storing macro formal 00335 * parameters and all global macro definitions. Making this unification wastes some memory, 00336 * becuse macro formal parameters don't need further lists of symbols. We lose 8 bytes per 00337 * formal parameter here, but making this we can use the same code to substitute macro parameters 00338 * as well as macros in the source string. 00339 */ 00340 00341 typedef struct 00342 { 00343 struct pp_symbol_ *symbols; 00344 GLuint count; 00345 } pp_symbols; 00346 00347 static GLvoid 00348 pp_symbols_init (pp_symbols *self) 00349 { 00350 self->symbols = NULL; 00351 self->count = 0; 00352 } 00353 00354 static GLvoid 00355 pp_symbols_free (pp_symbols *); 00356 00357 typedef struct pp_symbol_ 00358 { 00359 slang_string name; 00360 slang_string replacement; 00361 pp_symbols parameters; 00362 } pp_symbol; 00363 00364 static GLvoid 00365 pp_symbol_init (pp_symbol *self) 00366 { 00367 slang_string_init (&self->name); 00368 slang_string_init (&self->replacement); 00369 pp_symbols_init (&self->parameters); 00370 } 00371 00372 static GLvoid 00373 pp_symbol_free (pp_symbol *self) 00374 { 00375 slang_string_free (&self->name); 00376 slang_string_free (&self->replacement); 00377 pp_symbols_free (&self->parameters); 00378 } 00379 00380 static GLvoid 00381 pp_symbol_reset (pp_symbol *self) 00382 { 00383 /* Leave symbol name intact. */ 00384 slang_string_reset (&self->replacement); 00385 pp_symbols_free (&self->parameters); 00386 pp_symbols_init (&self->parameters); 00387 } 00388 00389 static GLvoid 00390 pp_symbols_free (pp_symbols *self) 00391 { 00392 GLuint i; 00393 00394 for (i = 0; i < self->count; i++) 00395 pp_symbol_free (&self->symbols[i]); 00396 _mesa_free (self->symbols); 00397 } 00398 00399 static pp_symbol * 00400 pp_symbols_push (pp_symbols *self) 00401 { 00402 self->symbols = (pp_symbol *) (_mesa_realloc (self->symbols, self->count * sizeof (pp_symbol), 00403 (self->count + 1) * sizeof (pp_symbol))); 00404 if (self->symbols == NULL) 00405 return NULL; 00406 pp_symbol_init (&self->symbols[self->count]); 00407 return &self->symbols[self->count++]; 00408 } 00409 00410 static GLboolean 00411 pp_symbols_erase (pp_symbols *self, pp_symbol *symbol) 00412 { 00413 assert (symbol >= self->symbols && symbol < self->symbols + self->count); 00414 00415 self->count--; 00416 pp_symbol_free (symbol); 00417 if (symbol < self->symbols + self->count) 00418 _mesa_memcpy (symbol, symbol + 1, sizeof (pp_symbol) * (self->symbols + self->count - symbol)); 00419 self->symbols = (pp_symbol *) (_mesa_realloc (self->symbols, (self->count + 1) * sizeof (pp_symbol), 00420 self->count * sizeof (pp_symbol))); 00421 return self->symbols != NULL; 00422 } 00423 00424 static pp_symbol * 00425 pp_symbols_find (pp_symbols *self, const char *name) 00426 { 00427 GLuint i; 00428 00429 for (i = 0; i < self->count; i++) 00430 if (_mesa_strcmp (name, slang_string_cstr (&self->symbols[i].name)) == 0) 00431 return &self->symbols[i]; 00432 return NULL; 00433 } 00434 00435 /* 00436 * The condition context of a single #if/#else/#endif level. Those can be nested, so there 00437 * is a stack of condition contexts. 00438 * There is a special global context on the bottom of the stack. It is there to simplify 00439 * context handling. 00440 */ 00441 00442 typedef struct 00443 { 00444 GLboolean current; /* The condition value of this level. */ 00445 GLboolean effective; /* The effective product of current condition, outer level conditions 00446 * and position within #if-#else-#endif sections. */ 00447 GLboolean else_allowed; /* TRUE if in #if-#else section, FALSE if in #else-#endif section 00448 * and for global context. */ 00449 GLboolean endif_required; /* FALSE for global context only. */ 00450 } pp_cond_ctx; 00451 00452 /* Should be enuff. */ 00453 #define CONDITION_STACK_SIZE 64 00454 00455 typedef struct 00456 { 00457 pp_cond_ctx stack[CONDITION_STACK_SIZE]; 00458 pp_cond_ctx *top; 00459 } pp_cond_stack; 00460 00461 static GLboolean 00462 pp_cond_stack_push (pp_cond_stack *self, slang_info_log *elog) 00463 { 00464 if (self->top == self->stack) { 00465 slang_info_log_error (elog, "internal compiler error: preprocessor condition stack overflow."); 00466 return GL_FALSE; 00467 } 00468 self->top--; 00469 return GL_TRUE; 00470 } 00471 00472 static GLvoid 00473 pp_cond_stack_reevaluate (pp_cond_stack *self) 00474 { 00475 /* There must be at least 2 conditions on the stack - one global and one being evaluated. */ 00476 assert (self->top <= &self->stack[CONDITION_STACK_SIZE - 2]); 00477 00478 self->top->effective = self->top->current && self->top[1].effective; 00479 } 00480 00481 00486 typedef struct 00487 { 00488 GLboolean ARB_draw_buffers; 00489 GLboolean ARB_texture_rectangle; 00490 } pp_ext; 00491 00492 00496 static GLvoid 00497 pp_ext_disable_all(pp_ext *self) 00498 { 00499 _mesa_memset(self, 0, sizeof(self)); 00500 } 00501 00502 00507 static GLvoid 00508 pp_ext_init(pp_ext *self, const struct gl_extensions *extensions) 00509 { 00510 pp_ext_disable_all (self); 00511 if (extensions->ARB_draw_buffers) 00512 self->ARB_draw_buffers = GL_TRUE; 00513 if (extensions->NV_texture_rectangle) 00514 self->ARB_texture_rectangle = GL_TRUE; 00515 } 00516 00521 static GLboolean 00522 pp_ext_set(pp_ext *self, const char *name, GLboolean enable) 00523 { 00524 if (_mesa_strcmp (name, "GL_ARB_draw_buffers") == 0) 00525 self->ARB_draw_buffers = enable; 00526 else if (_mesa_strcmp (name, "GL_ARB_texture_rectangle") == 0) 00527 self->ARB_texture_rectangle = enable; 00528 else 00529 return GL_FALSE; 00530 return GL_TRUE; 00531 } 00532 00533 00534 static void 00535 pp_pragmas_init(struct gl_sl_pragmas *pragmas) 00536 { 00537 pragmas->Optimize = GL_TRUE; 00538 pragmas->Debug = GL_FALSE; 00539 } 00540 00541 00547 static GLboolean 00548 pp_pragma(struct gl_sl_pragmas *pragmas, const char *pragma, const char *param) 00549 { 00550 #if 0 00551 printf("#pragma %s %s\n", pragma, param); 00552 #endif 00553 if (_mesa_strcmp(pragma, "optimize") == 0) { 00554 if (!param) 00555 return GL_FALSE; /* missing required param */ 00556 if (_mesa_strcmp(param, "on") == 0) { 00557 pragmas->Optimize = GL_TRUE; 00558 } 00559 else if (_mesa_strcmp(param, "off") == 0) { 00560 pragmas->Optimize = GL_FALSE; 00561 } 00562 else { 00563 return GL_FALSE; /* invalid param */ 00564 } 00565 } 00566 else if (_mesa_strcmp(pragma, "debug") == 0) { 00567 if (!param) 00568 return GL_FALSE; /* missing required param */ 00569 if (_mesa_strcmp(param, "on") == 0) { 00570 pragmas->Debug = GL_TRUE; 00571 } 00572 else if (_mesa_strcmp(param, "off") == 0) { 00573 pragmas->Debug = GL_FALSE; 00574 } 00575 else { 00576 return GL_FALSE; /* invalid param */ 00577 } 00578 } 00579 /* all other pragmas are silently ignored */ 00580 return GL_TRUE; 00581 } 00582 00583 00588 typedef struct 00589 { 00590 GLint line; 00591 GLint file; 00592 GLint version; 00593 pp_symbols symbols; 00594 pp_ext ext; 00595 slang_info_log *elog; 00596 pp_cond_stack cond; 00597 } pp_state; 00598 00599 static GLvoid 00600 pp_state_init (pp_state *self, slang_info_log *elog, 00601 const struct gl_extensions *extensions) 00602 { 00603 self->line = 0; 00604 self->file = 1; 00605 #if FEATURE_es2_glsl 00606 self->version = 100; 00607 #else 00608 self->version = 110; 00609 #endif 00610 pp_symbols_init (&self->symbols); 00611 pp_ext_init (&self->ext, extensions); 00612 self->elog = elog; 00613 00614 /* Initialize condition stack and create the global context. */ 00615 self->cond.top = &self->cond.stack[CONDITION_STACK_SIZE - 1]; 00616 self->cond.top->current = GL_TRUE; 00617 self->cond.top->effective = GL_TRUE; 00618 self->cond.top->else_allowed = GL_FALSE; 00619 self->cond.top->endif_required = GL_FALSE; 00620 } 00621 00622 static GLvoid 00623 pp_state_free (pp_state *self) 00624 { 00625 pp_symbols_free (&self->symbols); 00626 } 00627 00628 #define IS_FIRST_ID_CHAR(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z') || (x) == '_') 00629 #define IS_NEXT_ID_CHAR(x) (IS_FIRST_ID_CHAR(x) || ((x) >= '0' && (x) <= '9')) 00630 #define IS_WHITE(x) ((x) == ' ' || (x) == '\n') 00631 #define IS_NULL(x) ((x) == '\0') 00632 00633 #define SKIP_WHITE(x) do { while (IS_WHITE(*(x))) (x)++; } while (GL_FALSE) 00634 00635 typedef struct 00636 { 00637 slang_string *output; 00638 const char *input; 00639 pp_state *state; 00640 } expand_state; 00641 00642 static GLboolean 00643 expand_defined (expand_state *e, slang_string *buffer) 00644 { 00645 GLboolean in_paren = GL_FALSE; 00646 const char *id; 00647 00648 /* Parse the optional opening parenthesis. */ 00649 SKIP_WHITE(e->input); 00650 if (*e->input == '(') { 00651 e->input++; 00652 in_paren = GL_TRUE; 00653 SKIP_WHITE(e->input); 00654 } 00655 00656 /* Parse operand. */ 00657 if (!IS_FIRST_ID_CHAR(*e->input)) { 00658 slang_info_log_error (e->state->elog, 00659 "preprocess error: identifier expected after operator 'defined'."); 00660 return GL_FALSE; 00661 } 00662 slang_string_reset (buffer); 00663 slang_string_pushc (buffer, *e->input++); 00664 while (IS_NEXT_ID_CHAR(*e->input)) 00665 slang_string_pushc (buffer, *e->input++); 00666 id = slang_string_cstr (buffer); 00667 00668 /* Check if the operand is defined. Output 1 if it is defined, output 0 if not. */ 00669 if (pp_symbols_find (&e->state->symbols, id) == NULL) 00670 slang_string_pushs (e->output, " 0 ", 3); 00671 else 00672 slang_string_pushs (e->output, " 1 ", 3); 00673 00674 /* Parse the closing parentehesis if the opening one was there. */ 00675 if (in_paren) { 00676 SKIP_WHITE(e->input); 00677 if (*e->input != ')') { 00678 slang_info_log_error (e->state->elog, "preprocess error: ')' expected."); 00679 return GL_FALSE; 00680 } 00681 e->input++; 00682 SKIP_WHITE(e->input); 00683 } 00684 return GL_TRUE; 00685 } 00686 00687 static GLboolean 00688 expand (expand_state *, pp_symbols *); 00689 00690 static GLboolean 00691 expand_symbol (expand_state *e, pp_symbol *symbol) 00692 { 00693 expand_state es; 00694 00695 /* If the macro has some parameters, we need to parse them. */ 00696 if (symbol->parameters.count != 0) { 00697 GLuint i; 00698 00699 /* Parse the opening parenthesis. */ 00700 SKIP_WHITE(e->input); 00701 if (*e->input != '(') { 00702 slang_info_log_error (e->state->elog, "preprocess error: '(' expected."); 00703 return GL_FALSE; 00704 } 00705 e->input++; 00706 SKIP_WHITE(e->input); 00707 00708 /* Parse macro actual parameters. This can be anything, separated by a colon. 00709 */ 00710 for (i = 0; i < symbol->parameters.count; i++) { 00711 GLuint nested_paren_count = 0; /* track number of nested parentheses */ 00712 00713 if (*e->input == ')') { 00714 slang_info_log_error (e->state->elog, "preprocess error: unexpected ')'."); 00715 return GL_FALSE; 00716 } 00717 00718 /* Eat all characters up to the comma or closing parentheses. */ 00719 pp_symbol_reset (&symbol->parameters.symbols[i]); 00720 while (!IS_NULL(*e->input)) { 00721 /* Exit loop only when all nested parens have been eaten. */ 00722 if (nested_paren_count == 0 && (*e->input == ',' || *e->input == ')')) 00723 break; 00724 00725 /* Actually count nested parens here. */ 00726 if (*e->input == '(') 00727 nested_paren_count++; 00728 else if (*e->input == ')') 00729 nested_paren_count--; 00730 00731 slang_string_pushc (&symbol->parameters.symbols[i].replacement, *e->input++); 00732 } 00733 00734 /* If it was not the last paremeter, skip the comma. Otherwise, skip the 00735 * closing parentheses. */ 00736 if (i + 1 == symbol->parameters.count) { 00737 /* This is the last paremeter - skip the closing parentheses. */ 00738 if (*e->input != ')') { 00739 slang_info_log_error (e->state->elog, "preprocess error: ')' expected."); 00740 return GL_FALSE; 00741 } 00742 e->input++; 00743 SKIP_WHITE(e->input); 00744 } 00745 else { 00746 /* Skip the separating comma. */ 00747 if (*e->input != ',') { 00748 slang_info_log_error (e->state->elog, "preprocess error: ',' expected."); 00749 return GL_FALSE; 00750 } 00751 e->input++; 00752 SKIP_WHITE(e->input); 00753 } 00754 } 00755 } 00756 00757 /* Expand the macro. Use its parameters as a priority symbol list to expand 00758 * macro parameters correctly. */ 00759 es.output = e->output; 00760 es.input = slang_string_cstr (&symbol->replacement); 00761 es.state = e->state; 00762 slang_string_pushc (e->output, ' '); 00763 if (!expand (&es, &symbol->parameters)) 00764 return GL_FALSE; 00765 slang_string_pushc (e->output, ' '); 00766 return GL_TRUE; 00767 } 00768 00769 /* 00770 * Function expand() expands source text from <input> to <output>. The expansion is made using 00771 * the list passed in <symbols> parameter. It allows us to expand macro formal parameters with 00772 * actual parameters. The global list of symbols from pp state is used when doing a recursive 00773 * call of expand(). 00774 */ 00775 00776 static GLboolean 00777 expand (expand_state *e, pp_symbols *symbols) 00778 { 00779 while (!IS_NULL(*e->input)) { 00780 if (IS_FIRST_ID_CHAR(*e->input)) { 00781 slang_string buffer; 00782 const char *id; 00783 00784 /* Parse the identifier. */ 00785 slang_string_init (&buffer); 00786 slang_string_pushc (&buffer, *e->input++); 00787 while (IS_NEXT_ID_CHAR(*e->input)) 00788 slang_string_pushc (&buffer, *e->input++); 00789 id = slang_string_cstr (&buffer); 00790 00791 /* Now check if the identifier is special in some way. The "defined" identifier is 00792 * actually an operator that we must handle here and expand it either to " 0 " or " 1 ". 00793 * The other identifiers start with "__" and we expand it to appropriate values 00794 * taken from the preprocessor state. */ 00795 if (_mesa_strcmp (id, "defined") == 0) { 00796 if (!expand_defined (e, &buffer)) 00797 return GL_FALSE; 00798 } 00799 else if (_mesa_strcmp (id, "__LINE__") == 0) { 00800 slang_string_pushc (e->output, ' '); 00801 slang_string_pushi (e->output, e->state->line); 00802 slang_string_pushc (e->output, ' '); 00803 } 00804 else if (_mesa_strcmp (id, "__FILE__") == 0) { 00805 slang_string_pushc (e->output, ' '); 00806 slang_string_pushi (e->output, e->state->file); 00807 slang_string_pushc (e->output, ' '); 00808 } 00809 else if (_mesa_strcmp (id, "__VERSION__") == 0) { 00810 slang_string_pushc (e->output, ' '); 00811 slang_string_pushi (e->output, e->state->version); 00812 slang_string_pushc (e->output, ' '); 00813 } 00814 #if FEATURE_es2_glsl 00815 else if (_mesa_strcmp (id, "GL_ES") == 0 || 00816 _mesa_strcmp (id, "GL_FRAGMENT_PRECISION_HIGH") == 0) { 00817 slang_string_pushc (e->output, ' '); 00818 slang_string_pushi (e->output, '1'); 00819 slang_string_pushc (e->output, ' '); 00820 } 00821 #endif 00822 else { 00823 pp_symbol *symbol; 00824 00825 /* The list of symbols from <symbols> take precedence over the list from <state>. 00826 * Note that in some cases this is the same list so avoid double look-up. */ 00827 symbol = pp_symbols_find (symbols, id); 00828 if (symbol == NULL && symbols != &e->state->symbols) 00829 symbol = pp_symbols_find (&e->state->symbols, id); 00830 00831 /* If the symbol was found, recursively expand its definition. */ 00832 if (symbol != NULL) { 00833 if (!expand_symbol (e, symbol)) { 00834 slang_string_free (&buffer); 00835 return GL_FALSE; 00836 } 00837 } 00838 else { 00839 slang_string_push (e->output, &buffer); 00840 } 00841 } 00842 slang_string_free (&buffer); 00843 } 00844 else if (IS_WHITE(*e->input)) { 00845 slang_string_pushc (e->output, *e->input++); 00846 } 00847 else { 00848 while (!IS_WHITE(*e->input) && !IS_NULL(*e->input) && !IS_FIRST_ID_CHAR(*e->input)) 00849 slang_string_pushc (e->output, *e->input++); 00850 } 00851 } 00852 return GL_TRUE; 00853 } 00854 00855 static GLboolean 00856 parse_if (slang_string *output, const byte *prod, GLuint *pi, GLint *result, pp_state *state, 00857 grammar eid) 00858 { 00859 const char *text; 00860 GLuint len; 00861 00862 text = (const char *) (&prod[*pi]); 00863 len = _mesa_strlen (text); 00864 00865 if (state->cond.top->effective) { 00866 slang_string expr; 00867 GLuint count; 00868 GLint results[2]; 00869 expand_state es; 00870 00871 /* Expand the expression. */ 00872 slang_string_init (&expr); 00873 es.output = &expr; 00874 es.input = text; 00875 es.state = state; 00876 if (!expand (&es, &state->symbols)) 00877 return GL_FALSE; 00878 00879 /* Execute the expression. */ 00880 count = execute_expressions (output, eid, (const byte *) (slang_string_cstr (&expr)), 00881 results, state->elog); 00882 slang_string_free (&expr); 00883 if (count != 1) 00884 return GL_FALSE; 00885 *result = results[0]; 00886 } 00887 else { 00888 /* The directive is dead. */ 00889 *result = 0; 00890 } 00891 00892 *pi += len + 1; 00893 return GL_TRUE; 00894 } 00895 00896 #define ESCAPE_TOKEN 0 00897 00898 #define TOKEN_END 0 00899 #define TOKEN_DEFINE 1 00900 #define TOKEN_UNDEF 2 00901 #define TOKEN_IF 3 00902 #define TOKEN_ELSE 4 00903 #define TOKEN_ELIF 5 00904 #define TOKEN_ENDIF 6 00905 #define TOKEN_ERROR 7 00906 #define TOKEN_PRAGMA 8 00907 #define TOKEN_EXTENSION 9 00908 #define TOKEN_LINE 10 00909 00910 #define PARAM_END 0 00911 #define PARAM_PARAMETER 1 00912 00913 #define BEHAVIOR_REQUIRE 1 00914 #define BEHAVIOR_ENABLE 2 00915 #define BEHAVIOR_WARN 3 00916 #define BEHAVIOR_DISABLE 4 00917 00918 #define PRAGMA_NO_PARAM 0 00919 #define PRAGMA_PARAM 1 00920 00921 00922 static GLboolean 00923 preprocess_source (slang_string *output, const char *source, 00924 grammar pid, grammar eid, 00925 slang_info_log *elog, 00926 const struct gl_extensions *extensions, 00927 struct gl_sl_pragmas *pragmas) 00928 { 00929 static const char *predefined[] = { 00930 "__FILE__", 00931 "__LINE__", 00932 "__VERSION__", 00933 #if FEATURE_es2_glsl 00934 "GL_ES", 00935 "GL_FRAGMENT_PRECISION_HIGH", 00936 #endif 00937 NULL 00938 }; 00939 byte *prod; 00940 GLuint size, i; 00941 pp_state state; 00942 00943 if (!grammar_fast_check (pid, (const byte *) (source), &prod, &size, 65536)) { 00944 grammar_error_to_log (elog); 00945 return GL_FALSE; 00946 } 00947 00948 pp_state_init (&state, elog, extensions); 00949 pp_pragmas_init (pragmas); 00950 00951 /* add the predefined symbols to the symbol table */ 00952 for (i = 0; predefined[i]; i++) { 00953 pp_symbol *symbol = NULL; 00954 symbol = pp_symbols_push(&state.symbols); 00955 assert(symbol); 00956 slang_string_pushs(&symbol->name, 00957 predefined[i], _mesa_strlen(predefined[i])); 00958 } 00959 00960 i = 0; 00961 while (i < size) { 00962 if (prod[i] != ESCAPE_TOKEN) { 00963 if (state.cond.top->effective) { 00964 slang_string input; 00965 expand_state es; 00966 00967 /* Eat only one line of source code to expand it. 00968 * FIXME: This approach has one drawback. If a macro with parameters spans across 00969 * multiple lines, the preprocessor will raise an error. */ 00970 slang_string_init (&input); 00971 while (prod[i] != '\0' && prod[i] != '\n') 00972 slang_string_pushc (&input, prod[i++]); 00973 if (prod[i] != '\0') 00974 slang_string_pushc (&input, prod[i++]); 00975 00976 /* Increment line number. */ 00977 state.line++; 00978 00979 es.output = output; 00980 es.input = slang_string_cstr (&input); 00981 es.state = &state; 00982 if (!expand (&es, &state.symbols)) 00983 goto error; 00984 00985 slang_string_free (&input); 00986 } 00987 else { 00988 /* Condition stack is disabled - keep track on line numbers and output only newlines. */ 00989 if (prod[i] == '\n') { 00990 state.line++; 00991 /*pp_annotate (output, "%c", prod[i]);*/ 00992 } 00993 else { 00994 /*pp_annotate (output, "%c", prod[i]);*/ 00995 } 00996 i++; 00997 } 00998 } 00999 else { 01000 const char *id; 01001 GLuint idlen; 01002 GLubyte token; 01003 01004 i++; 01005 token = prod[i++]; 01006 switch (token) { 01007 01008 case TOKEN_END: 01009 /* End of source string. 01010 * Check if all #ifs have been terminated by matching #endifs. 01011 * On condition stack there should be only the global condition context. */ 01012 if (state.cond.top->endif_required) { 01013 slang_info_log_error (elog, "end of source without matching #endif."); 01014 return GL_FALSE; 01015 } 01016 break; 01017 01018 case TOKEN_DEFINE: 01019 { 01020 pp_symbol *symbol = NULL; 01021 01022 /* Parse macro name. */ 01023 id = (const char *) (&prod[i]); 01024 idlen = _mesa_strlen (id); 01025 if (state.cond.top->effective) { 01026 pp_annotate (output, "// #define %s(", id); 01027 01028 /* If the symbol is already defined, override it. */ 01029 symbol = pp_symbols_find (&state.symbols, id); 01030 if (symbol == NULL) { 01031 symbol = pp_symbols_push (&state.symbols); 01032 if (symbol == NULL) 01033 goto error; 01034 slang_string_pushs (&symbol->name, id, idlen); 01035 } 01036 else { 01037 pp_symbol_reset (symbol); 01038 } 01039 } 01040 i += idlen + 1; 01041 01042 /* Parse optional macro parameters. */ 01043 while (prod[i++] != PARAM_END) { 01044 if (state.cond.top->effective) { 01045 pp_symbol *param; 01046 01047 id = (const char *) (&prod[i]); 01048 idlen = _mesa_strlen (id); 01049 pp_annotate (output, "%s, ", id); 01050 param = pp_symbols_push (&symbol->parameters); 01051 if (param == NULL) 01052 goto error; 01053 slang_string_pushs (¶m->name, id, idlen); 01054 } 01055 i += idlen + 1; 01056 } 01057 01058 /* Parse macro replacement. */ 01059 id = (const char *) (&prod[i]); 01060 idlen = _mesa_strlen (id); 01061 if (state.cond.top->effective) { 01062 pp_annotate (output, ") %s", id); 01063 slang_string_pushs (&symbol->replacement, id, idlen); 01064 } 01065 i += idlen + 1; 01066 } 01067 break; 01068 01069 case TOKEN_UNDEF: 01070 id = (const char *) (&prod[i]); 01071 i += _mesa_strlen (id) + 1; 01072 if (state.cond.top->effective) { 01073 pp_symbol *symbol; 01074 01075 pp_annotate (output, "// #undef %s", id); 01076 /* Try to find symbol with given name and remove it. */ 01077 symbol = pp_symbols_find (&state.symbols, id); 01078 if (symbol != NULL) 01079 if (!pp_symbols_erase (&state.symbols, symbol)) 01080 goto error; 01081 } 01082 break; 01083 01084 case TOKEN_IF: 01085 { 01086 GLint result; 01087 01088 /* Parse #if expression end execute it. */ 01089 pp_annotate (output, "// #if "); 01090 if (!parse_if (output, prod, &i, &result, &state, eid)) 01091 goto error; 01092 01093 /* Push new condition on the stack. */ 01094 if (!pp_cond_stack_push (&state.cond, state.elog)) 01095 goto error; 01096 state.cond.top->current = result ? GL_TRUE : GL_FALSE; 01097 state.cond.top->else_allowed = GL_TRUE; 01098 state.cond.top->endif_required = GL_TRUE; 01099 pp_cond_stack_reevaluate (&state.cond); 01100 } 01101 break; 01102 01103 case TOKEN_ELSE: 01104 /* Check if #else is alloved here. */ 01105 if (!state.cond.top->else_allowed) { 01106 slang_info_log_error (elog, "#else without matching #if."); 01107 goto error; 01108 } 01109 01110 /* Negate current condition and reevaluate it. */ 01111 state.cond.top->current = !state.cond.top->current; 01112 state.cond.top->else_allowed = GL_FALSE; 01113 pp_cond_stack_reevaluate (&state.cond); 01114 if (state.cond.top->effective) 01115 pp_annotate (output, "// #else"); 01116 break; 01117 01118 case TOKEN_ELIF: 01119 /* Check if #elif is alloved here. */ 01120 if (!state.cond.top->else_allowed) { 01121 slang_info_log_error (elog, "#elif without matching #if."); 01122 goto error; 01123 } 01124 01125 /* Negate current condition and reevaluate it. */ 01126 state.cond.top->current = !state.cond.top->current; 01127 pp_cond_stack_reevaluate (&state.cond); 01128 01129 if (state.cond.top->effective) 01130 pp_annotate (output, "// #elif "); 01131 01132 { 01133 GLint result; 01134 01135 /* Parse #elif expression end execute it. */ 01136 if (!parse_if (output, prod, &i, &result, &state, eid)) 01137 goto error; 01138 01139 /* Update current condition and reevaluate it. */ 01140 state.cond.top->current = result ? GL_TRUE : GL_FALSE; 01141 pp_cond_stack_reevaluate (&state.cond); 01142 } 01143 break; 01144 01145 case TOKEN_ENDIF: 01146 /* Check if #endif is alloved here. */ 01147 if (!state.cond.top->endif_required) { 01148 slang_info_log_error (elog, "#endif without matching #if."); 01149 goto error; 01150 } 01151 01152 /* Pop the condition off the stack. */ 01153 state.cond.top++; 01154 if (state.cond.top->effective) 01155 pp_annotate (output, "// #endif"); 01156 break; 01157 01158 case TOKEN_EXTENSION: 01159 /* Parse the extension name. */ 01160 id = (const char *) (&prod[i]); 01161 i += _mesa_strlen (id) + 1; 01162 if (state.cond.top->effective) 01163 pp_annotate (output, "// #extension %s: ", id); 01164 01165 /* Parse and apply extension behavior. */ 01166 if (state.cond.top->effective) { 01167 switch (prod[i++]) { 01168 01169 case BEHAVIOR_REQUIRE: 01170 pp_annotate (output, "require"); 01171 if (!pp_ext_set (&state.ext, id, GL_TRUE)) { 01172 if (_mesa_strcmp (id, "all") == 0) { 01173 slang_info_log_error (elog, "require: bad behavior for #extension all."); 01174 goto error; 01175 } 01176 else { 01177 slang_info_log_error (elog, "%s: required extension is not supported.", id); 01178 goto error; 01179 } 01180 } 01181 break; 01182 01183 case BEHAVIOR_ENABLE: 01184 pp_annotate (output, "enable"); 01185 if (!pp_ext_set (&state.ext, id, GL_TRUE)) { 01186 if (_mesa_strcmp (id, "all") == 0) { 01187 slang_info_log_error (elog, "enable: bad behavior for #extension all."); 01188 goto error; 01189 } 01190 else { 01191 slang_info_log_warning (elog, "%s: enabled extension is not supported.", id); 01192 } 01193 } 01194 break; 01195 01196 case BEHAVIOR_WARN: 01197 pp_annotate (output, "warn"); 01198 if (!pp_ext_set (&state.ext, id, GL_TRUE)) { 01199 if (_mesa_strcmp (id, "all") != 0) { 01200 slang_info_log_warning (elog, "%s: enabled extension is not supported.", id); 01201 } 01202 } 01203 break; 01204 01205 case BEHAVIOR_DISABLE: 01206 pp_annotate (output, "disable"); 01207 if (!pp_ext_set (&state.ext, id, GL_FALSE)) { 01208 if (_mesa_strcmp (id, "all") == 0) { 01209 pp_ext_disable_all (&state.ext); 01210 } 01211 else { 01212 slang_info_log_warning (elog, "%s: disabled extension is not supported.", id); 01213 } 01214 } 01215 break; 01216 01217 default: 01218 assert (0); 01219 } 01220 } 01221 break; 01222 01223 case TOKEN_PRAGMA: 01224 { 01225 GLint have_param; 01226 const char *pragma, *param; 01227 01228 pragma = (const char *) (&prod[i]); 01229 i += _mesa_strlen(pragma) + 1; 01230 have_param = (prod[i++] == PRAGMA_PARAM); 01231 if (have_param) { 01232 param = (const char *) (&prod[i]); 01233 i += _mesa_strlen(param) + 1; 01234 } 01235 else { 01236 param = NULL; 01237 } 01238 pp_pragma(pragmas, pragma, param); 01239 } 01240 break; 01241 01242 case TOKEN_LINE: 01243 id = (const char *) (&prod[i]); 01244 i += _mesa_strlen (id) + 1; 01245 01246 if (state.cond.top->effective) { 01247 slang_string buffer; 01248 GLuint count; 01249 GLint results[2]; 01250 expand_state es; 01251 01252 slang_string_init (&buffer); 01253 state.line++; 01254 es.output = &buffer; 01255 es.input = id; 01256 es.state = &state; 01257 if (!expand (&es, &state.symbols)) 01258 goto error; 01259 01260 pp_annotate (output, "// #line "); 01261 count = execute_expressions (output, eid, 01262 (const byte *) (slang_string_cstr (&buffer)), 01263 results, state.elog); 01264 slang_string_free (&buffer); 01265 if (count == 0) 01266 goto error; 01267 01268 state.line = results[0] - 1; 01269 if (count == 2) 01270 state.file = results[1]; 01271 } 01272 break; 01273 } 01274 } 01275 } 01276 01277 /* Check for missing #endifs. */ 01278 if (state.cond.top->endif_required) { 01279 slang_info_log_error (elog, "#endif expected but end of source found."); 01280 goto error; 01281 } 01282 01283 grammar_alloc_free(prod); 01284 pp_state_free (&state); 01285 return GL_TRUE; 01286 01287 error: 01288 grammar_alloc_free(prod); 01289 pp_state_free (&state); 01290 return GL_FALSE; 01291 } 01292 01293 01302 GLboolean 01303 _slang_preprocess_directives(slang_string *output, 01304 const char *input, 01305 slang_info_log *elog, 01306 const struct gl_extensions *extensions, 01307 struct gl_sl_pragmas *pragmas) 01308 { 01309 grammar pid, eid; 01310 GLboolean success; 01311 01312 pid = grammar_load_from_text ((const byte *) (slang_pp_directives_syn)); 01313 if (pid == 0) { 01314 grammar_error_to_log (elog); 01315 return GL_FALSE; 01316 } 01317 eid = grammar_load_from_text ((const byte *) (slang_pp_expression_syn)); 01318 if (eid == 0) { 01319 grammar_error_to_log (elog); 01320 grammar_destroy (pid); 01321 return GL_FALSE; 01322 } 01323 success = preprocess_source (output, input, pid, eid, elog, extensions, pragmas); 01324 grammar_destroy (eid); 01325 grammar_destroy (pid); 01326 return success; 01327 } 01328 Generated on Sun May 27 2012 04:20:41 for ReactOS by
1.7.6.1
|