GLvoid slang_string_pushi (slang_string *self, GLint i) { char buffer[12]; sprintf (buffer, "%d", i); slang_string_pushs (self, buffer, strlen (buffer)); }
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 GLvoid pp_annotate (slang_string *output, const char *fmt, ...) { #if PP_ANNOTATE va_list va; char buffer[1024]; va_start (va, fmt); _mesa_vsprintf (buffer, fmt, va); va_end (va); slang_string_pushs (output, buffer, _mesa_strlen (buffer)); #else (GLvoid) (output); (GLvoid) (fmt); #endif }
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; }