X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fglsl%2Fglcpp%2Fglcpp-parse.y;h=a11b6b2c7c8b81f2ae62f5d005651aac1884f020;hb=3bdbc1e436828606d0b549b9480e7cc28b42d159;hp=5474577ebe4a2d2b2627339aa8d95290ce5d9e0b;hpb=6005e9cb283214cd57038c7c5e7758ba72ec6ac2;p=mesa.git diff --git a/src/glsl/glcpp/glcpp-parse.y b/src/glsl/glcpp/glcpp-parse.y index 5474577ebe4..a11b6b2c7c8 100644 --- a/src/glsl/glcpp/glcpp-parse.y +++ b/src/glsl/glcpp/glcpp-parse.y @@ -57,6 +57,9 @@ _string_list_append_item (string_list_t *list, const char *str); static int _string_list_contains (string_list_t *list, const char *member, int *index); +static const char * +_string_list_has_duplicate (string_list_t *list); + static int _string_list_length (string_list_t *list); @@ -105,18 +108,25 @@ _parser_active_list_pop (glcpp_parser_t *parser); static int _parser_active_list_contains (glcpp_parser_t *parser, const char *identifier); +typedef enum { + EXPANSION_MODE_IGNORE_DEFINED, + EXPANSION_MODE_EVALUATE_DEFINED +} expansion_mode_t; + /* Expand list, and begin lexing from the result (after first * prefixing a token of type 'head_token_type'). */ static void _glcpp_parser_expand_and_lex_from (glcpp_parser_t *parser, int head_token_type, - token_list_t *list); + token_list_t *list, + expansion_mode_t mode); /* Perform macro expansion in-place on the given list. */ static void _glcpp_parser_expand_token_list (glcpp_parser_t *parser, - token_list_t *list); + token_list_t *list, + expansion_mode_t mode); static void _glcpp_parser_print_expanded_token_list (glcpp_parser_t *parser, @@ -135,7 +145,7 @@ _glcpp_parser_skip_stack_pop (glcpp_parser_t *parser, YYLTYPE *loc); static void _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t version, - const char *ident); + const char *ident, bool explicitly_set); static int glcpp_parser_lex (YYSTYPE *yylval, YYLTYPE *yylloc, glcpp_parser_t *parser); @@ -164,13 +174,18 @@ add_builtin_define(glcpp_parser_t *parser, const char *name, int value); %lex-param {glcpp_parser_t *parser} %expect 0 -%token COMMA_FINAL DEFINED ELIF_EXPANDED HASH HASH_DEFINE FUNC_IDENTIFIER OBJ_IDENTIFIER HASH_ELIF HASH_ELSE HASH_ENDIF HASH_IF HASH_IFDEF HASH_IFNDEF HASH_LINE HASH_UNDEF HASH_VERSION IDENTIFIER IF_EXPANDED INTEGER INTEGER_STRING LINE_EXPANDED NEWLINE OTHER PLACEHOLDER SPACE + + /* We use HASH_TOKEN, DEFINE_TOKEN and VERSION_TOKEN (as opposed to + * HASH, DEFINE, and VERSION) to avoid conflicts with other symbols, + * (such as the and start conditions in the lexer). */ +%token DEFINED ELIF_EXPANDED HASH_TOKEN DEFINE_TOKEN FUNC_IDENTIFIER OBJ_IDENTIFIER ELIF ELSE ENDIF ERROR_TOKEN IF IFDEF IFNDEF LINE PRAGMA UNDEF VERSION_TOKEN GARBAGE IDENTIFIER IF_EXPANDED INTEGER INTEGER_STRING LINE_EXPANDED NEWLINE OTHER PLACEHOLDER SPACE PLUS_PLUS MINUS_MINUS %token PASTE -%type expression INTEGER operator SPACE integer_constant -%type IDENTIFIER FUNC_IDENTIFIER OBJ_IDENTIFIER INTEGER_STRING OTHER +%type INTEGER operator SPACE integer_constant +%type expression +%type IDENTIFIER FUNC_IDENTIFIER OBJ_IDENTIFIER INTEGER_STRING OTHER ERROR_TOKEN PRAGMA %type identifier_list -%type preprocessing_token conditional_token -%type pp_tokens replacement_list text_line conditional_tokens +%type preprocessing_token +%type pp_tokens replacement_list text_line %left OR %left AND %left '|' @@ -183,6 +198,8 @@ add_builtin_define(glcpp_parser_t *parser, const char *name, int value); %left '*' '/' '%' %right UNARY +%debug + %% input: @@ -191,32 +208,26 @@ input: ; line: - control_line { - ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "\n"); - } -| HASH_LINE pp_tokens NEWLINE { - if (parser->skip_stack == NULL || - parser->skip_stack->type == SKIP_NO_SKIP) - { - _glcpp_parser_expand_and_lex_from (parser, - LINE_EXPANDED, $2); - } - } + control_line +| SPACE control_line | text_line { _glcpp_parser_print_expanded_token_list (parser, $1); ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "\n"); ralloc_free ($1); } | expanded_line -| HASH non_directive ; expanded_line: IF_EXPANDED expression NEWLINE { - _glcpp_parser_skip_stack_push_if (parser, & @1, $2); + if (parser->is_gles && $2.undefined_macro) + glcpp_error(& @1, parser, "undefined macro %s in expression (illegal in GLES)", $2.undefined_macro); + _glcpp_parser_skip_stack_push_if (parser, & @1, $2.value); } | ELIF_EXPANDED expression NEWLINE { - _glcpp_parser_skip_stack_change_if (parser, & @1, "elif", $2); + if (parser->is_gles && $2.undefined_macro) + glcpp_error(& @1, parser, "undefined macro %s in expression (illegal in GLES)", $2.undefined_macro); + _glcpp_parser_skip_stack_change_if (parser, & @1, "elif", $2.value); } | LINE_EXPANDED integer_constant NEWLINE { parser->has_new_line_number = 1; @@ -238,25 +249,62 @@ expanded_line: } ; -control_line: - HASH_DEFINE OBJ_IDENTIFIER replacement_list NEWLINE { - _define_object_macro (parser, & @2, $2, $3); +define: + OBJ_IDENTIFIER replacement_list NEWLINE { + _define_object_macro (parser, & @1, $1, $2); + } +| FUNC_IDENTIFIER '(' ')' replacement_list NEWLINE { + _define_function_macro (parser, & @1, $1, NULL, $4); } -| HASH_DEFINE FUNC_IDENTIFIER '(' ')' replacement_list NEWLINE { - _define_function_macro (parser, & @2, $2, NULL, $5); +| FUNC_IDENTIFIER '(' identifier_list ')' replacement_list NEWLINE { + _define_function_macro (parser, & @1, $1, $3, $5); + } +; + +control_line: + control_line_success { + ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "\n"); } -| HASH_DEFINE FUNC_IDENTIFIER '(' identifier_list ')' replacement_list NEWLINE { - _define_function_macro (parser, & @2, $2, $4, $6); +| control_line_error +| HASH_TOKEN LINE { + glcpp_parser_resolve_implicit_version(parser); + } pp_tokens NEWLINE { + + if (parser->skip_stack == NULL || + parser->skip_stack->type == SKIP_NO_SKIP) + { + _glcpp_parser_expand_and_lex_from (parser, + LINE_EXPANDED, $4, + EXPANSION_MODE_IGNORE_DEFINED); + } } -| HASH_UNDEF IDENTIFIER NEWLINE { - macro_t *macro = hash_table_find (parser->defines, $2); +; + +control_line_success: + HASH_TOKEN DEFINE_TOKEN { + glcpp_parser_resolve_implicit_version(parser); + } define +| HASH_TOKEN UNDEF { + glcpp_parser_resolve_implicit_version(parser); + } IDENTIFIER NEWLINE { + macro_t *macro; + if (strcmp("__LINE__", $4) == 0 + || strcmp("__FILE__", $4) == 0 + || strcmp("__VERSION__", $4) == 0 + || strncmp("GL_", $4, 3) == 0) + glcpp_error(& @1, parser, "Built-in (pre-defined)" + " macro names cannot be undefined."); + + macro = hash_table_find (parser->defines, $4); if (macro) { - hash_table_remove (parser->defines, $2); + hash_table_remove (parser->defines, $4); ralloc_free (macro); } - ralloc_free ($2); + ralloc_free ($4); } -| HASH_IF conditional_tokens NEWLINE { +| HASH_TOKEN IF { + glcpp_parser_resolve_implicit_version(parser); + } pp_tokens NEWLINE { /* Be careful to only evaluate the 'if' expression if * we are not skipping. When we are skipping, we * simply push a new 0-valued 'if' onto the skip @@ -268,7 +316,8 @@ control_line: parser->skip_stack->type == SKIP_NO_SKIP) { _glcpp_parser_expand_and_lex_from (parser, - IF_EXPANDED, $2); + IF_EXPANDED, $4, + EXPANSION_MODE_EVALUATE_DEFINED); } else { @@ -276,7 +325,7 @@ control_line: parser->skip_stack->type = SKIP_TO_ENDIF; } } -| HASH_IF NEWLINE { +| HASH_TOKEN IF NEWLINE { /* #if without an expression is only an error if we * are not skipping */ if (parser->skip_stack == NULL || @@ -286,17 +335,21 @@ control_line: } _glcpp_parser_skip_stack_push_if (parser, & @1, 0); } -| HASH_IFDEF IDENTIFIER junk NEWLINE { - macro_t *macro = hash_table_find (parser->defines, $2); - ralloc_free ($2); +| HASH_TOKEN IFDEF { + glcpp_parser_resolve_implicit_version(parser); + } IDENTIFIER junk NEWLINE { + macro_t *macro = hash_table_find (parser->defines, $4); + ralloc_free ($4); _glcpp_parser_skip_stack_push_if (parser, & @1, macro != NULL); } -| HASH_IFNDEF IDENTIFIER junk NEWLINE { - macro_t *macro = hash_table_find (parser->defines, $2); - ralloc_free ($2); - _glcpp_parser_skip_stack_push_if (parser, & @1, macro == NULL); +| HASH_TOKEN IFNDEF { + glcpp_parser_resolve_implicit_version(parser); + } IDENTIFIER junk NEWLINE { + macro_t *macro = hash_table_find (parser->defines, $4); + ralloc_free ($4); + _glcpp_parser_skip_stack_push_if (parser, & @3, macro == NULL); } -| HASH_ELIF conditional_tokens NEWLINE { +| HASH_TOKEN ELIF pp_tokens NEWLINE { /* Be careful to only evaluate the 'elif' expression * if we are not skipping. When we are skipping, we * simply change to a 0-valued 'elif' on the skip @@ -308,7 +361,13 @@ control_line: parser->skip_stack->type == SKIP_TO_ELSE) { _glcpp_parser_expand_and_lex_from (parser, - ELIF_EXPANDED, $2); + ELIF_EXPANDED, $3, + EXPANSION_MODE_EVALUATE_DEFINED); + } + else if (parser->skip_stack && + parser->skip_stack->has_else) + { + glcpp_error(& @1, parser, "#elif after #else"); } else { @@ -316,7 +375,7 @@ control_line: "elif", 0); } } -| HASH_ELIF NEWLINE { +| HASH_TOKEN ELIF NEWLINE { /* #elif without an expression is an error unless we * are skipping. */ if (parser->skip_stack && @@ -324,6 +383,11 @@ control_line: { glcpp_error(& @1, parser, "#elif with no expression"); } + else if (parser->skip_stack && + parser->skip_stack->has_else) + { + glcpp_error(& @1, parser, "#elif after #else"); + } else { _glcpp_parser_skip_stack_change_if (parser, & @1, @@ -331,19 +395,52 @@ control_line: glcpp_warning(& @1, parser, "ignoring illegal #elif without expression"); } } -| HASH_ELSE { - _glcpp_parser_skip_stack_change_if (parser, & @1, "else", 1); - } NEWLINE -| HASH_ENDIF { +| HASH_TOKEN ELSE { parser->lexing_directive = 1; } NEWLINE { + if (parser->skip_stack && + parser->skip_stack->has_else) + { + glcpp_error(& @1, parser, "multiple #else"); + } + else + { + _glcpp_parser_skip_stack_change_if (parser, & @1, "else", 1); + if (parser->skip_stack) + parser->skip_stack->has_else = true; + } + } +| HASH_TOKEN ENDIF { _glcpp_parser_skip_stack_pop (parser, & @1); } NEWLINE -| HASH_VERSION integer_constant NEWLINE { - _glcpp_parser_handle_version_declaration(parser, $2, NULL); +| HASH_TOKEN VERSION_TOKEN integer_constant NEWLINE { + if (parser->version_resolved) { + glcpp_error(& @1, parser, "#version must appear on the first line"); + } + _glcpp_parser_handle_version_declaration(parser, $3, NULL, true); + } +| HASH_TOKEN VERSION_TOKEN integer_constant IDENTIFIER NEWLINE { + if (parser->version_resolved) { + glcpp_error(& @1, parser, "#version must appear on the first line"); + } + _glcpp_parser_handle_version_declaration(parser, $3, $4, true); } -| HASH_VERSION integer_constant IDENTIFIER NEWLINE { - _glcpp_parser_handle_version_declaration(parser, $2, $3); +| HASH_TOKEN NEWLINE { + glcpp_parser_resolve_implicit_version(parser); + } +| HASH_TOKEN PRAGMA NEWLINE { + ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "#%s", $2); + } +; + +control_line_error: + HASH_TOKEN ERROR_TOKEN NEWLINE { + glcpp_error(& @1, parser, "#%s", $2); + } +| HASH_TOKEN DEFINE_TOKEN NEWLINE { + glcpp_error (& @1, parser, "#define without macro name"); + } +| HASH_TOKEN GARBAGE pp_tokens NEWLINE { + glcpp_error (& @1, parser, "Illegal non-directive after #"); } -| HASH NEWLINE ; integer_constant: @@ -361,87 +458,176 @@ integer_constant: } expression: - integer_constant + integer_constant { + $$.value = $1; + $$.undefined_macro = NULL; + } | IDENTIFIER { + $$.value = 0; if (parser->is_gles) - glcpp_error(& @1, parser, "undefined macro %s in expression (illegal in GLES)", $1); - $$ = 0; + $$.undefined_macro = ralloc_strdup (parser, $1); + else + $$.undefined_macro = NULL; } | expression OR expression { - $$ = $1 || $3; + $$.value = $1.value || $3.value; + + /* Short-circuit: Only flag undefined from right side + * if left side evaluates to false. + */ + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else if (! $1.value) + $$.undefined_macro = $3.undefined_macro; } | expression AND expression { - $$ = $1 && $3; + $$.value = $1.value && $3.value; + + /* Short-circuit: Only flag undefined from right-side + * if left side evaluates to true. + */ + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else if ($1.value) + $$.undefined_macro = $3.undefined_macro; } | expression '|' expression { - $$ = $1 | $3; + $$.value = $1.value | $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression '^' expression { - $$ = $1 ^ $3; + $$.value = $1.value ^ $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression '&' expression { - $$ = $1 & $3; + $$.value = $1.value & $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression NOT_EQUAL expression { - $$ = $1 != $3; + $$.value = $1.value != $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression EQUAL expression { - $$ = $1 == $3; + $$.value = $1.value == $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression GREATER_OR_EQUAL expression { - $$ = $1 >= $3; + $$.value = $1.value >= $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression LESS_OR_EQUAL expression { - $$ = $1 <= $3; + $$.value = $1.value <= $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression '>' expression { - $$ = $1 > $3; + $$.value = $1.value > $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression '<' expression { - $$ = $1 < $3; + $$.value = $1.value < $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression RIGHT_SHIFT expression { - $$ = $1 >> $3; + $$.value = $1.value >> $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression LEFT_SHIFT expression { - $$ = $1 << $3; + $$.value = $1.value << $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression '-' expression { - $$ = $1 - $3; + $$.value = $1.value - $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression '+' expression { - $$ = $1 + $3; + $$.value = $1.value + $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression '%' expression { - if ($3 == 0) { + if ($3.value == 0) { yyerror (& @1, parser, "zero modulus in preprocessor directive"); } else { - $$ = $1 % $3; + $$.value = $1.value % $3.value; } + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression '/' expression { - if ($3 == 0) { + if ($3.value == 0) { yyerror (& @1, parser, "division by 0 in preprocessor directive"); } else { - $$ = $1 / $3; + $$.value = $1.value / $3.value; } + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | expression '*' expression { - $$ = $1 * $3; + $$.value = $1.value * $3.value; + if ($1.undefined_macro) + $$.undefined_macro = $1.undefined_macro; + else + $$.undefined_macro = $3.undefined_macro; } | '!' expression %prec UNARY { - $$ = ! $2; + $$.value = ! $2.value; + $$.undefined_macro = $2.undefined_macro; } | '~' expression %prec UNARY { - $$ = ~ $2; + $$.value = ~ $2.value; + $$.undefined_macro = $2.undefined_macro; } | '-' expression %prec UNARY { - $$ = - $2; + $$.value = - $2.value; + $$.undefined_macro = $2.undefined_macro; } | '+' expression %prec UNARY { - $$ = + $2; + $$.value = + $2.value; + $$.undefined_macro = $2.undefined_macro; } | '(' expression ')' { $$ = $2; @@ -466,12 +652,6 @@ text_line: | pp_tokens NEWLINE ; -non_directive: - pp_tokens NEWLINE { - yyerror (& @1, parser, "Invalid tokens after #"); - } -; - replacement_list: /* empty */ { $$ = NULL; } | pp_tokens @@ -480,32 +660,7 @@ replacement_list: junk: /* empty */ | pp_tokens { - glcpp_warning(&@1, parser, "extra tokens at end of directive"); - } -; - -conditional_token: - /* Handle "defined" operator */ - DEFINED IDENTIFIER { - int v = hash_table_find (parser->defines, $2) ? 1 : 0; - $$ = _token_create_ival (parser, INTEGER, v); - } -| DEFINED '(' IDENTIFIER ')' { - int v = hash_table_find (parser->defines, $3) ? 1 : 0; - $$ = _token_create_ival (parser, INTEGER, v); - } -| preprocessing_token -; - -conditional_tokens: - /* Exactly the same as pp_tokens, but using conditional_token */ - conditional_token { - $$ = _token_list_create (parser); - _token_list_append ($$, $1); - } -| conditional_tokens conditional_token { - $$ = $1; - _token_list_append ($$, $2); + glcpp_error(&@1, parser, "extra tokens at end of directive"); } ; @@ -534,6 +689,10 @@ preprocessing_token: $$ = _token_create_ival (parser, $1, $1); $$->location = yylloc; } +| DEFINED { + $$ = _token_create_ival (parser, DEFINED, DEFINED); + $$->location = yylloc; + } | OTHER { $$ = _token_create_str (parser, OTHER, $1); $$->location = yylloc; @@ -576,6 +735,8 @@ operator: | ',' { $$ = ','; } | '=' { $$ = '='; } | PASTE { $$ = PASTE; } +| PLUS_PLUS { $$ = PLUS_PLUS; } +| MINUS_MINUS { $$ = MINUS_MINUS; } ; %% @@ -631,6 +792,25 @@ _string_list_contains (string_list_t *list, const char *member, int *index) return 0; } +/* Return duplicate string in list (if any), NULL otherwise. */ +const char * +_string_list_has_duplicate (string_list_t *list) +{ + string_node_t *node, *dup; + + if (list == NULL) + return NULL; + + for (node = list->head; node; node = node->next) { + for (dup = node->next; dup; dup = dup->next) { + if (strcmp (node->str, dup->str) == 0) + return node->str; + } + } + + return NULL; +} + int _string_list_length (string_list_t *list) { @@ -888,14 +1068,16 @@ _token_list_equal_ignoring_space (token_list_t *a, token_list_t *b) if (node_a == NULL || node_b == NULL) return 0; - - if (node_a->token->type == SPACE) { - node_a = node_a->next; - continue; - } - - if (node_b->token->type == SPACE) { - node_b = node_b->next; + /* Make sure whitespace appears in the same places in both. + * It need not be exactly the same amount of whitespace, + * though. + */ + if (node_a->token->type == SPACE + && node_b->token->type == SPACE) { + while (node_a->token->type == SPACE) + node_a = node_a->next; + while (node_b->token->type == SPACE) + node_b = node_b->next; continue; } @@ -975,14 +1157,21 @@ _token_print (char **out, size_t *len, token_t *token) case PASTE: ralloc_asprintf_rewrite_tail (out, len, "##"); break; - case COMMA_FINAL: - ralloc_asprintf_rewrite_tail (out, len, ","); + case PLUS_PLUS: + ralloc_asprintf_rewrite_tail (out, len, "++"); + break; + case MINUS_MINUS: + ralloc_asprintf_rewrite_tail (out, len, "--"); + break; + case DEFINED: + ralloc_asprintf_rewrite_tail (out, len, "defined"); break; case PLACEHOLDER: /* Nothing to print. */ break; default: assert(!"Error: Don't know how to print token."); + break; } } @@ -1148,10 +1337,9 @@ static void add_builtin_define(glcpp_parser_t *parser, } glcpp_parser_t * -glcpp_parser_create (const struct gl_extensions *extensions, int api) +glcpp_parser_create (const struct gl_extensions *extensions, gl_api api) { glcpp_parser_t *parser; - int language_version; parser = ralloc (NULL, glcpp_parser_t); @@ -1159,14 +1347,18 @@ glcpp_parser_create (const struct gl_extensions *extensions, int api) parser->defines = hash_table_ctor (32, hash_table_string_hash, hash_table_string_compare); parser->active = NULL; - parser->lexing_if = 0; + parser->lexing_directive = 0; parser->space_tokens = 1; + parser->last_token_was_newline = 0; + parser->last_token_was_space = 0; + parser->first_non_space_token_this_line = 1; parser->newline_as_space = 0; parser->in_control_line = 0; parser->paren_count = 0; parser->commented_newlines = 0; parser->skip_stack = NULL; + parser->skipping = 0; parser->lex_from_list = NULL; parser->lex_from_node = NULL; @@ -1177,96 +1369,15 @@ glcpp_parser_create (const struct gl_extensions *extensions, int api) parser->info_log_length = 0; parser->error = 0; + parser->extensions = extensions; + parser->api = api; + parser->version_resolved = false; + parser->has_new_line_number = 0; parser->new_line_number = 1; parser->has_new_source_number = 0; parser->new_source_number = 0; - parser->is_gles = false; - - /* Add pre-defined macros. */ - if (api == API_OPENGLES2) { - parser->is_gles = true; - add_builtin_define(parser, "GL_ES", 1); - - if (extensions != NULL) { - if (extensions->OES_EGL_image_external) - add_builtin_define(parser, "GL_OES_EGL_image_external", 1); - } - } else { - add_builtin_define(parser, "GL_ARB_draw_buffers", 1); - add_builtin_define(parser, "GL_ARB_texture_rectangle", 1); - - if (extensions != NULL) { - if (extensions->EXT_texture_array) { - add_builtin_define(parser, "GL_EXT_texture_array", 1); - } - - if (extensions->ARB_fragment_coord_conventions) - add_builtin_define(parser, "GL_ARB_fragment_coord_conventions", - 1); - - if (extensions->ARB_explicit_attrib_location) - add_builtin_define(parser, "GL_ARB_explicit_attrib_location", 1); - - if (extensions->ARB_shader_texture_lod) - add_builtin_define(parser, "GL_ARB_shader_texture_lod", 1); - - if (extensions->ARB_draw_instanced) - add_builtin_define(parser, "GL_ARB_draw_instanced", 1); - - if (extensions->ARB_conservative_depth) { - add_builtin_define(parser, "GL_AMD_conservative_depth", 1); - add_builtin_define(parser, "GL_ARB_conservative_depth", 1); - } - - if (extensions->ARB_shader_bit_encoding) - add_builtin_define(parser, "GL_ARB_shader_bit_encoding", 1); - - if (extensions->ARB_uniform_buffer_object) - add_builtin_define(parser, "GL_ARB_uniform_buffer_object", 1); - - if (extensions->ARB_texture_cube_map_array) - add_builtin_define(parser, "GL_ARB_texture_cube_map_array", 1); - - if (extensions->ARB_shading_language_packing) - add_builtin_define(parser, "GL_ARB_shading_language_packing", 1); - - if (extensions->ARB_texture_multisample) - add_builtin_define(parser, "GL_ARB_texture_multisample", 1); - - if (extensions->ARB_texture_query_levels) - add_builtin_define(parser, "GL_ARB_texture_query_levels", 1); - - if (extensions->ARB_texture_query_lod) - add_builtin_define(parser, "GL_ARB_texture_query_lod", 1); - - if (extensions->ARB_gpu_shader5) - add_builtin_define(parser, "GL_ARB_gpu_shader5", 1); - - if (extensions->AMD_vertex_shader_layer) - add_builtin_define(parser, "GL_AMD_vertex_shader_layer", 1); - - if (extensions->ARB_shading_language_420pack) - add_builtin_define(parser, "GL_ARB_shading_language_420pack", 1); - - if (extensions->ARB_sample_shading) - add_builtin_define(parser, "GL_ARB_sample_shading", 1); - - if (extensions->EXT_shader_integer_mix) - add_builtin_define(parser, "GL_EXT_shader_integer_mix", 1); - - if (extensions->ARB_texture_gather) - add_builtin_define(parser, "GL_ARB_texture_gather", 1); - - if (extensions->ARB_shader_atomic_counters) - add_builtin_define(parser, "GL_ARB_shader_atomic_counters", 1); - } - } - - language_version = 110; - add_builtin_define(parser, "__VERSION__", language_version); - return parser; } @@ -1352,7 +1463,7 @@ _arguments_parse (argument_list_t *arguments, else { if (argument->head == NULL) { /* Don't treat initial whitespace as - * part of the arguement. */ + * part of the argument. */ if (node->token->type == SPACE) continue; } @@ -1393,15 +1504,143 @@ _token_list_create_with_one_integer (void *ctx, int ival) return _token_list_create_with_one_ival (ctx, INTEGER, ival); } +/* Evaluate a DEFINED token node (based on subsequent tokens in the list). + * + * Note: This function must only be called when "node" is a DEFINED token, + * (and will abort with an assertion failure otherwise). + * + * If "node" is followed, (ignoring any SPACE tokens), by an IDENTIFIER token + * (optionally preceded and followed by '(' and ')' tokens) then the following + * occurs: + * + * If the identifier is a defined macro, this function returns 1. + * + * If the identifier is not a defined macro, this function returns 0. + * + * In either case, *last will be updated to the last node in the list + * consumed by the evaluation, (either the token of the identifier or the + * token of the closing parenthesis). + * + * In all other cases, (such as "node is the final node of the list", or + * "missing closing parenthesis", etc.), this function generates a + * preprocessor error, returns -1 and *last will not be set. + */ +static int +_glcpp_parser_evaluate_defined (glcpp_parser_t *parser, + token_node_t *node, + token_node_t **last) +{ + token_node_t *argument, *defined = node; + + assert (node->token->type == DEFINED); + + node = node->next; + + /* Ignore whitespace after DEFINED token. */ + while (node && node->token->type == SPACE) + node = node->next; + + if (node == NULL) + goto FAIL; + + if (node->token->type == IDENTIFIER || node->token->type == OTHER) { + argument = node; + } else if (node->token->type == '(') { + node = node->next; + + /* Ignore whitespace after '(' token. */ + while (node && node->token->type == SPACE) + node = node->next; + + if (node == NULL || (node->token->type != IDENTIFIER && + node->token->type != OTHER)) + { + goto FAIL; + } + + argument = node; + + node = node->next; + + /* Ignore whitespace after identifier, before ')' token. */ + while (node && node->token->type == SPACE) + node = node->next; + + if (node == NULL || node->token->type != ')') + goto FAIL; + } else { + goto FAIL; + } + + *last = node; + + return hash_table_find (parser->defines, + argument->token->value.str) ? 1 : 0; + +FAIL: + glcpp_error (&defined->token->location, parser, + "\"defined\" not followed by an identifier"); + return -1; +} + +/* Evaluate all DEFINED nodes in a given list, modifying the list in place. + */ +static void +_glcpp_parser_evaluate_defined_in_list (glcpp_parser_t *parser, + token_list_t *list) +{ + token_node_t *node, *node_prev, *replacement, *last = NULL; + int value; + + if (list == NULL) + return; + + node_prev = NULL; + node = list->head; + + while (node) { + + if (node->token->type != DEFINED) + goto NEXT; + + value = _glcpp_parser_evaluate_defined (parser, node, &last); + if (value == -1) + goto NEXT; + + replacement = ralloc (list, token_node_t); + replacement->token = _token_create_ival (list, INTEGER, value); + + /* Splice replacement node into list, replacing from "node" + * through "last". */ + if (node_prev) + node_prev->next = replacement; + else + list->head = replacement; + replacement->next = last->next; + if (last == list->tail) + list->tail = replacement; + + node = replacement; + + NEXT: + node_prev = node; + node = node->next; + } +} + /* Perform macro expansion on 'list', placing the resulting tokens * into a new list which is initialized with a first token of type * 'head_token_type'. Then begin lexing from the resulting list, * (return to the current lexing source when this list is exhausted). + * + * See the documentation of _glcpp_parser_expand_token_list for a description + * of the "mode" parameter. */ static void _glcpp_parser_expand_and_lex_from (glcpp_parser_t *parser, int head_token_type, - token_list_t *list) + token_list_t *list, + expansion_mode_t mode) { token_list_t *expanded; token_t *token; @@ -1409,7 +1648,7 @@ _glcpp_parser_expand_and_lex_from (glcpp_parser_t *parser, expanded = _token_list_create (parser); token = _token_create_ival (parser, head_token_type, head_token_type); _token_list_append (expanded, token); - _glcpp_parser_expand_token_list (parser, list); + _glcpp_parser_expand_token_list (parser, list, mode); _token_list_append_list (expanded, list); glcpp_parser_lex_from (parser, expanded); } @@ -1472,12 +1711,15 @@ _glcpp_parser_apply_pastes (glcpp_parser_t *parser, token_list_t *list) * *last to the last node in the list that was consumed by the * expansion. Specifically, *last will be set as follows: as the * token of the closing right parenthesis. + * + * See the documentation of _glcpp_parser_expand_token_list for a description + * of the "mode" parameter. */ static token_list_t * _glcpp_parser_expand_function (glcpp_parser_t *parser, token_node_t *node, - token_node_t **last) - + token_node_t **last, + expansion_mode_t mode) { macro_t *macro; const char *identifier; @@ -1546,7 +1788,8 @@ _glcpp_parser_expand_function (glcpp_parser_t *parser, expanded_argument = _token_list_copy (parser, argument); _glcpp_parser_expand_token_list (parser, - expanded_argument); + expanded_argument, + mode); _token_list_append_list (substituted, expanded_argument); } else { @@ -1586,11 +1829,15 @@ _glcpp_parser_expand_function (glcpp_parser_t *parser, * * As the token of the closing right parenthesis in the case of * function-like macro expansion. + * + * See the documentation of _glcpp_parser_expand_token_list for a description + * of the "mode" parameter. */ static token_list_t * _glcpp_parser_expand_node (glcpp_parser_t *parser, token_node_t *node, - token_node_t **last) + token_node_t **last, + expansion_mode_t mode) { token_t *token = node->token; const char *identifier; @@ -1598,14 +1845,6 @@ _glcpp_parser_expand_node (glcpp_parser_t *parser, /* We only expand identifiers */ if (token->type != IDENTIFIER) { - /* We change any COMMA into a COMMA_FINAL to prevent - * it being mistaken for an argument separator - * later. */ - if (token->type == ',') { - token->type = COMMA_FINAL; - token->value.ival = COMMA_FINAL; - } - return NULL; } @@ -1657,7 +1896,7 @@ _glcpp_parser_expand_node (glcpp_parser_t *parser, return replacement; } - return _glcpp_parser_expand_function (parser, node, last); + return _glcpp_parser_expand_function (parser, node, last, mode); } /* Push a new identifier onto the parser's active list. @@ -1716,11 +1955,28 @@ _parser_active_list_contains (glcpp_parser_t *parser, const char *identifier) /* Walk over the token list replacing nodes with their expansion. * Whenever nodes are expanded the walking will walk over the new * nodes, continuing to expand as necessary. The results are placed in - * 'list' itself; + * 'list' itself. + * + * The "mode" argument controls the handling of any DEFINED tokens that + * result from expansion as follows: + * + * EXPANSION_MODE_IGNORE_DEFINED: Any resulting DEFINED tokens will be + * left in the final list, unevaluated. This is the correct mode + * for expanding any list in any context other than a + * preprocessor conditional, (#if or #elif). + * + * EXPANSION_MODE_EVALUATE_DEFINED: Any resulting DEFINED tokens will be + * evaluated to 0 or 1 tokens depending on whether the following + * token is the name of a defined macro. If the DEFINED token is + * not followed by an (optionally parenthesized) identifier, then + * an error will be generated. This the correct mode for + * expanding any list in the context of a preprocessor + * conditional, (#if or #elif). */ static void _glcpp_parser_expand_token_list (glcpp_parser_t *parser, - token_list_t *list) + token_list_t *list, + expansion_mode_t mode) { token_node_t *node_prev; token_node_t *node, *last = NULL; @@ -1735,15 +1991,23 @@ _glcpp_parser_expand_token_list (glcpp_parser_t *parser, node_prev = NULL; node = list->head; + if (mode == EXPANSION_MODE_EVALUATE_DEFINED) + _glcpp_parser_evaluate_defined_in_list (parser, list); + while (node) { while (parser->active && parser->active->marker == node) _parser_active_list_pop (parser); - expansion = _glcpp_parser_expand_node (parser, node, &last); + expansion = _glcpp_parser_expand_node (parser, node, &last, mode); if (expansion) { token_node_t *n; + if (mode == EXPANSION_MODE_EVALUATE_DEFINED) { + _glcpp_parser_evaluate_defined_in_list (parser, + expansion); + } + for (n = node; n != last->next; n = n->next) while (parser->active && parser->active->marker == n) @@ -1796,7 +2060,7 @@ _glcpp_parser_print_expanded_token_list (glcpp_parser_t *parser, if (list == NULL) return; - _glcpp_parser_expand_token_list (parser, list); + _glcpp_parser_expand_token_list (parser, list, EXPANSION_MODE_IGNORE_DEFINED); _token_list_trim_trailing_space (list); @@ -1807,11 +2071,27 @@ static void _check_for_reserved_macro_name (glcpp_parser_t *parser, YYLTYPE *loc, const char *identifier) { - /* According to the GLSL specification, macro names starting with "__" - * or "GL_" are reserved for future use. So, don't allow them. + /* Section 3.3 (Preprocessor) of the GLSL 1.30 spec (and later) and + * the GLSL ES spec (all versions) say: + * + * "All macro names containing two consecutive underscores ( __ ) + * are reserved for future use as predefined macro names. All + * macro names prefixed with "GL_" ("GL" followed by a single + * underscore) are also reserved." + * + * The intention is that names containing __ are reserved for internal + * use by the implementation, and names prefixed with GL_ are reserved + * for use by Khronos. Since every extension adds a name prefixed + * with GL_ (i.e., the name of the extension), that should be an + * error. Names simply containing __ are dangerous to use, but should + * be allowed. + * + * A future version of the GLSL specification will clarify this. */ if (strstr(identifier, "__")) { - glcpp_error (loc, parser, "Macro names containing \"__\" are reserved.\n"); + glcpp_warning(loc, parser, + "Macro names containing \"__\" are reserved " + "for use by the implementation.\n"); } if (strncmp(identifier, "GL_", 3) == 0) { glcpp_error (loc, parser, "Macro names starting with \"GL_\" are reserved.\n"); @@ -1841,6 +2121,10 @@ _define_object_macro (glcpp_parser_t *parser, { macro_t *macro, *previous; + /* We define pre-defined macros before we've started parsing the + * actual file. So if there's no location defined yet, that's what + * were doing and we don't want to generate an error for using the + * reserved names. */ if (loc != NULL) _check_for_reserved_macro_name(parser, loc, identifier); @@ -1873,9 +2157,16 @@ _define_function_macro (glcpp_parser_t *parser, token_list_t *replacements) { macro_t *macro, *previous; + const char *dup; _check_for_reserved_macro_name(parser, loc, identifier); + /* Check for any duplicate parameter names. */ + if ((dup = _string_list_has_duplicate (parameters)) != NULL) { + glcpp_error (loc, parser, "Duplicate macro parameter \"%s\"", + dup); + } + macro = ralloc (parser, macro_t); ralloc_steal (macro, parameters); ralloc_steal (macro, replacements); @@ -1938,11 +2229,11 @@ glcpp_parser_lex (YYSTYPE *yylval, YYLTYPE *yylloc, glcpp_parser_t *parser) if (ret == NEWLINE) parser->in_control_line = 0; } - else if (ret == HASH_DEFINE || - ret == HASH_UNDEF || ret == HASH_IF || - ret == HASH_IFDEF || ret == HASH_IFNDEF || - ret == HASH_ELIF || ret == HASH_ELSE || - ret == HASH_ENDIF || ret == HASH) + else if (ret == DEFINE_TOKEN || + ret == UNDEF || ret == IF || + ret == IFDEF || ret == IFNDEF || + ret == ELIF || ret == ELSE || + ret == ENDIF || ret == HASH_TOKEN) { parser->in_control_line = 1; } @@ -2025,6 +2316,7 @@ _glcpp_parser_skip_stack_push_if (glcpp_parser_t *parser, YYLTYPE *loc, node->type = SKIP_TO_ENDIF; } + node->has_else = false; node->next = parser->skip_stack; parser->skip_stack = node; } @@ -2034,7 +2326,7 @@ _glcpp_parser_skip_stack_change_if (glcpp_parser_t *parser, YYLTYPE *loc, const char *type, int condition) { if (parser->skip_stack == NULL) { - glcpp_error (loc, parser, "%s without #if\n", type); + glcpp_error (loc, parser, "#%s without #if\n", type); return; } @@ -2063,24 +2355,140 @@ _glcpp_parser_skip_stack_pop (glcpp_parser_t *parser, YYLTYPE *loc) static void _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t version, - const char *es_identifier) + const char *es_identifier, + bool explicitly_set) { - macro_t *macro = hash_table_find (parser->defines, "__VERSION__"); - if (macro) { - hash_table_remove (parser->defines, "__VERSION__"); - ralloc_free (macro); - } + const struct gl_extensions *extensions = parser->extensions; + + if (parser->version_resolved) + return; + + parser->version_resolved = true; + add_builtin_define (parser, "__VERSION__", version); - /* If we didn't have a GLES context to begin with, (indicated - * by parser->api), then the version declaration here might - * indicate GLES. */ - if (! parser->is_gles && - (version == 100 || - (es_identifier && (strcmp(es_identifier, "es") == 0)))) - { - parser->is_gles = true; - add_builtin_define (parser, "GL_ES", 1); + parser->is_gles = (version == 100) || + (es_identifier && + (strcmp(es_identifier, "es") == 0)); + + /* Add pre-defined macros. */ + if (parser->is_gles) { + add_builtin_define(parser, "GL_ES", 1); + add_builtin_define(parser, "GL_EXT_separate_shader_objects", 1); + add_builtin_define(parser, "GL_EXT_draw_buffers", 1); + + if (extensions != NULL) { + if (extensions->OES_EGL_image_external) + add_builtin_define(parser, "GL_OES_EGL_image_external", 1); + if (extensions->OES_standard_derivatives) + add_builtin_define(parser, "GL_OES_standard_derivatives", 1); + } + } else { + add_builtin_define(parser, "GL_ARB_draw_buffers", 1); + add_builtin_define(parser, "GL_ARB_separate_shader_objects", 1); + add_builtin_define(parser, "GL_ARB_texture_rectangle", 1); + add_builtin_define(parser, "GL_AMD_shader_trinary_minmax", 1); + + + if (extensions != NULL) { + if (extensions->EXT_texture_array) + add_builtin_define(parser, "GL_EXT_texture_array", 1); + + if (extensions->ARB_arrays_of_arrays) + add_builtin_define(parser, "GL_ARB_arrays_of_arrays", 1); + + if (extensions->ARB_fragment_coord_conventions) + add_builtin_define(parser, "GL_ARB_fragment_coord_conventions", + 1); + + if (extensions->ARB_fragment_layer_viewport) + add_builtin_define(parser, "GL_ARB_fragment_layer_viewport", 1); + + if (extensions->ARB_explicit_attrib_location) + add_builtin_define(parser, "GL_ARB_explicit_attrib_location", 1); + + if (extensions->ARB_explicit_uniform_location) + add_builtin_define(parser, "GL_ARB_explicit_uniform_location", 1); + + if (extensions->ARB_shader_texture_lod) + add_builtin_define(parser, "GL_ARB_shader_texture_lod", 1); + + if (extensions->ARB_draw_instanced) + add_builtin_define(parser, "GL_ARB_draw_instanced", 1); + + if (extensions->ARB_conservative_depth) { + add_builtin_define(parser, "GL_AMD_conservative_depth", 1); + add_builtin_define(parser, "GL_ARB_conservative_depth", 1); + } + + if (extensions->ARB_shader_bit_encoding) + add_builtin_define(parser, "GL_ARB_shader_bit_encoding", 1); + + if (extensions->ARB_uniform_buffer_object) + add_builtin_define(parser, "GL_ARB_uniform_buffer_object", 1); + + if (extensions->ARB_texture_cube_map_array) + add_builtin_define(parser, "GL_ARB_texture_cube_map_array", 1); + + if (extensions->ARB_shading_language_packing) + add_builtin_define(parser, "GL_ARB_shading_language_packing", 1); + + if (extensions->ARB_texture_multisample) + add_builtin_define(parser, "GL_ARB_texture_multisample", 1); + + if (extensions->ARB_texture_query_levels) + add_builtin_define(parser, "GL_ARB_texture_query_levels", 1); + + if (extensions->ARB_texture_query_lod) + add_builtin_define(parser, "GL_ARB_texture_query_lod", 1); + + if (extensions->ARB_gpu_shader5) + add_builtin_define(parser, "GL_ARB_gpu_shader5", 1); + + if (extensions->ARB_gpu_shader_fp64) + add_builtin_define(parser, "GL_ARB_gpu_shader_fp64", 1); + + if (extensions->ARB_vertex_attrib_64bit) + add_builtin_define(parser, "GL_ARB_vertex_attrib_64bit", 1); + + if (extensions->AMD_vertex_shader_layer) + add_builtin_define(parser, "GL_AMD_vertex_shader_layer", 1); + + if (extensions->AMD_vertex_shader_viewport_index) + add_builtin_define(parser, "GL_AMD_vertex_shader_viewport_index", 1); + + if (extensions->ARB_shading_language_420pack) + add_builtin_define(parser, "GL_ARB_shading_language_420pack", 1); + + if (extensions->ARB_sample_shading) + add_builtin_define(parser, "GL_ARB_sample_shading", 1); + + if (extensions->ARB_texture_gather) + add_builtin_define(parser, "GL_ARB_texture_gather", 1); + + if (extensions->ARB_shader_atomic_counters) + add_builtin_define(parser, "GL_ARB_shader_atomic_counters", 1); + + if (extensions->ARB_viewport_array) + add_builtin_define(parser, "GL_ARB_viewport_array", 1); + + if (extensions->ARB_compute_shader) + add_builtin_define(parser, "GL_ARB_compute_shader", 1); + + if (extensions->ARB_shader_image_load_store) + add_builtin_define(parser, "GL_ARB_shader_image_load_store", 1); + + if (extensions->ARB_derivative_control) + add_builtin_define(parser, "GL_ARB_derivative_control", 1); + + if (extensions->ARB_shader_precision) + add_builtin_define(parser, "GL_ARB_shader_precision", 1); + } + } + + if (extensions != NULL) { + if (extensions->EXT_shader_integer_mix) + add_builtin_define(parser, "GL_EXT_shader_integer_mix", 1); } if (version >= 150) @@ -2094,8 +2502,27 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio if (version >= 130 || parser->is_gles) add_builtin_define (parser, "GL_FRAGMENT_PRECISION_HIGH", 1); - ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, - "#version %" PRIiMAX "%s%s", version, - es_identifier ? " " : "", - es_identifier ? es_identifier : ""); + if (explicitly_set) { + ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, + "#version %" PRIiMAX "%s%s", version, + es_identifier ? " " : "", + es_identifier ? es_identifier : ""); + } +} + +/* GLSL version if no version is explicitly specified. */ +#define IMPLICIT_GLSL_VERSION 110 + +/* GLSL ES version if no version is explicitly specified. */ +#define IMPLICIT_GLSL_ES_VERSION 100 + +void +glcpp_parser_resolve_implicit_version(glcpp_parser_t *parser) +{ + int language_version = parser->api == API_OPENGLES2 ? + IMPLICIT_GLSL_ES_VERSION : + IMPLICIT_GLSL_VERSION; + + _glcpp_parser_handle_version_declaration(parser, language_version, + NULL, false); }