glsl/glcpp: Drop extra, final newline from most output
[mesa.git] / src / glsl / glcpp / glcpp-parse.y
index 8b486d21805949ade0cf975989bc574ab3c27356..07d780e38776191118fda2efcd2b6805218fbaf1 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "glcpp.h"
 #include "main/core.h" /* for struct gl_extensions */
+#include "main/mtypes.h" /* for gl_api enum */
 
 static void
 yyerror (YYLTYPE *locp, glcpp_parser_t *parser, const char *error);
@@ -165,7 +166,8 @@ add_builtin_define(glcpp_parser_t *parser, const char *name, int value);
 %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
 %token PASTE
-%type <ival> expression INTEGER operator SPACE integer_constant
+%type <ival> INTEGER operator SPACE integer_constant
+%type <expression_value> expression
 %type <str> IDENTIFIER FUNC_IDENTIFIER OBJ_IDENTIFIER INTEGER_STRING OTHER
 %type <string_list> identifier_list
 %type <token> preprocessing_token conditional_token
@@ -194,7 +196,7 @@ line:
                ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "\n");
        }
 |      HASH_LINE {
-               glcpp_parser_resolve_version(parser);
+               glcpp_parser_resolve_implicit_version(parser);
        } pp_tokens NEWLINE {
 
                if (parser->skip_stack == NULL ||
@@ -215,10 +217,14 @@ line:
 
 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;
@@ -254,12 +260,19 @@ define:
 
 control_line:
        HASH_DEFINE {
-               glcpp_parser_resolve_version(parser);
+               glcpp_parser_resolve_implicit_version(parser);
        } define
 |      HASH_UNDEF {
-               glcpp_parser_resolve_version(parser);
+               glcpp_parser_resolve_implicit_version(parser);
        } IDENTIFIER NEWLINE {
-               macro_t *macro = hash_table_find (parser->defines, $3);
+               macro_t *macro;
+               if (strcmp("__LINE__", $3) == 0
+                   || strcmp("__FILE__", $3) == 0
+                   || strcmp("__VERSION__", $3) == 0)
+                       glcpp_error(& @1, parser, "Built-in (pre-defined)"
+                                   " macro names can not be undefined.");
+
+               macro = hash_table_find (parser->defines, $3);
                if (macro) {
                        hash_table_remove (parser->defines, $3);
                        ralloc_free (macro);
@@ -267,7 +280,7 @@ control_line:
                ralloc_free ($3);
        }
 |      HASH_IF {
-               glcpp_parser_resolve_version(parser);
+               glcpp_parser_resolve_implicit_version(parser);
        } conditional_tokens NEWLINE {
                /* Be careful to only evaluate the 'if' expression if
                 * we are not skipping. When we are skipping, we
@@ -299,14 +312,14 @@ control_line:
                _glcpp_parser_skip_stack_push_if (parser, & @1, 0);
        }
 |      HASH_IFDEF {
-               glcpp_parser_resolve_version(parser);
+               glcpp_parser_resolve_implicit_version(parser);
        } IDENTIFIER junk NEWLINE {
                macro_t *macro = hash_table_find (parser->defines, $3);
                ralloc_free ($3);
                _glcpp_parser_skip_stack_push_if (parser, & @1, macro != NULL);
        }
 |      HASH_IFNDEF {
-               glcpp_parser_resolve_version(parser);
+               glcpp_parser_resolve_implicit_version(parser);
        } IDENTIFIER junk NEWLINE {
                macro_t *macro = hash_table_find (parser->defines, $3);
                ralloc_free ($3);
@@ -357,7 +370,7 @@ control_line:
                        glcpp_warning(& @1, parser, "ignoring illegal #elif without expression");
                }
        }
-|      HASH_ELSE {
+|      HASH_ELSE { parser->lexing_directive = 1; } NEWLINE {
                if (parser->skip_stack &&
                    parser->skip_stack->has_else)
                {
@@ -369,18 +382,24 @@ control_line:
                        if (parser->skip_stack)
                                parser->skip_stack->has_else = true;
                }
-       } NEWLINE
+       }
 |      HASH_ENDIF {
                _glcpp_parser_skip_stack_pop (parser, & @1);
        } NEWLINE
 |      HASH_VERSION integer_constant NEWLINE {
+               if (parser->version_resolved) {
+                       glcpp_error(& @1, parser, "#version must appear on the first line");
+               }
                _glcpp_parser_handle_version_declaration(parser, $2, NULL, true);
        }
 |      HASH_VERSION 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, $2, $3, true);
        }
 |      HASH NEWLINE {
-               glcpp_parser_resolve_version(parser);
+               glcpp_parser_resolve_implicit_version(parser);
        }
 ;
 
@@ -399,87 +418,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;
@@ -518,7 +626,7 @@ replacement_list:
 junk:
        /* empty */
 |      pp_tokens {
-               glcpp_warning(&@1, parser, "extra tokens at end of directive");
+               glcpp_error(&@1, parser, "extra tokens at end of directive");
        }
 ;
 
@@ -926,14 +1034,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;
                }
 
@@ -1186,7 +1296,7 @@ static void add_builtin_define(glcpp_parser_t *parser,
 }
 
 glcpp_parser_t *
-glcpp_parser_create (const struct gl_extensions *extensions)
+glcpp_parser_create (const struct gl_extensions *extensions, gl_api api)
 {
        glcpp_parser_t *parser;
 
@@ -1196,8 +1306,9 @@ glcpp_parser_create (const struct gl_extensions *extensions)
        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->newline_as_space = 0;
        parser->in_control_line = 0;
        parser->paren_count = 0;
@@ -1215,6 +1326,7 @@ glcpp_parser_create (const struct gl_extensions *extensions)
        parser->error = 0;
 
         parser->extensions = extensions;
+        parser->api = api;
         parser->version_resolved = false;
 
        parser->has_new_line_number = 0;
@@ -1762,11 +1874,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");
@@ -2024,6 +2152,9 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
 {
        const struct gl_extensions *extensions = parser->extensions;
 
+       if (parser->version_resolved)
+               return;
+
        parser->version_resolved = true;
 
        add_builtin_define (parser, "__VERSION__", version);
@@ -2035,6 +2166,7 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
        /* 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);
 
           if (extensions != NULL) {
              if (extensions->OES_EGL_image_external)
@@ -2042,6 +2174,7 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
           }
        } 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);
 
@@ -2057,9 +2190,15 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
                 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);
 
@@ -2098,6 +2237,9 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
              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);
 
@@ -2112,6 +2254,12 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
 
              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);
           }
        }
 
@@ -2139,15 +2287,19 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
        }
 }
 
-/* GLSL version is no version is explicitly specified. */
+/* 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_version(glcpp_parser_t *parser)
+glcpp_parser_resolve_implicit_version(glcpp_parser_t *parser)
 {
-       if (parser->version_resolved)
-               return;
+       int language_version = parser->api == API_OPENGLES2 ?
+                              IMPLICIT_GLSL_ES_VERSION :
+                              IMPLICIT_GLSL_VERSION;
 
-       _glcpp_parser_handle_version_declaration(parser, IMPLICIT_GLSL_VERSION,
+       _glcpp_parser_handle_version_declaration(parser, language_version,
                                                 NULL, false);
 }