diff options
Diffstat (limited to 'src/mesa/shader/slang/slang_preprocess.c')
-rw-r--r-- | src/mesa/shader/slang/slang_preprocess.c | 1406 |
1 files changed, 0 insertions, 1406 deletions
diff --git a/src/mesa/shader/slang/slang_preprocess.c b/src/mesa/shader/slang/slang_preprocess.c deleted file mode 100644 index e9a24cc009a..00000000000 --- a/src/mesa/shader/slang/slang_preprocess.c +++ /dev/null @@ -1,1406 +0,0 @@ -/* - * Mesa 3-D graphics library - * - * Copyright (C) 2005-2008 Brian Paul All Rights Reserved. - * Copyright (C) 2009 VMware, Inc. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** - * \file slang_preprocess.c - * slang preprocessor - * \author Michal Krol - */ - -#include "main/imports.h" -#include "shader/grammar/grammar_mesa.h" -#include "slang_preprocess.h" - -LONGSTRING static const char *slang_pp_directives_syn = -#include "library/slang_pp_directives_syn.h" -; - -LONGSTRING static const char *slang_pp_expression_syn = -#include "library/slang_pp_expression_syn.h" -; - -LONGSTRING static const char *slang_pp_version_syn = -#include "library/slang_pp_version_syn.h" -; - -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); -} - -GLboolean -_slang_preprocess_version (const char *text, GLuint *version, GLuint *eaten, slang_info_log *log) -{ - grammar id; - byte *prod, *I; - unsigned int size; - - id = grammar_load_from_text ((const byte *) (slang_pp_version_syn)); - if (id == 0) { - grammar_error_to_log (log); - return GL_FALSE; - } - - if (!grammar_fast_check (id, (const byte *) (text), &prod, &size, 8)) { - grammar_error_to_log (log); - grammar_destroy (id); - return GL_FALSE; - } - - /* there can be multiple #version directives - grab the last one */ - I = &prod[size - 6]; - *version = (GLuint) (I[0]) + (GLuint) (I[1]) * 100; - *eaten = (GLuint) (I[2]) + ((GLuint) (I[3]) << 8) + ((GLuint) (I[4]) << 16) + ((GLuint) (I[5]) << 24); - - grammar_destroy (id); - grammar_alloc_free (prod); - return GL_TRUE; -} - -/* - * The preprocessor does the following work. - * 1. Remove comments. Each comment block is replaced with a single space and if the - * block contains new-lines, they are preserved. This ensures that line numbers - * stay the same and if a comment block delimits two tokens, the are delitmited - * by the space after comment removal. - * 2. Remove preprocessor directives from the source string, checking their syntax and - * executing them if appropriate. Again, new-lines are preserved. - * 3. Expand macros. - * 4. Tokenize the source string by ensuring there is at least one space between every - * two adjacent tokens. - */ - -#define PP_ANNOTATE 0 - -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 -} - - /* - * The expression is executed on a fixed-sized stack. The PUSH macro makes a runtime - * check if the stack is not overflown by too complex expressions. In that situation the - * GLSL preprocessor should report internal compiler error. - * The BINARYDIV makes a runtime check if the divider is not 0. If it is, it reports - * compilation error. - */ - -#define EXECUTION_STACK_SIZE 1024 - -#define PUSH(x)\ - do {\ - if (sp == 0) {\ - slang_info_log_error (elog, "internal compiler error: preprocessor execution stack overflow.");\ - return GL_FALSE;\ - }\ - stack[--sp] = x;\ - } while (GL_FALSE) - -#define POP(x)\ - do {\ - assert (sp < EXECUTION_STACK_SIZE);\ - x = stack[sp++];\ - } while (GL_FALSE) - -#define BINARY(op)\ - do {\ - GLint a, b;\ - POP(b);\ - POP(a);\ - PUSH(a op b);\ - } while (GL_FALSE) - -#define BINARYDIV(op)\ - do {\ - GLint a, b;\ - POP(b);\ - POP(a);\ - if (b == 0) {\ - slang_info_log_error (elog, "division by zero in preprocessor expression.");\ - return GL_FALSE;\ - }\ - PUSH(a op b);\ - } while (GL_FALSE) - -#define UNARY(op)\ - do {\ - GLint a;\ - POP(a);\ - PUSH(op a);\ - } while (GL_FALSE) - -#define OP_END 0 -#define OP_PUSHINT 1 -#define OP_LOGICALOR 2 -#define OP_LOGICALAND 3 -#define OP_OR 4 -#define OP_XOR 5 -#define OP_AND 6 -#define OP_EQUAL 7 -#define OP_NOTEQUAL 8 -#define OP_LESSEQUAL 9 -#define OP_GREATEREQUAL 10 -#define OP_LESS 11 -#define OP_GREATER 12 -#define OP_LEFTSHIFT 13 -#define OP_RIGHTSHIFT 14 -#define OP_ADD 15 -#define OP_SUBTRACT 16 -#define OP_MULTIPLY 17 -#define OP_DIVIDE 18 -#define OP_MODULUS 19 -#define OP_PLUS 20 -#define OP_MINUS 21 -#define OP_NEGATE 22 -#define OP_COMPLEMENT 23 - -static GLboolean -execute_expression (slang_string *output, const byte *code, GLuint *pi, GLint *result, - slang_info_log *elog) -{ - GLuint i = *pi; - GLint stack[EXECUTION_STACK_SIZE]; - GLuint sp = EXECUTION_STACK_SIZE; - - while (code[i] != OP_END) { - switch (code[i++]) { - case OP_PUSHINT: - i++; - PUSH(_mesa_atoi ((const char *) (&code[i]))); - i += _mesa_strlen ((const char *) (&code[i])) + 1; - break; - case OP_LOGICALOR: - BINARY(||); - break; - case OP_LOGICALAND: - BINARY(&&); - break; - case OP_OR: - BINARY(|); - break; - case OP_XOR: - BINARY(^); - break; - case OP_AND: - BINARY(&); - break; - case OP_EQUAL: - BINARY(==); - break; - case OP_NOTEQUAL: - BINARY(!=); - break; - case OP_LESSEQUAL: - BINARY(<=); - break; - case OP_GREATEREQUAL: - BINARY(>=); - break; - case OP_LESS: - BINARY(<); - break; - case OP_GREATER: - BINARY(>); - break; - case OP_LEFTSHIFT: - BINARY(<<); - break; - case OP_RIGHTSHIFT: - BINARY(>>); - break; - case OP_ADD: - BINARY(+); - break; - case OP_SUBTRACT: - BINARY(-); - break; - case OP_MULTIPLY: - BINARY(*); - break; - case OP_DIVIDE: - BINARYDIV(/); - break; - case OP_MODULUS: - BINARYDIV(%); - break; - case OP_PLUS: - UNARY(+); - break; - case OP_MINUS: - UNARY(-); - break; - case OP_NEGATE: - UNARY(!); - break; - case OP_COMPLEMENT: - UNARY(~); - break; - default: - assert (0); - } - } - - /* Write-back the index skipping the OP_END. */ - *pi = i + 1; - - /* There should be exactly one value left on the stack. This is our result. */ - POP(*result); - pp_annotate (output, "%d ", *result); - assert (sp == EXECUTION_STACK_SIZE); - return GL_TRUE; -} - -/* - * Function execute_expressions() executes up to 2 expressions. The second expression is there - * for the #line directive which takes 1 or 2 expressions that indicate line and file numbers. - * If it fails, it returns 0. If it succeeds, it returns the number of executed expressions. - */ - -#define EXP_END 0 -#define EXP_EXPRESSION 1 - -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; -} - -/* - * The pp_symbol structure is used to hold macro definitions and macro formal parameters. The - * pp_symbols strcture is a collection of pp_symbol. It is used both for storing macro formal - * parameters and all global macro definitions. Making this unification wastes some memory, - * becuse macro formal parameters don't need further lists of symbols. We lose 8 bytes per - * formal parameter here, but making this we can use the same code to substitute macro parameters - * as well as macros in the source string. - */ - -typedef struct -{ - struct pp_symbol_ *symbols; - GLuint count; -} pp_symbols; - -static GLvoid -pp_symbols_init (pp_symbols *self) -{ - self->symbols = NULL; - self->count = 0; -} - -static GLvoid -pp_symbols_free (pp_symbols *); - -typedef struct pp_symbol_ -{ - slang_string name; - slang_string replacement; - pp_symbols parameters; -} pp_symbol; - -static GLvoid -pp_symbol_init (pp_symbol *self) -{ - slang_string_init (&self->name); - slang_string_init (&self->replacement); - pp_symbols_init (&self->parameters); -} - -static GLvoid -pp_symbol_free (pp_symbol *self) -{ - slang_string_free (&self->name); - slang_string_free (&self->replacement); - pp_symbols_free (&self->parameters); -} - -static GLvoid -pp_symbol_reset (pp_symbol *self) -{ - /* Leave symbol name intact. */ - slang_string_reset (&self->replacement); - pp_symbols_free (&self->parameters); - pp_symbols_init (&self->parameters); -} - -static GLvoid -pp_symbols_free (pp_symbols *self) -{ - GLuint i; - - for (i = 0; i < self->count; i++) - pp_symbol_free (&self->symbols[i]); - _mesa_free (self->symbols); -} - -static pp_symbol * -pp_symbols_push (pp_symbols *self) -{ - self->symbols = (pp_symbol *) (_mesa_realloc (self->symbols, self->count * sizeof (pp_symbol), - (self->count + 1) * sizeof (pp_symbol))); - if (self->symbols == NULL) - return NULL; - pp_symbol_init (&self->symbols[self->count]); - return &self->symbols[self->count++]; -} - -static GLboolean -pp_symbols_erase (pp_symbols *self, pp_symbol *symbol) -{ - assert (symbol >= self->symbols && symbol < self->symbols + self->count); - - self->count--; - pp_symbol_free (symbol); - if (symbol < self->symbols + self->count) - _mesa_memcpy (symbol, symbol + 1, sizeof (pp_symbol) * (self->symbols + self->count - symbol)); - self->symbols = (pp_symbol *) (_mesa_realloc (self->symbols, (self->count + 1) * sizeof (pp_symbol), - self->count * sizeof (pp_symbol))); - return self->symbols != NULL; -} - -static pp_symbol * -pp_symbols_find (pp_symbols *self, const char *name) -{ - GLuint i; - - for (i = 0; i < self->count; i++) - if (_mesa_strcmp (name, slang_string_cstr (&self->symbols[i].name)) == 0) - return &self->symbols[i]; - return NULL; -} - -/* - * The condition context of a single #if/#else/#endif level. Those can be nested, so there - * is a stack of condition contexts. - * There is a special global context on the bottom of the stack. It is there to simplify - * context handling. - */ - -typedef struct -{ - GLboolean current; /* The condition value of this level. */ - GLboolean effective; /* The effective product of current condition, outer level conditions - * and position within #if-#else-#endif sections. */ - GLboolean else_allowed; /* TRUE if in #if-#else section, FALSE if in #else-#endif section - * and for global context. */ - GLboolean endif_required; /* FALSE for global context only. */ -} pp_cond_ctx; - -/* Should be enuff. */ -#define CONDITION_STACK_SIZE 64 - -typedef struct -{ - pp_cond_ctx stack[CONDITION_STACK_SIZE]; - pp_cond_ctx *top; -} pp_cond_stack; - -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 -pp_cond_stack_reevaluate (pp_cond_stack *self) -{ - /* There must be at least 2 conditions on the stack - one global and one being evaluated. */ - assert (self->top <= &self->stack[CONDITION_STACK_SIZE - 2]); - - self->top->effective = self->top->current && self->top[1].effective; -} - - -/** - * Extension enables through #extension directive. - * NOTE: Currently, only enable/disable state is stored. - */ -typedef struct -{ - GLboolean ARB_draw_buffers; - GLboolean ARB_texture_rectangle; -} pp_ext; - - -/** - * Disable all extensions. Called at startup and on #extension all: disable. - */ -static GLvoid -pp_ext_disable_all(pp_ext *self) -{ - _mesa_memset(self, 0, sizeof(self)); -} - - -/** - * Called during preprocessor initialization to set the initial enable/disable - * state of extensions. - */ -static GLvoid -pp_ext_init(pp_ext *self, const struct gl_extensions *extensions) -{ - pp_ext_disable_all (self); - self->ARB_draw_buffers = GL_TRUE; - if (extensions->NV_texture_rectangle) - self->ARB_texture_rectangle = GL_TRUE; -} - -/** - * Called in response to #extension directives to enable/disable - * the named extension. - */ -static GLboolean -pp_ext_set(pp_ext *self, const char *name, GLboolean enable) -{ - if (_mesa_strcmp (name, "GL_ARB_draw_buffers") == 0) - self->ARB_draw_buffers = enable; - else if (_mesa_strcmp (name, "GL_ARB_texture_rectangle") == 0) - self->ARB_texture_rectangle = enable; - else - return GL_FALSE; - return GL_TRUE; -} - - -/** - * Called in response to #pragma. For example, "#pragma debug(on)" would - * call this function as pp_pragma("debug", "on"). - * \return GL_TRUE if pragma is valid, GL_FALSE if invalid - */ -static GLboolean -pp_pragma(struct gl_sl_pragmas *pragmas, const char *pragma, const char *param) -{ -#if 0 - printf("#pragma %s %s\n", pragma, param); -#endif - if (_mesa_strcmp(pragma, "optimize") == 0) { - if (!param) - return GL_FALSE; /* missing required param */ - if (_mesa_strcmp(param, "on") == 0) { - if (!pragmas->IgnoreOptimize) - pragmas->Optimize = GL_TRUE; - } - else if (_mesa_strcmp(param, "off") == 0) { - if (!pragmas->IgnoreOptimize) - pragmas->Optimize = GL_FALSE; - } - else { - return GL_FALSE; /* invalid param */ - } - } - else if (_mesa_strcmp(pragma, "debug") == 0) { - if (!param) - return GL_FALSE; /* missing required param */ - if (_mesa_strcmp(param, "on") == 0) { - if (!pragmas->IgnoreDebug) - pragmas->Debug = GL_TRUE; - } - else if (_mesa_strcmp(param, "off") == 0) { - if (!pragmas->IgnoreDebug) - pragmas->Debug = GL_FALSE; - } - else { - return GL_FALSE; /* invalid param */ - } - } - /* all other pragmas are silently ignored */ - return GL_TRUE; -} - - -/** - * The state of preprocessor: current line, file and version number, list - * of all defined macros and the #if/#endif context. - */ -typedef struct -{ - GLint line; - GLint file; - GLint version; - pp_symbols symbols; - pp_ext ext; - slang_info_log *elog; - pp_cond_stack cond; -} pp_state; - -static GLvoid -pp_state_init (pp_state *self, slang_info_log *elog, - const struct gl_extensions *extensions) -{ - self->line = 0; - self->file = 1; -#if FEATURE_es2_glsl - self->version = 100; -#else - self->version = 110; -#endif - pp_symbols_init (&self->symbols); - pp_ext_init (&self->ext, extensions); - self->elog = elog; - - /* Initialize condition stack and create the global context. */ - self->cond.top = &self->cond.stack[CONDITION_STACK_SIZE - 1]; - self->cond.top->current = GL_TRUE; - self->cond.top->effective = GL_TRUE; - self->cond.top->else_allowed = GL_FALSE; - self->cond.top->endif_required = GL_FALSE; -} - -static GLvoid -pp_state_free (pp_state *self) -{ - pp_symbols_free (&self->symbols); -} - -#define IS_FIRST_ID_CHAR(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z') || (x) == '_') -#define IS_NEXT_ID_CHAR(x) (IS_FIRST_ID_CHAR(x) || ((x) >= '0' && (x) <= '9')) -#define IS_WHITE(x) ((x) == ' ' || (x) == '\n') -#define IS_NULL(x) ((x) == '\0') - -#define SKIP_WHITE(x) do { while (IS_WHITE(*(x))) (x)++; } while (GL_FALSE) - -typedef struct -{ - slang_string *output; - const char *input; - pp_state *state; -} expand_state; - -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 -expand (expand_state *, pp_symbols *); - -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; -} - -/* - * Function expand() expands source text from <input> to <output>. The expansion is made using - * the list passed in <symbols> parameter. It allows us to expand macro formal parameters with - * actual parameters. The global list of symbols from pp state is used when doing a recursive - * call of expand(). - */ - -static GLboolean -expand (expand_state *e, pp_symbols *symbols) -{ - while (!IS_NULL(*e->input)) { - if (IS_FIRST_ID_CHAR(*e->input)) { - slang_string buffer; - const char *id; - - /* Parse the identifier. */ - slang_string_init (&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); - - /* Now check if the identifier is special in some way. The "defined" identifier is - * actually an operator that we must handle here and expand it either to " 0 " or " 1 ". - * The other identifiers start with "__" and we expand it to appropriate values - * taken from the preprocessor state. */ - if (_mesa_strcmp (id, "defined") == 0) { - if (!expand_defined (e, &buffer)) - return GL_FALSE; - } - else if (_mesa_strcmp (id, "__LINE__") == 0) { - slang_string_pushc (e->output, ' '); - slang_string_pushi (e->output, e->state->line); - slang_string_pushc (e->output, ' '); - } - else if (_mesa_strcmp (id, "__FILE__") == 0) { - slang_string_pushc (e->output, ' '); - slang_string_pushi (e->output, e->state->file); - slang_string_pushc (e->output, ' '); - } - else if (_mesa_strcmp (id, "__VERSION__") == 0) { - slang_string_pushc (e->output, ' '); - slang_string_pushi (e->output, e->state->version); - slang_string_pushc (e->output, ' '); - } -#if FEATURE_es2_glsl - else if (_mesa_strcmp (id, "GL_ES") == 0 || - _mesa_strcmp (id, "GL_FRAGMENT_PRECISION_HIGH") == 0) { - slang_string_pushc (e->output, ' '); - slang_string_pushi (e->output, '1'); - slang_string_pushc (e->output, ' '); - } -#endif - else { - pp_symbol *symbol; - - /* The list of symbols from <symbols> take precedence over the list from <state>. - * Note that in some cases this is the same list so avoid double look-up. */ - symbol = pp_symbols_find (symbols, id); - if (symbol == NULL && symbols != &e->state->symbols) - symbol = pp_symbols_find (&e->state->symbols, id); - - /* If the symbol was found, recursively expand its definition. */ - if (symbol != NULL) { - if (!expand_symbol (e, symbol)) { - slang_string_free (&buffer); - return GL_FALSE; - } - } - else { - slang_string_push (e->output, &buffer); - } - } - slang_string_free (&buffer); - } - else if (IS_WHITE(*e->input)) { - slang_string_pushc (e->output, *e->input++); - } - else { - while (!IS_WHITE(*e->input) && !IS_NULL(*e->input) && !IS_FIRST_ID_CHAR(*e->input)) - slang_string_pushc (e->output, *e->input++); - } - } - return GL_TRUE; -} - -static GLboolean -parse_if (slang_string *output, const byte *prod, GLuint *pi, GLint *result, pp_state *state, - grammar eid) -{ - const char *text; - GLuint len; - - text = (const char *) (&prod[*pi]); - len = _mesa_strlen (text); - - if (state->cond.top->effective) { - slang_string expr; - GLuint count; - GLint results[2]; - expand_state es; - - /* Expand the expression. */ - slang_string_init (&expr); - es.output = &expr; - es.input = text; - es.state = state; - if (!expand (&es, &state->symbols)) - return GL_FALSE; - - /* Execute the expression. */ - count = execute_expressions (output, eid, (const byte *) (slang_string_cstr (&expr)), - results, state->elog); - slang_string_free (&expr); - if (count != 1) - return GL_FALSE; - *result = results[0]; - } - else { - /* The directive is dead. */ - *result = 0; - } - - *pi += len + 1; - return GL_TRUE; -} - -#define ESCAPE_TOKEN 0 - -#define TOKEN_END 0 -#define TOKEN_DEFINE 1 -#define TOKEN_UNDEF 2 -#define TOKEN_IF 3 -#define TOKEN_ELSE 4 -#define TOKEN_ELIF 5 -#define TOKEN_ENDIF 6 -#define TOKEN_ERROR 7 -#define TOKEN_PRAGMA 8 -#define TOKEN_EXTENSION 9 -#define TOKEN_LINE 10 - -#define PARAM_END 0 -#define PARAM_PARAMETER 1 - -#define BEHAVIOR_REQUIRE 1 -#define BEHAVIOR_ENABLE 2 -#define BEHAVIOR_WARN 3 -#define BEHAVIOR_DISABLE 4 - -#define PRAGMA_NO_PARAM 0 -#define PRAGMA_PARAM 1 - - -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); - - /* 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; -} - - -/** - * Remove the continuation characters from the input string. - * This is the very first step in preprocessing and is effective - * even inside comment blocks. - * If there is a whitespace between a backslash and a newline, - * this is not considered as a line continuation. - * \return GL_TRUE for success, GL_FALSE otherwise. - */ -static GLboolean -_slang_preprocess_backslashes(slang_string *output, - const char *input) -{ - while (*input) { - if (input[0] == '\\') { - /* If a newline follows, eat the backslash and the newline. */ - if (input[1] == '\r') { - if (input[2] == '\n') { - input += 3; - } else { - input += 2; - } - } else if (input[1] == '\n') { - if (input[2] == '\r') { - input += 3; - } else { - input += 2; - } - } else { - /* Leave the backslash alone. */ - slang_string_pushc(output, *input++); - } - } else { - slang_string_pushc(output, *input++); - } - } - return GL_TRUE; -} - - -/** - * Run preprocessor on source code. - * \param extensions indicates which GL extensions are enabled - * \param output the post-process results - * \param input the input text - * \param elog log to record warnings, errors - * \param extensions out extension settings - * \param pragmas in/out #pragma settings - * \return GL_TRUE for success, GL_FALSE for error - */ -GLboolean -_slang_preprocess_directives(slang_string *output, - const char *input, - slang_info_log *elog, - const struct gl_extensions *extensions, - struct gl_sl_pragmas *pragmas) -{ - grammar pid, eid; - GLboolean success; - slang_string without_backslashes; - - pid = grammar_load_from_text ((const byte *) (slang_pp_directives_syn)); - if (pid == 0) { - grammar_error_to_log (elog); - return GL_FALSE; - } - eid = grammar_load_from_text ((const byte *) (slang_pp_expression_syn)); - if (eid == 0) { - grammar_error_to_log (elog); - grammar_destroy (pid); - return GL_FALSE; - } - - slang_string_init(&without_backslashes); - success = _slang_preprocess_backslashes(&without_backslashes, input); - - if (0) { - _mesa_printf("Pre-processed shader:\n"); - _mesa_printf("%s", slang_string_cstr(&without_backslashes)); - _mesa_printf("----------------------\n"); - } - - if (success) { - success = preprocess_source(output, - slang_string_cstr(&without_backslashes), - pid, - eid, - elog, - extensions, - pragmas); - } - - slang_string_free(&without_backslashes); - grammar_destroy (eid); - grammar_destroy (pid); - - if (0) { - _mesa_printf("Post-processed shader:\n"); - _mesa_printf("%s", slang_string_cstr(output)); - _mesa_printf("----------------------\n"); - } - - return success; -} - |