static GLuint execute_expressions (slang_string *output, grammar eid, const byte *expr, GLint results[2], slang_info_log *elog) { GLint success; byte *code; GLuint size, count = 0; success = grammar_fast_check (eid, expr, &code, &size, 64); if (success) { GLuint i = 0; while (code[i++] == EXP_EXPRESSION) { assert (count < 2); if (!execute_expression (output, code, &i, &results[count], elog)) { count = 0; break; } count++; } grammar_alloc_free (code); } else { slang_info_log_error (elog, "syntax error in preprocessor expression.");\ } return count; }
int _slang_preprocess_version (const char *text, unsigned int *version, unsigned int *eaten, slang_info_log *log) { grammar id; byte *prod, *I; unsigned int size; id = grammar_load_from_text ((const byte *) slang_version_syn); if (id == 0) { char buf[1024]; unsigned int pos; grammar_get_last_error ( (unsigned char*) buf, 1024, (int*) &pos); slang_info_log_error (log, buf); return 0; } if (!grammar_fast_check (id, (const byte *) text, &prod, &size, 8)) { char buf[1024]; unsigned int pos; grammar_get_last_error ( (unsigned char*) buf, 1024, (int*) &pos); slang_info_log_error (log, buf); grammar_destroy (id); return 0; } grammar_destroy (id); /* there can be multiple #version directives - grab the last one */ I = prod; while (I < prod + size) { *version = (unsigned int) I[0] + (unsigned int) I[1] * 100; *eaten = ((unsigned int) I[2]) + ((unsigned int) I[3] << 8) + ((unsigned int) I[4] << 16) + ((unsigned int) I[5] << 24); I += 6; } grammar_alloc_free (prod); return 1; }
static GLboolean expand_defined (expand_state *e, slang_string *buffer) { GLboolean in_paren = GL_FALSE; const char *id; /* Parse the optional opening parenthesis. */ SKIP_WHITE(e->input); if (*e->input == '(') { e->input++; in_paren = GL_TRUE; SKIP_WHITE(e->input); } /* Parse operand. */ if (!IS_FIRST_ID_CHAR(*e->input)) { slang_info_log_error (e->state->elog, "preprocess error: identifier expected after operator 'defined'."); return GL_FALSE; } slang_string_reset (buffer); slang_string_pushc (buffer, *e->input++); while (IS_NEXT_ID_CHAR(*e->input)) slang_string_pushc (buffer, *e->input++); id = slang_string_cstr (buffer); /* Check if the operand is defined. Output 1 if it is defined, output 0 if not. */ if (pp_symbols_find (&e->state->symbols, id) == NULL) slang_string_pushs (e->output, " 0 ", 3); else slang_string_pushs (e->output, " 1 ", 3); /* Parse the closing parentehesis if the opening one was there. */ if (in_paren) { SKIP_WHITE(e->input); if (*e->input != ')') { slang_info_log_error (e->state->elog, "preprocess error: ')' expected."); return GL_FALSE; } e->input++; SKIP_WHITE(e->input); } return GL_TRUE; }
static GLboolean pp_cond_stack_push (pp_cond_stack *self, slang_info_log *elog) { if (self->top == self->stack) { slang_info_log_error (elog, "internal compiler error: preprocessor condition stack overflow."); return GL_FALSE; } self->top--; return GL_TRUE; }
static GLvoid grammar_error_to_log (slang_info_log *log) { char buf[1024]; GLint pos; grammar_get_last_error ((byte *) (buf), sizeof (buf), &pos); if (buf[0] == 0) { _mesa_snprintf(buf, sizeof(buf), "Preprocessor error"); } slang_info_log_error (log, buf); }
static GLboolean preprocess_source (slang_string *output, const char *source, grammar pid, grammar eid, slang_info_log *elog, const struct gl_extensions *extensions, struct gl_sl_pragmas *pragmas) { static const char *predefined[] = { "__FILE__", "__LINE__", "__VERSION__", #if FEATURE_es2_glsl "GL_ES", "GL_FRAGMENT_PRECISION_HIGH", #endif NULL }; byte *prod; GLuint size, i; pp_state state; if (!grammar_fast_check (pid, (const byte *) (source), &prod, &size, 65536)) { grammar_error_to_log (elog); return GL_FALSE; } pp_state_init (&state, elog, extensions); pp_pragmas_init (pragmas); /* add the predefined symbols to the symbol table */ for (i = 0; predefined[i]; i++) { pp_symbol *symbol = NULL; symbol = pp_symbols_push(&state.symbols); assert(symbol); slang_string_pushs(&symbol->name, predefined[i], _mesa_strlen(predefined[i])); } i = 0; while (i < size) { if (prod[i] != ESCAPE_TOKEN) { if (state.cond.top->effective) { slang_string input; expand_state es; /* Eat only one line of source code to expand it. * FIXME: This approach has one drawback. If a macro with parameters spans across * multiple lines, the preprocessor will raise an error. */ slang_string_init (&input); while (prod[i] != '\0' && prod[i] != '\n') slang_string_pushc (&input, prod[i++]); if (prod[i] != '\0') slang_string_pushc (&input, prod[i++]); /* Increment line number. */ state.line++; es.output = output; es.input = slang_string_cstr (&input); es.state = &state; if (!expand (&es, &state.symbols)) goto error; slang_string_free (&input); } else { /* Condition stack is disabled - keep track on line numbers and output only newlines. */ if (prod[i] == '\n') { state.line++; /*pp_annotate (output, "%c", prod[i]);*/ } else { /*pp_annotate (output, "%c", prod[i]);*/ } i++; } } else { const char *id; GLuint idlen; GLubyte token; i++; token = prod[i++]; switch (token) { case TOKEN_END: /* End of source string. * Check if all #ifs have been terminated by matching #endifs. * On condition stack there should be only the global condition context. */ if (state.cond.top->endif_required) { slang_info_log_error (elog, "end of source without matching #endif."); return GL_FALSE; } break; case TOKEN_DEFINE: { pp_symbol *symbol = NULL; /* Parse macro name. */ id = (const char *) (&prod[i]); idlen = _mesa_strlen (id); if (state.cond.top->effective) { pp_annotate (output, "// #define %s(", id); /* If the symbol is already defined, override it. */ symbol = pp_symbols_find (&state.symbols, id); if (symbol == NULL) { symbol = pp_symbols_push (&state.symbols); if (symbol == NULL) goto error; slang_string_pushs (&symbol->name, id, idlen); } else { pp_symbol_reset (symbol); } } i += idlen + 1; /* Parse optional macro parameters. */ while (prod[i++] != PARAM_END) { pp_symbol *param; id = (const char *) (&prod[i]); idlen = _mesa_strlen (id); if (state.cond.top->effective) { pp_annotate (output, "%s, ", id); param = pp_symbols_push (&symbol->parameters); if (param == NULL) goto error; slang_string_pushs (¶m->name, id, idlen); } i += idlen + 1; } /* Parse macro replacement. */ id = (const char *) (&prod[i]); idlen = _mesa_strlen (id); if (state.cond.top->effective) { slang_string replacement; expand_state es; pp_annotate (output, ") %s", id); slang_string_init(&replacement); slang_string_pushs(&replacement, id, idlen); /* Expand macro replacement. */ es.output = &symbol->replacement; es.input = slang_string_cstr(&replacement); es.state = &state; if (!expand(&es, &state.symbols)) { slang_string_free(&replacement); goto error; } slang_string_free(&replacement); } i += idlen + 1; } break; case TOKEN_UNDEF: id = (const char *) (&prod[i]); i += _mesa_strlen (id) + 1; if (state.cond.top->effective) { pp_symbol *symbol; pp_annotate (output, "// #undef %s", id); /* Try to find symbol with given name and remove it. */ symbol = pp_symbols_find (&state.symbols, id); if (symbol != NULL) if (!pp_symbols_erase (&state.symbols, symbol)) goto error; } break; case TOKEN_IF: { GLint result; /* Parse #if expression end execute it. */ pp_annotate (output, "// #if "); if (!parse_if (output, prod, &i, &result, &state, eid)) goto error; /* Push new condition on the stack. */ if (!pp_cond_stack_push (&state.cond, state.elog)) goto error; state.cond.top->current = result ? GL_TRUE : GL_FALSE; state.cond.top->else_allowed = GL_TRUE; state.cond.top->endif_required = GL_TRUE; pp_cond_stack_reevaluate (&state.cond); } break; case TOKEN_ELSE: /* Check if #else is alloved here. */ if (!state.cond.top->else_allowed) { slang_info_log_error (elog, "#else without matching #if."); goto error; } /* Negate current condition and reevaluate it. */ state.cond.top->current = !state.cond.top->current; state.cond.top->else_allowed = GL_FALSE; pp_cond_stack_reevaluate (&state.cond); if (state.cond.top->effective) pp_annotate (output, "// #else"); break; case TOKEN_ELIF: /* Check if #elif is alloved here. */ if (!state.cond.top->else_allowed) { slang_info_log_error (elog, "#elif without matching #if."); goto error; } /* Negate current condition and reevaluate it. */ state.cond.top->current = !state.cond.top->current; pp_cond_stack_reevaluate (&state.cond); if (state.cond.top->effective) pp_annotate (output, "// #elif "); { GLint result; /* Parse #elif expression end execute it. */ if (!parse_if (output, prod, &i, &result, &state, eid)) goto error; /* Update current condition and reevaluate it. */ state.cond.top->current = result ? GL_TRUE : GL_FALSE; pp_cond_stack_reevaluate (&state.cond); } break; case TOKEN_ENDIF: /* Check if #endif is alloved here. */ if (!state.cond.top->endif_required) { slang_info_log_error (elog, "#endif without matching #if."); goto error; } /* Pop the condition off the stack. */ state.cond.top++; if (state.cond.top->effective) pp_annotate (output, "// #endif"); break; case TOKEN_EXTENSION: /* Parse the extension name. */ id = (const char *) (&prod[i]); i += _mesa_strlen (id) + 1; if (state.cond.top->effective) pp_annotate (output, "// #extension %s: ", id); /* Parse and apply extension behavior. */ if (state.cond.top->effective) { switch (prod[i++]) { case BEHAVIOR_REQUIRE: pp_annotate (output, "require"); if (!pp_ext_set (&state.ext, id, GL_TRUE)) { if (_mesa_strcmp (id, "all") == 0) { slang_info_log_error (elog, "require: bad behavior for #extension all."); goto error; } else { slang_info_log_error (elog, "%s: required extension is not supported.", id); goto error; } } break; case BEHAVIOR_ENABLE: pp_annotate (output, "enable"); if (!pp_ext_set (&state.ext, id, GL_TRUE)) { if (_mesa_strcmp (id, "all") == 0) { slang_info_log_error (elog, "enable: bad behavior for #extension all."); goto error; } else { slang_info_log_warning (elog, "%s: enabled extension is not supported.", id); } } break; case BEHAVIOR_WARN: pp_annotate (output, "warn"); if (!pp_ext_set (&state.ext, id, GL_TRUE)) { if (_mesa_strcmp (id, "all") != 0) { slang_info_log_warning (elog, "%s: enabled extension is not supported.", id); } } break; case BEHAVIOR_DISABLE: pp_annotate (output, "disable"); if (!pp_ext_set (&state.ext, id, GL_FALSE)) { if (_mesa_strcmp (id, "all") == 0) { pp_ext_disable_all (&state.ext); } else { slang_info_log_warning (elog, "%s: disabled extension is not supported.", id); } } break; default: assert (0); } } break; case TOKEN_PRAGMA: { GLint have_param; const char *pragma, *param; pragma = (const char *) (&prod[i]); i += _mesa_strlen(pragma) + 1; have_param = (prod[i++] == PRAGMA_PARAM); if (have_param) { param = (const char *) (&prod[i]); i += _mesa_strlen(param) + 1; } else { param = NULL; } pp_pragma(pragmas, pragma, param); } break; case TOKEN_LINE: id = (const char *) (&prod[i]); i += _mesa_strlen (id) + 1; if (state.cond.top->effective) { slang_string buffer; GLuint count; GLint results[2]; expand_state es; slang_string_init (&buffer); state.line++; es.output = &buffer; es.input = id; es.state = &state; if (!expand (&es, &state.symbols)) goto error; pp_annotate (output, "// #line "); count = execute_expressions (output, eid, (const byte *) (slang_string_cstr (&buffer)), results, state.elog); slang_string_free (&buffer); if (count == 0) goto error; state.line = results[0] - 1; if (count == 2) state.file = results[1]; } break; } } } /* Check for missing #endifs. */ if (state.cond.top->endif_required) { slang_info_log_error (elog, "#endif expected but end of source found."); goto error; } grammar_alloc_free(prod); pp_state_free (&state); return GL_TRUE; error: grammar_alloc_free(prod); pp_state_free (&state); return GL_FALSE; }
static GLboolean expand_symbol (expand_state *e, pp_symbol *symbol) { expand_state es; /* If the macro has some parameters, we need to parse them. */ if (symbol->parameters.count != 0) { GLuint i; /* Parse the opening parenthesis. */ SKIP_WHITE(e->input); if (*e->input != '(') { slang_info_log_error (e->state->elog, "preprocess error: '(' expected."); return GL_FALSE; } e->input++; SKIP_WHITE(e->input); /* Parse macro actual parameters. This can be anything, separated by a colon. */ for (i = 0; i < symbol->parameters.count; i++) { GLuint nested_paren_count = 0; /* track number of nested parentheses */ if (*e->input == ')') { slang_info_log_error (e->state->elog, "preprocess error: unexpected ')'."); return GL_FALSE; } /* Eat all characters up to the comma or closing parentheses. */ pp_symbol_reset (&symbol->parameters.symbols[i]); while (!IS_NULL(*e->input)) { /* Exit loop only when all nested parens have been eaten. */ if (nested_paren_count == 0 && (*e->input == ',' || *e->input == ')')) break; /* Actually count nested parens here. */ if (*e->input == '(') nested_paren_count++; else if (*e->input == ')') nested_paren_count--; slang_string_pushc (&symbol->parameters.symbols[i].replacement, *e->input++); } /* If it was not the last paremeter, skip the comma. Otherwise, skip the * closing parentheses. */ if (i + 1 == symbol->parameters.count) { /* This is the last paremeter - skip the closing parentheses. */ if (*e->input != ')') { slang_info_log_error (e->state->elog, "preprocess error: ')' expected."); return GL_FALSE; } e->input++; SKIP_WHITE(e->input); } else { /* Skip the separating comma. */ if (*e->input != ',') { slang_info_log_error (e->state->elog, "preprocess error: ',' expected."); return GL_FALSE; } e->input++; SKIP_WHITE(e->input); } } } /* Expand the macro. Use its parameters as a priority symbol list to expand * macro parameters correctly. */ es.output = e->output; es.input = slang_string_cstr (&symbol->replacement); es.state = e->state; slang_string_pushc (e->output, ' '); if (!expand (&es, &symbol->parameters)) return GL_FALSE; slang_string_pushc (e->output, ' '); return GL_TRUE; }