nir: move to compiler/
[mesa.git] / src / glsl / glcpp / glcpp-parse.y
index ccf8105597bab0e7eb6a6d4cb20f4fa02b54f368..ef1a6575aaa359a1a6bc0c3b334dbeb615bd3e4e 100644 (file)
@@ -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,
@@ -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 <HASH> and <DEFINE> 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 <ival> expression INTEGER operator SPACE integer_constant
-%type <str> IDENTIFIER FUNC_IDENTIFIER OBJ_IDENTIFIER INTEGER_STRING OTHER
+%type <ival> INTEGER operator SPACE integer_constant
+%type <expression_value> expression
+%type <str> IDENTIFIER FUNC_IDENTIFIER OBJ_IDENTIFIER INTEGER_STRING OTHER ERROR_TOKEN PRAGMA
 %type <string_list> identifier_list
-%type <token> preprocessing_token conditional_token
-%type <token_list> pp_tokens replacement_list text_line conditional_tokens
+%type <token> preprocessing_token
+%type <token_list> 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,35 +208,26 @@ input:
 ;
 
 line:
-       control_line {
-               ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "\n");
-       }
-|      HASH_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, $3);
-               }
-       }
+       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;
@@ -254,22 +262,49 @@ define:
 ;
 
 control_line:
-       HASH_DEFINE {
+       control_line_success {
+               ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "\n");
+       }
+|      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);
+               }
+       }
+;
+
+control_line_success:
+       HASH_TOKEN DEFINE_TOKEN {
                glcpp_parser_resolve_implicit_version(parser);
        } define
-|      HASH_UNDEF {
+|      HASH_TOKEN UNDEF {
                glcpp_parser_resolve_implicit_version(parser);
        } IDENTIFIER NEWLINE {
-               macro_t *macro = hash_table_find (parser->defines, $3);
+               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, $3);
+                       hash_table_remove (parser->defines, $4);
                        ralloc_free (macro);
                }
-               ralloc_free ($3);
+               ralloc_free ($4);
        }
-|      HASH_IF {
+|      HASH_TOKEN IF {
                glcpp_parser_resolve_implicit_version(parser);
-       } conditional_tokens NEWLINE {
+       } 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
@@ -281,7 +316,8 @@ control_line:
                    parser->skip_stack->type == SKIP_NO_SKIP)
                {
                        _glcpp_parser_expand_and_lex_from (parser,
-                                                          IF_EXPANDED, $3);
+                                                          IF_EXPANDED, $4,
+                                                          EXPANSION_MODE_EVALUATE_DEFINED);
                }       
                else
                {
@@ -289,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 ||
@@ -299,21 +335,21 @@ control_line:
                }       
                _glcpp_parser_skip_stack_push_if (parser, & @1, 0);
        }
-|      HASH_IFDEF {
+|      HASH_TOKEN IFDEF {
                glcpp_parser_resolve_implicit_version(parser);
        } IDENTIFIER junk NEWLINE {
-               macro_t *macro = hash_table_find (parser->defines, $3);
-               ralloc_free ($3);
+               macro_t *macro = hash_table_find (parser->defines, $4);
+               ralloc_free ($4);
                _glcpp_parser_skip_stack_push_if (parser, & @1, macro != NULL);
        }
-|      HASH_IFNDEF {
+|      HASH_TOKEN IFNDEF {
                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, & @2, macro == NULL);
+               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
@@ -325,7 +361,8 @@ 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)
@@ -338,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 &&
@@ -358,7 +395,7 @@ control_line:
                        glcpp_warning(& @1, parser, "ignoring illegal #elif without expression");
                }
        }
-|      HASH_ELSE {
+|      HASH_TOKEN ELSE { parser->lexing_directive = 1; } NEWLINE {
                if (parser->skip_stack &&
                    parser->skip_stack->has_else)
                {
@@ -370,25 +407,40 @@ control_line:
                        if (parser->skip_stack)
                                parser->skip_stack->has_else = true;
                }
-       } NEWLINE
-|      HASH_ENDIF {
+       }
+|      HASH_TOKEN ENDIF {
                _glcpp_parser_skip_stack_pop (parser, & @1);
        } NEWLINE
-|      HASH_VERSION integer_constant NEWLINE {
+|      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, $2, NULL, true);
+               _glcpp_parser_handle_version_declaration(parser, $3, NULL, true);
        }
-|      HASH_VERSION integer_constant IDENTIFIER NEWLINE {
+|      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, $2, $3, true);
+               _glcpp_parser_handle_version_declaration(parser, $3, $4, true);
        }
-|      HASH NEWLINE {
+|      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 #");
+       }
 ;
 
 integer_constant:
@@ -406,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;
@@ -511,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
@@ -525,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");
        }
 ;
 
@@ -579,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;
@@ -621,6 +735,8 @@ operator:
 |      ','                     { $$ = ','; }
 |      '='                     { $$ = '='; }
 |      PASTE                   { $$ = PASTE; }
+|      PLUS_PLUS               { $$ = PLUS_PLUS; }
+|      MINUS_MINUS             { $$ = MINUS_MINUS; }
 ;
 
 %%
@@ -676,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)
 {
@@ -933,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 && node_a->token->type == SPACE)
+                               node_a = node_a->next;
+                       while (node_b && node_b->token->type == SPACE)
+                               node_b = node_b->next;
                        continue;
                }
 
@@ -1020,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;
        }
 }
@@ -1203,14 +1347,18 @@ glcpp_parser_create (const struct gl_extensions *extensions, gl_api 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;
@@ -1315,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;
                        }
@@ -1356,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;
@@ -1372,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);
 }
@@ -1435,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;
@@ -1509,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 {
@@ -1549,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;
@@ -1561,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;
        }
 
@@ -1620,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.
@@ -1679,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;
@@ -1698,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)
@@ -1759,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);
 
@@ -1820,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);
 
@@ -1852,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);
@@ -1917,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;
                }
@@ -2014,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,13 +2375,21 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
        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);
+              if (extensions->ARB_texture_multisample)
+                 add_builtin_define(parser, "GL_OES_texture_storage_multisample_2d_array", 1);
+              if (extensions->ARB_blend_func_extended)
+                 add_builtin_define(parser, "GL_EXT_blend_func_extended", 1);
           }
        } else {
           add_builtin_define(parser, "GL_ARB_draw_buffers", 1);
+           add_builtin_define(parser, "GL_ARB_enhanced_layouts", 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);
@@ -2109,6 +2429,9 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
              if (extensions->ARB_shader_bit_encoding)
                 add_builtin_define(parser, "GL_ARB_shader_bit_encoding", 1);
 
+             if (extensions->ARB_shader_clock)
+                add_builtin_define(parser, "GL_ARB_shader_clock", 1);
+
              if (extensions->ARB_uniform_buffer_object)
                 add_builtin_define(parser, "GL_ARB_uniform_buffer_object", 1);
 
@@ -2130,6 +2453,12 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
              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);
 
@@ -2156,12 +2485,39 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
 
              if (extensions->ARB_shader_image_load_store)
                 add_builtin_define(parser, "GL_ARB_shader_image_load_store", 1);
+
+              if (extensions->ARB_shader_image_size)
+                 add_builtin_define(parser, "GL_ARB_shader_image_size", 1);
+
+              if (extensions->ARB_shader_texture_image_samples)
+                 add_builtin_define(parser, "GL_ARB_shader_texture_image_samples", 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->ARB_shader_storage_buffer_object)
+                add_builtin_define(parser, "GL_ARB_shader_storage_buffer_object", 1);
+
+             if (extensions->ARB_tessellation_shader)
+                add_builtin_define(parser, "GL_ARB_tessellation_shader", 1);
+
+              if (extensions->ARB_shader_subroutine)
+                 add_builtin_define(parser, "GL_ARB_shader_subroutine", 1);
+
+              if (extensions->ARB_shader_draw_parameters)
+                 add_builtin_define(parser, "GL_ARB_shader_draw_parameters", 1);
           }
        }
 
        if (extensions != NULL) {
           if (extensions->EXT_shader_integer_mix)
              add_builtin_define(parser, "GL_EXT_shader_integer_mix", 1);
+
+          if (extensions->EXT_shader_samples_identical)
+             add_builtin_define(parser, "GL_EXT_shader_samples_identical", 1);
        }
 
        if (version >= 150)