glcpp: Flag invalid pastes for integer followed by non-digits
[mesa.git] / src / glsl / glcpp / glcpp-parse.y
index 5b7467836d7258540fbf21462834a26a3847ed41..8f17d0d8ab3196c471238dae79ad026c001e9520 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <assert.h>
 #include <inttypes.h>
 
 #include "glcpp.h"
-#include "main/mtypes.h"
-
-#define glcpp_print(stream, str) stream = talloc_strdup_append(stream, str)
-#define glcpp_printf(stream, fmt, args...) \
-       stream = talloc_asprintf_append(stream, fmt, args)
+#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);
@@ -62,6 +60,9 @@ _string_list_contains (string_list_t *list, const char *member, int *index);
 static int
 _string_list_length (string_list_t *list);
 
+static int
+_string_list_equal (string_list_t *a, string_list_t *b);
+
 static argument_list_t *
 _argument_list_create (void *ctx);
 
@@ -74,7 +75,7 @@ _argument_list_length (argument_list_t *list);
 static token_list_t *
 _argument_list_member_at (argument_list_t *list, int index);
 
-/* Note: This function talloc_steal()s the str pointer. */
+/* Note: This function ralloc_steal()s the str pointer. */
 static token_t *
 _token_create_str (void *ctx, int type, char *str);
 
@@ -84,27 +85,35 @@ _token_create_ival (void *ctx, int type, int ival);
 static token_list_t *
 _token_list_create (void *ctx);
 
-/* Note: This function adds a talloc_reference() to token.
- *
- * You may want to talloc_unlink any current reference if you no
- * longer need it. */
 static void
 _token_list_append (token_list_t *list, token_t *token);
 
 static void
 _token_list_append_list (token_list_t *list, token_list_t *tail);
 
-static active_list_t *
-_active_list_push (active_list_t *list,
-                  const char *identifier,
-                  token_node_t *marker);
+static int
+_token_list_equal_ignoring_space (token_list_t *a, token_list_t *b);
 
-static active_list_t *
-_active_list_pop (active_list_t *list);
+static void
+_parser_active_list_push (glcpp_parser_t *parser,
+                         const char *identifier,
+                         token_node_t *marker);
 
-int
-_active_list_contains (active_list_t *list, const char *identifier);
+static void
+_parser_active_list_pop (glcpp_parser_t *parser);
 
+static int
+_parser_active_list_contains (glcpp_parser_t *parser, const char *identifier);
+
+/* 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);
+
+/* Perform macro expansion in-place on the given list. */
 static void
 _glcpp_parser_expand_token_list (glcpp_parser_t *parser,
                                 token_list_t *list);
@@ -124,27 +133,37 @@ _glcpp_parser_skip_stack_change_if (glcpp_parser_t *parser, YYLTYPE *loc,
 static void
 _glcpp_parser_skip_stack_pop (glcpp_parser_t *parser, YYLTYPE *loc);
 
-#define yylex glcpp_parser_lex
-
 static int
 glcpp_parser_lex (YYSTYPE *yylval, YYLTYPE *yylloc, glcpp_parser_t *parser);
 
 static void
 glcpp_parser_lex_from (glcpp_parser_t *parser, token_list_t *list);
 
+static void
+add_builtin_define(glcpp_parser_t *parser, const char *name, int value);
+
 %}
 
 %pure-parser
 %error-verbose
+
 %locations
+%initial-action {
+       @$.first_line = 1;
+       @$.first_column = 1;
+       @$.last_line = 1;
+       @$.last_column = 1;
+       @$.source = 0;
+}
 
 %parse-param {glcpp_parser_t *parser}
 %lex-param {glcpp_parser_t *parser}
 
-%token COMMA_FINAL DEFINED ELIF_EXPANDED HASH HASH_DEFINE_FUNC HASH_DEFINE_OBJ HASH_ELIF HASH_ELSE HASH_ENDIF HASH_IF HASH_IFDEF HASH_IFNDEF HASH_UNDEF IDENTIFIER IF_EXPANDED INTEGER INTEGER_STRING NEWLINE OTHER PLACEHOLDER SPACE
+%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
-%type <str> IDENTIFIER INTEGER_STRING OTHER
+%type <ival> expression INTEGER operator SPACE integer_constant
+%type <str> IDENTIFIER FUNC_IDENTIFIER OBJ_IDENTIFIER INTEGER_STRING OTHER
 %type <string_list> identifier_list
 %type <token> preprocessing_token conditional_token
 %type <token_list> pp_tokens replacement_list text_line conditional_tokens
@@ -169,12 +188,20 @@ input:
 
 line:
        control_line {
-               glcpp_print(parser->output, "\n");
+               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);
+               }
        }
 |      text_line {
                _glcpp_parser_print_expanded_token_list (parser, $1);
-               glcpp_print(parser->output, "\n");
-               talloc_free ($1);
+               ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "\n");
+               ralloc_free ($1);
        }
 |      expanded_line
 |      HASH non_directive
@@ -187,81 +214,150 @@ expanded_line:
 |      ELIF_EXPANDED expression NEWLINE {
                _glcpp_parser_skip_stack_change_if (parser, & @1, "elif", $2);
        }
+|      LINE_EXPANDED integer_constant NEWLINE {
+               parser->has_new_line_number = 1;
+               parser->new_line_number = $2;
+               ralloc_asprintf_rewrite_tail (&parser->output,
+                                             &parser->output_length,
+                                             "#line %" PRIiMAX "\n",
+                                             $2);
+       }
+|      LINE_EXPANDED integer_constant integer_constant NEWLINE {
+               parser->has_new_line_number = 1;
+               parser->new_line_number = $2;
+               parser->has_new_source_number = 1;
+               parser->new_source_number = $3;
+               ralloc_asprintf_rewrite_tail (&parser->output,
+                                             &parser->output_length,
+                                             "#line %" PRIiMAX " %" PRIiMAX "\n",
+                                             $2, $3);
+       }
 ;
 
 control_line:
-       HASH_DEFINE_OBJ IDENTIFIER replacement_list NEWLINE {
+       HASH_DEFINE OBJ_IDENTIFIER replacement_list NEWLINE {
                _define_object_macro (parser, & @2, $2, $3);
        }
-|      HASH_DEFINE_FUNC IDENTIFIER '(' ')' replacement_list NEWLINE {
+|      HASH_DEFINE FUNC_IDENTIFIER '(' ')' replacement_list NEWLINE {
                _define_function_macro (parser, & @2, $2, NULL, $5);
        }
-|      HASH_DEFINE_FUNC IDENTIFIER '(' identifier_list ')' replacement_list NEWLINE {
+|      HASH_DEFINE FUNC_IDENTIFIER '(' identifier_list ')' replacement_list NEWLINE {
                _define_function_macro (parser, & @2, $2, $4, $6);
        }
 |      HASH_UNDEF IDENTIFIER NEWLINE {
                macro_t *macro = hash_table_find (parser->defines, $2);
                if (macro) {
                        hash_table_remove (parser->defines, $2);
-                       talloc_free (macro);
+                       ralloc_free (macro);
                }
-               talloc_free ($2);
+               ralloc_free ($2);
        }
 |      HASH_IF conditional_tokens NEWLINE {
-               token_list_t *expanded;
-               token_t *token;
-
-               expanded = _token_list_create (parser);
-               token = _token_create_ival (parser, IF_EXPANDED, IF_EXPANDED);
-               _token_list_append (expanded, token);
-               talloc_unlink (parser, token);
-               _glcpp_parser_expand_token_list (parser, $2);
-               _token_list_append_list (expanded, $2);
-               glcpp_parser_lex_from (parser, expanded);
+               /* 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
+                * stack.
+                *
+                * This avoids generating diagnostics for invalid
+                * expressions that are being skipped. */
+               if (parser->skip_stack == NULL ||
+                   parser->skip_stack->type == SKIP_NO_SKIP)
+               {
+                       _glcpp_parser_expand_and_lex_from (parser,
+                                                          IF_EXPANDED, $2);
+               }       
+               else
+               {
+                       _glcpp_parser_skip_stack_push_if (parser, & @1, 0);
+                       parser->skip_stack->type = SKIP_TO_ENDIF;
+               }
+       }
+|      HASH_IF NEWLINE {
+               /* #if without an expression is only an error if we
+                *  are not skipping */
+               if (parser->skip_stack == NULL ||
+                   parser->skip_stack->type == SKIP_NO_SKIP)
+               {
+                       glcpp_error(& @1, parser, "#if with no expression");
+               }       
+               _glcpp_parser_skip_stack_push_if (parser, & @1, 0);
        }
 |      HASH_IFDEF IDENTIFIER junk NEWLINE {
                macro_t *macro = hash_table_find (parser->defines, $2);
-               talloc_free ($2);
+               ralloc_free ($2);
                _glcpp_parser_skip_stack_push_if (parser, & @1, macro != NULL);
        }
 |      HASH_IFNDEF IDENTIFIER junk NEWLINE {
                macro_t *macro = hash_table_find (parser->defines, $2);
-               talloc_free ($2);
+               ralloc_free ($2);
                _glcpp_parser_skip_stack_push_if (parser, & @1, macro == NULL);
        }
 |      HASH_ELIF conditional_tokens NEWLINE {
-               token_list_t *expanded;
-               token_t *token;
-
-               expanded = _token_list_create (parser);
-               token = _token_create_ival (parser, ELIF_EXPANDED, ELIF_EXPANDED);
-               _token_list_append (expanded, token);
-               talloc_unlink (parser, token);
-               _glcpp_parser_expand_token_list (parser, $2);
-               _token_list_append_list (expanded, $2);
-               glcpp_parser_lex_from (parser, expanded);
+               /* 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
+                * stack.
+                *
+                * This avoids generating diagnostics for invalid
+                * expressions that are being skipped. */
+               if (parser->skip_stack &&
+                   parser->skip_stack->type == SKIP_TO_ELSE)
+               {
+                       _glcpp_parser_expand_and_lex_from (parser,
+                                                          ELIF_EXPANDED, $2);
+               }
+               else
+               {
+                       _glcpp_parser_skip_stack_change_if (parser, & @1,
+                                                           "elif", 0);
+               }
        }
 |      HASH_ELIF NEWLINE {
-               /* #elif without an expression results in a warning if the
-                * condition doesn't matter (we just handled #if 1 or such)
-                * but an error otherwise. */
-               if (parser->skip_stack != NULL && parser->skip_stack->type == SKIP_NO_SKIP) {
-                       parser->skip_stack->type = SKIP_TO_ENDIF;
+               /* #elif without an expression is an error unless we
+                * are skipping. */
+               if (parser->skip_stack &&
+                   parser->skip_stack->type == SKIP_TO_ELSE)
+               {
+                       glcpp_error(& @1, parser, "#elif with no expression");
+               }
+               else
+               {
+                       _glcpp_parser_skip_stack_change_if (parser, & @1,
+                                                           "elif", 0);
                        glcpp_warning(& @1, parser, "ignoring illegal #elif without expression");
-               } else {
-                       glcpp_error(& @1, parser, "#elif needs an expression");
                }
        }
-|      HASH_ELSE NEWLINE {
+|      HASH_ELSE {
                _glcpp_parser_skip_stack_change_if (parser, & @1, "else", 1);
-       }
-|      HASH_ENDIF NEWLINE {
+       } NEWLINE
+|      HASH_ENDIF {
                _glcpp_parser_skip_stack_pop (parser, & @1);
+       } NEWLINE
+|      HASH_VERSION integer_constant NEWLINE {
+               macro_t *macro = hash_table_find (parser->defines, "__VERSION__");
+               if (macro) {
+                       hash_table_remove (parser->defines, "__VERSION__");
+                       ralloc_free (macro);
+               }
+               add_builtin_define (parser, "__VERSION__", $2);
+
+               if ($2 == 100)
+                       add_builtin_define (parser, "GL_ES", 1);
+
+               /* Currently, all ES2 implementations support highp in the
+                * fragment shader, so we always define this macro in ES2.
+                * If we ever get a driver that doesn't support highp, we'll
+                * need to add a flag to the gl_context and check that here.
+                */
+               if ($2 >= 130 || $2 == 100)
+                       add_builtin_define (parser, "GL_FRAGMENT_PRECISION_HIGH", 1);
+
+               ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "#version %" PRIiMAX, $2);
        }
 |      HASH NEWLINE
 ;
 
-expression:
+integer_constant:
        INTEGER_STRING {
                if (strlen ($1) >= 3 && strncmp ($1, "0x", 2) == 0) {
                        $$ = strtoll ($1 + 2, NULL, 16);
@@ -274,6 +370,12 @@ expression:
 |      INTEGER {
                $$ = $1;
        }
+
+expression:
+       integer_constant
+|      IDENTIFIER {
+               $$ = 0;
+       }
 |      expression OR expression {
                $$ = $1 || $3;
        }
@@ -320,10 +422,20 @@ expression:
                $$ = $1 + $3;
        }
 |      expression '%' expression {
-               $$ = $1 % $3;
+               if ($3 == 0) {
+                       yyerror (& @1, parser,
+                                "zero modulus in preprocessor directive");
+               } else {
+                       $$ = $1 % $3;
+               }
        }
 |      expression '/' expression {
-               $$ = $1 / $3;
+               if ($3 == 0) {
+                       yyerror (& @1, parser,
+                                "division by 0 in preprocessor directive");
+               } else {
+                       $$ = $1 / $3;
+               }
        }
 |      expression '*' expression {
                $$ = $1 * $3;
@@ -349,12 +461,12 @@ identifier_list:
        IDENTIFIER {
                $$ = _string_list_create (parser);
                _string_list_append_item ($$, $1);
-               talloc_steal ($$, $1);
+               ralloc_steal ($$, $1);
        }
 |      identifier_list ',' IDENTIFIER {
                $$ = $1;        
                _string_list_append_item ($$, $3);
-               talloc_steal ($$, $3);
+               ralloc_steal ($$, $3);
        }
 ;
 
@@ -397,15 +509,12 @@ conditional_token:
 conditional_tokens:
        /* Exactly the same as pp_tokens, but using conditional_token */
        conditional_token {
-               parser->space_tokens = 1;
                $$ = _token_list_create (parser);
                _token_list_append ($$, $1);
-               talloc_unlink (parser, $1);
        }
 |      conditional_tokens conditional_token {
                $$ = $1;
                _token_list_append ($$, $2);
-               talloc_unlink (parser, $2);
        }
 ;
 
@@ -414,12 +523,10 @@ pp_tokens:
                parser->space_tokens = 1;
                $$ = _token_list_create (parser);
                _token_list_append ($$, $1);
-               talloc_unlink (parser, $1);
        }
 |      pp_tokens preprocessing_token {
                $$ = $1;
                _token_list_append ($$, $2);
-               talloc_unlink (parser, $2);
        }
 ;
 
@@ -478,7 +585,6 @@ operator:
 |      ','                     { $$ = ','; }
 |      '='                     { $$ = '='; }
 |      PASTE                   { $$ = PASTE; }
-|      DEFINED                 { $$ = DEFINED; }
 ;
 
 %%
@@ -488,7 +594,7 @@ _string_list_create (void *ctx)
 {
        string_list_t *list;
 
-       list = xtalloc (ctx, string_list_t);
+       list = ralloc (ctx, string_list_t);
        list->head = NULL;
        list->tail = NULL;
 
@@ -500,8 +606,8 @@ _string_list_append_item (string_list_t *list, const char *str)
 {
        string_node_t *node;
 
-       node = xtalloc (list, string_node_t);
-       node->str = xtalloc_strdup (node, str);
+       node = ralloc (list, string_node_t);
+       node->str = ralloc_strdup (node, str);
 
        node->next = NULL;
 
@@ -549,12 +655,37 @@ _string_list_length (string_list_t *list)
        return length;
 }
 
+int
+_string_list_equal (string_list_t *a, string_list_t *b)
+{
+       string_node_t *node_a, *node_b;
+
+       if (a == NULL && b == NULL)
+               return 1;
+
+       if (a == NULL || b == NULL)
+               return 0;
+
+       for (node_a = a->head, node_b = b->head;
+            node_a && node_b;
+            node_a = node_a->next, node_b = node_b->next)
+       {
+               if (strcmp (node_a->str, node_b->str))
+                       return 0;
+       }
+
+       /* Catch the case of lists being different lengths, (which
+        * would cause the loop above to terminate after the shorter
+        * list). */
+       return node_a == node_b;
+}
+
 argument_list_t *
 _argument_list_create (void *ctx)
 {
        argument_list_t *list;
 
-       list = xtalloc (ctx, argument_list_t);
+       list = ralloc (ctx, argument_list_t);
        list->head = NULL;
        list->tail = NULL;
 
@@ -566,7 +697,7 @@ _argument_list_append (argument_list_t *list, token_list_t *argument)
 {
        argument_node_t *node;
 
-       node = xtalloc (list, argument_node_t);
+       node = ralloc (list, argument_node_t);
        node->argument = argument;
 
        node->next = NULL;
@@ -617,15 +748,17 @@ _argument_list_member_at (argument_list_t *list, int index)
        return NULL;
 }
 
-/* Note: This function talloc_steal()s the str pointer. */
+/* Note: This function ralloc_steal()s the str pointer. */
 token_t *
 _token_create_str (void *ctx, int type, char *str)
 {
        token_t *token;
 
-       token = xtalloc (ctx, token_t);
+       token = ralloc (ctx, token_t);
        token->type = type;
-       token->value.str = talloc_steal (token, str);
+       token->value.str = str;
+
+       ralloc_steal (token, str);
 
        return token;
 }
@@ -635,7 +768,7 @@ _token_create_ival (void *ctx, int type, int ival)
 {
        token_t *token;
 
-       token = xtalloc (ctx, token_t);
+       token = ralloc (ctx, token_t);
        token->type = type;
        token->value.ival = ival;
 
@@ -647,7 +780,7 @@ _token_list_create (void *ctx)
 {
        token_list_t *list;
 
-       list = xtalloc (ctx, token_list_t);
+       list = ralloc (ctx, token_list_t);
        list->head = NULL;
        list->tail = NULL;
        list->non_space_tail = NULL;
@@ -660,9 +793,8 @@ _token_list_append (token_list_t *list, token_t *token)
 {
        token_node_t *node;
 
-       node = xtalloc (list, token_node_t);
-       node->token = xtalloc_reference (list, token);
-
+       node = ralloc (list, token_node_t);
+       node->token = token;
        node->next = NULL;
 
        if (list->head == NULL) {
@@ -702,8 +834,11 @@ _token_list_copy (void *ctx, token_list_t *other)
                return NULL;
 
        copy = _token_list_create (ctx);
-       for (node = other->head; node; node = node->next)
-               _token_list_append (copy, node->token);
+       for (node = other->head; node; node = node->next) {
+               token_t *new_token = ralloc (copy, token_t);
+               *new_token = *node->token;
+               _token_list_append (copy, new_token);
+       }
 
        return copy;
 }
@@ -720,61 +855,137 @@ _token_list_trim_trailing_space (token_list_t *list)
 
                while (tail) {
                        next = tail->next;
-                       talloc_free (tail);
+                       ralloc_free (tail);
                        tail = next;
                }
        }
 }
 
+static int
+_token_list_is_empty_ignoring_space (token_list_t *l)
+{
+       token_node_t *n;
+
+       if (l == NULL)
+               return 1;
+
+       n = l->head;
+       while (n != NULL && n->token->type == SPACE)
+               n = n->next;
+
+       return n == NULL;
+}
+
+int
+_token_list_equal_ignoring_space (token_list_t *a, token_list_t *b)
+{
+       token_node_t *node_a, *node_b;
+
+       if (a == NULL || b == NULL) {
+               int a_empty = _token_list_is_empty_ignoring_space(a);
+               int b_empty = _token_list_is_empty_ignoring_space(b);
+               return a_empty == b_empty;
+       }
+
+       node_a = a->head;
+       node_b = b->head;
+
+       while (1)
+       {
+               if (node_a == NULL && node_b == NULL)
+                       break;
+
+               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;
+                       continue;
+               }
+
+               if (node_a->token->type != node_b->token->type)
+                       return 0;
+
+               switch (node_a->token->type) {
+               case INTEGER:
+                       if (node_a->token->value.ival != 
+                           node_b->token->value.ival)
+                       {
+                               return 0;
+                       }
+                       break;
+               case IDENTIFIER:
+               case INTEGER_STRING:
+               case OTHER:
+                       if (strcmp (node_a->token->value.str,
+                                   node_b->token->value.str))
+                       {
+                               return 0;
+                       }
+                       break;
+               }
+
+               node_a = node_a->next;
+               node_b = node_b->next;
+       }
+
+       return 1;
+}
+
 static void
-_token_print (char **out, token_t *token)
+_token_print (char **out, size_t *len, token_t *token)
 {
        if (token->type < 256) {
-               glcpp_printf (*out, "%c", token->type);
+               ralloc_asprintf_rewrite_tail (out, len, "%c", token->type);
                return;
        }
 
        switch (token->type) {
        case INTEGER:
-               glcpp_printf (*out, "%" PRIxMAX, token->value.ival);
+               ralloc_asprintf_rewrite_tail (out, len, "%" PRIiMAX, token->value.ival);
                break;
        case IDENTIFIER:
        case INTEGER_STRING:
        case OTHER:
-               glcpp_print (*out, token->value.str);
+               ralloc_asprintf_rewrite_tail (out, len, "%s", token->value.str);
                break;
        case SPACE:
-               glcpp_print (*out, " ");
+               ralloc_asprintf_rewrite_tail (out, len, " ");
                break;
        case LEFT_SHIFT:
-               glcpp_print (*out, "<<");
+               ralloc_asprintf_rewrite_tail (out, len, "<<");
                break;
        case RIGHT_SHIFT:
-               glcpp_print (*out, ">>");
+               ralloc_asprintf_rewrite_tail (out, len, ">>");
                break;
        case LESS_OR_EQUAL:
-               glcpp_print (*out, "<=");
+               ralloc_asprintf_rewrite_tail (out, len, "<=");
                break;
        case GREATER_OR_EQUAL:
-               glcpp_print (*out, ">=");
+               ralloc_asprintf_rewrite_tail (out, len, ">=");
                break;
        case EQUAL:
-               glcpp_print (*out, "==");
+               ralloc_asprintf_rewrite_tail (out, len, "==");
                break;
        case NOT_EQUAL:
-               glcpp_print (*out, "!=");
+               ralloc_asprintf_rewrite_tail (out, len, "!=");
                break;
        case AND:
-               glcpp_print (*out, "&&");
+               ralloc_asprintf_rewrite_tail (out, len, "&&");
                break;
        case OR:
-               glcpp_print (*out, "||");
+               ralloc_asprintf_rewrite_tail (out, len, "||");
                break;
        case PASTE:
-               glcpp_print (*out, "##");
+               ralloc_asprintf_rewrite_tail (out, len, "##");
                break;
        case COMMA_FINAL:
-               glcpp_print (*out, ",");
+               ralloc_asprintf_rewrite_tail (out, len, ",");
                break;
        case PLACEHOLDER:
                /* Nothing to print. */
@@ -785,7 +996,7 @@ _token_print (char **out, token_t *token)
        }
 }
 
-/* Return a new token (talloc()ed off of 'token') formed by pasting
+/* Return a new token (ralloc()ed off of 'token') formed by pasting
  * 'token' and 'other'. Note that this function may return 'token' or
  * 'other' directly rather than allocating anything new.
  *
@@ -846,29 +1057,37 @@ _token_paste (glcpp_parser_t *parser, token_t *token, token_t *other)
        /* Two string-valued tokens can usually just be mashed
         * together.
         *
-        * XXX: This isn't actually legitimate. Several things here
-        * should result in a diagnostic since the result cannot be a
-        * valid, single pre-processing token. For example, pasting
-        * "123" and "abc" is not legal, but we don't catch that
-        * here. */
+        * There are some exceptions here. Notably, if the first token
+        * is a string representing an integer, then the second token
+        * must also be a an integer and must begin with a digit.
+        */
        if ((token->type == IDENTIFIER || token->type == OTHER || token->type == INTEGER_STRING) &&
            (other->type == IDENTIFIER || other->type == OTHER || other->type == INTEGER_STRING))
        {
                char *str;
 
-               str = xtalloc_asprintf (token, "%s%s",
-                                       token->value.str, other->value.str);
+               if (token->type == INTEGER_STRING) {
+                       if (other->type != INTEGER_STRING)
+                               goto FAIL;
+                       if (other->value.str[0] < '0' ||
+                           other->value.str[0] > '9')
+                               goto FAIL;
+               }
+
+               str = ralloc_asprintf (token, "%s%s", token->value.str,
+                                      other->value.str);
                combined = _token_create_str (token, token->type, str);
                combined->location = token->location;
                return combined;
        }
 
+    FAIL:
        glcpp_error (&token->location, parser, "");
-       glcpp_print (parser->info_log, "Pasting \"");
-       _token_print (&parser->info_log, token);
-       glcpp_print (parser->info_log, "\" and \"");
-       _token_print (&parser->info_log, other);
-       glcpp_print (parser->info_log, "\" does not give a valid preprocessing token.\n");
+       ralloc_asprintf_rewrite_tail (&parser->info_log, &parser->info_log_length, "Pasting \"");
+       _token_print (&parser->info_log, &parser->info_log_length, token);
+       ralloc_asprintf_rewrite_tail (&parser->info_log, &parser->info_log_length, "\" and \"");
+       _token_print (&parser->info_log, &parser->info_log_length, other);
+       ralloc_asprintf_rewrite_tail (&parser->info_log, &parser->info_log_length, "\" does not give a valid preprocessing token.\n");
 
        return token;
 }
@@ -882,7 +1101,7 @@ _token_list_print (glcpp_parser_t *parser, token_list_t *list)
                return;
 
        for (node = list->head; node; node = node->next)
-               _token_print (&parser->output, node->token);
+               _token_print (&parser->output, &parser->output_length, node->token);
 }
 
 void
@@ -891,14 +1110,26 @@ yyerror (YYLTYPE *locp, glcpp_parser_t *parser, const char *error)
        glcpp_error(locp, parser, "%s", error);
 }
 
+static void add_builtin_define(glcpp_parser_t *parser,
+                              const char *name, int value)
+{
+   token_t *tok;
+   token_list_t *list;
+
+   tok = _token_create_ival (parser, INTEGER, value);
+
+   list = _token_list_create(parser);
+   _token_list_append(list, tok);
+   _define_object_macro(parser, NULL, name, list);
+}
+
 glcpp_parser_t *
-glcpp_parser_create (const struct gl_extensions *extensions)
+glcpp_parser_create (const struct gl_extensions *extensions, int api)
 {
        glcpp_parser_t *parser;
-       token_t *tok;
-       token_list_t *list;
+       int language_version;
 
-       parser = xtalloc (NULL, glcpp_parser_t);
+       parser = ralloc (NULL, glcpp_parser_t);
 
        glcpp_lex_init_extra (parser, &parser->scanner);
        parser->defines = hash_table_ctor (32, hash_table_string_hash,
@@ -915,47 +1146,75 @@ glcpp_parser_create (const struct gl_extensions *extensions)
        parser->lex_from_list = NULL;
        parser->lex_from_node = NULL;
 
-       parser->output = talloc_strdup(parser, "");
-       parser->info_log = talloc_strdup(parser, "");
+       parser->output = ralloc_strdup(parser, "");
+       parser->output_length = 0;
+       parser->info_log = ralloc_strdup(parser, "");
+       parser->info_log_length = 0;
        parser->error = 0;
 
+       parser->has_new_line_number = 0;
+       parser->new_line_number = 1;
+       parser->has_new_source_number = 0;
+       parser->new_source_number = 0;
+
        /* Add pre-defined macros. */
-       tok = _token_create_ival (parser, INTEGER, 1);
+       if (extensions != NULL) {
+          if (extensions->OES_EGL_image_external)
+             add_builtin_define(parser, "GL_OES_EGL_image_external", 1);
+       }
+
+       if (api == API_OPENGLES2)
+               add_builtin_define(parser, "GL_ES", 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);
 
-       list = _token_list_create(parser);
-       _token_list_append(list, tok);
-       _define_object_macro(parser, NULL, "GL_ARB_draw_buffers", list);
+             if (extensions->ARB_draw_instanced)
+                add_builtin_define(parser, "GL_ARB_draw_instanced", 1);
 
-       list = _token_list_create(parser);
-       _token_list_append(list, tok);
-       _define_object_macro(parser, NULL, "GL_ARB_texture_rectangle", list);
+             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 != NULL) && extensions->EXT_texture_array) {
-               list = _token_list_create(parser);
-               _token_list_append(list, tok);
-               _define_object_macro(parser, NULL,
-                                    "GL_EXT_texture_array", list);
+             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);
+          }
        }
 
-       talloc_unlink(parser, tok);
+       language_version = 110;
+       add_builtin_define(parser, "__VERSION__", language_version);
 
        return parser;
 }
 
-int
-glcpp_parser_parse (glcpp_parser_t *parser)
-{
-       return yyparse (parser);
-}
-
 void
 glcpp_parser_destroy (glcpp_parser_t *parser)
 {
-       if (parser->skip_stack)
-               glcpp_error (&parser->skip_stack->loc, parser, "Unterminated #if\n");
        glcpp_lex_destroy (parser->scanner);
        hash_table_dtor (parser->defines);
-       talloc_free (parser);
+       ralloc_free (parser);
 }
 
 typedef enum function_status
@@ -1049,18 +1308,93 @@ _arguments_parse (argument_list_t *arguments,
 }
 
 static token_list_t *
-_token_list_create_with_one_space (void *ctx)
+_token_list_create_with_one_ival (void *ctx, int type, int ival)
 {
        token_list_t *list;
-       token_t *space;
+       token_t *node;
 
        list = _token_list_create (ctx);
-       space = _token_create_ival (list, SPACE, SPACE);
-       _token_list_append (list, space);
+       node = _token_create_ival (list, type, ival);
+       _token_list_append (list, node);
 
        return list;
 }
 
+static token_list_t *
+_token_list_create_with_one_space (void *ctx)
+{
+       return _token_list_create_with_one_ival (ctx, SPACE, SPACE);
+}
+
+static token_list_t *
+_token_list_create_with_one_integer (void *ctx, int ival)
+{
+       return _token_list_create_with_one_ival (ctx, INTEGER, ival);
+}
+
+/* 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).
+ */
+static void
+_glcpp_parser_expand_and_lex_from (glcpp_parser_t *parser,
+                                  int head_token_type,
+                                  token_list_t *list)
+{
+       token_list_t *expanded;
+       token_t *token;
+
+       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);
+       _token_list_append_list (expanded, list);
+       glcpp_parser_lex_from (parser, expanded);
+}
+
+static void
+_glcpp_parser_apply_pastes (glcpp_parser_t *parser, token_list_t *list)
+{
+       token_node_t *node;
+
+       node = list->head;
+       while (node)
+       {
+               token_node_t *next_non_space;
+
+               /* Look ahead for a PASTE token, skipping space. */
+               next_non_space = node->next;
+               while (next_non_space && next_non_space->token->type == SPACE)
+                       next_non_space = next_non_space->next;
+
+               if (next_non_space == NULL)
+                       break;
+
+               if (next_non_space->token->type != PASTE) {
+                       node = next_non_space;
+                       continue;
+               }
+
+               /* Now find the next non-space token after the PASTE. */
+               next_non_space = next_non_space->next;
+               while (next_non_space && next_non_space->token->type == SPACE)
+                       next_non_space = next_non_space->next;
+
+               if (next_non_space == NULL) {
+                       yyerror (&node->token->location, parser, "'##' cannot appear at either end of a macro expansion\n");
+                       return;
+               }
+
+               node->token = _token_paste (parser, node->token, next_non_space->token);
+               node->next = next_non_space->next;
+               if (next_non_space == list->tail)
+                       list->tail = node;
+       }
+
+       list->non_space_tail = list->tail;
+}
+
 /* This is a helper function that's essentially part of the
  * implementation of _glcpp_parser_expand_node. It shouldn't be called
  * except for by that function.
@@ -1112,7 +1446,7 @@ _glcpp_parser_expand_function (glcpp_parser_t *parser,
 
        /* Replace a macro defined as empty with a SPACE token. */
        if (macro->replacements == NULL) {
-               talloc_free (arguments);
+               ralloc_free (arguments);
                return _token_list_create_with_one_space (parser);
        }
 
@@ -1172,43 +1506,7 @@ _glcpp_parser_expand_function (glcpp_parser_t *parser,
 
        _token_list_trim_trailing_space (substituted);
 
-       node = substituted->head;
-       while (node)
-       {
-               token_node_t *next_non_space;
-
-               /* Look ahead for a PASTE token, skipping space. */
-               next_non_space = node->next;
-               while (next_non_space && next_non_space->token->type == SPACE)
-                       next_non_space = next_non_space->next;
-
-               if (next_non_space == NULL)
-                       break;
-
-               if (next_non_space->token->type != PASTE) {
-                       node = next_non_space;
-                       continue;
-               }
-
-               /* Now find the next non-space token after the PASTE. */
-               next_non_space = next_non_space->next;
-               while (next_non_space && next_non_space->token->type == SPACE)
-                       next_non_space = next_non_space->next;
-
-               if (next_non_space == NULL) {
-                       yyerror (&node->token->location, parser, "'##' cannot appear at either end of a macro expansion\n");
-                       return NULL;
-               }
-
-               node->token = _token_paste (parser, node->token, next_non_space->token);
-               node->next = next_non_space->next;
-               if (next_non_space == substituted->tail)
-                       substituted->tail = node;
-
-               node = node->next;
-       }
-
-       substituted->non_space_tail = substituted->tail;
+       _glcpp_parser_apply_pastes (parser, substituted);
 
        return substituted;
 }
@@ -1250,8 +1548,18 @@ _glcpp_parser_expand_node (glcpp_parser_t *parser,
                return NULL;
        }
 
-       /* Look up this identifier in the hash table. */
+       *last = node;
        identifier = token->value.str;
+
+       /* Special handling for __LINE__ and __FILE__, (not through
+        * the hash table). */
+       if (strcmp(identifier, "__LINE__") == 0)
+               return _token_list_create_with_one_integer (parser, node->token->location.first_line);
+
+       if (strcmp(identifier, "__FILE__") == 0)
+               return _token_list_create_with_one_integer (parser, node->token->location.source);
+
+       /* Look up this identifier in the hash table. */
        macro = hash_table_find (parser->defines, identifier);
 
        /* Not a macro, so no expansion needed. */
@@ -1260,7 +1568,7 @@ _glcpp_parser_expand_node (glcpp_parser_t *parser,
 
        /* Finally, don't expand this macro if we're already actively
         * expanding it, (to avoid infinite recursion). */
-       if (_active_list_contains (parser->active, identifier)) {
+       if (_parser_active_list_contains (parser, identifier)) {
                /* We change the token type here from IDENTIFIER to
                 * OTHER to prevent any future expansion of this
                 * unexpanded token. */
@@ -1268,73 +1576,76 @@ _glcpp_parser_expand_node (glcpp_parser_t *parser,
                token_list_t *expansion;
                token_t *final;
 
-               str = xtalloc_strdup (parser, token->value.str);
+               str = ralloc_strdup (parser, token->value.str);
                final = _token_create_str (parser, OTHER, str);
                expansion = _token_list_create (parser);
                _token_list_append (expansion, final);
-               *last = node;
                return expansion;
        }
 
        if (! macro->is_function)
        {
-               *last = node;
+               token_list_t *replacement;
 
                /* Replace a macro defined as empty with a SPACE token. */
                if (macro->replacements == NULL)
                        return _token_list_create_with_one_space (parser);
 
-               return _token_list_copy (parser, macro->replacements);
+               replacement = _token_list_copy (parser, macro->replacements);
+               _glcpp_parser_apply_pastes (parser, replacement);
+               return replacement;
        }
 
        return _glcpp_parser_expand_function (parser, node, last);
 }
 
-/* Push a new identifier onto the active list, returning the new list.
+/* Push a new identifier onto the parser's active list.
  *
  * Here, 'marker' is the token node that appears in the list after the
  * expansion of 'identifier'. That is, when the list iterator begins
- * examinging 'marker', then it is time to pop this node from the
+ * examining 'marker', then it is time to pop this node from the
  * active stack.
  */
-active_list_t *
-_active_list_push (active_list_t *list,
-                  const char *identifier,
-                  token_node_t *marker)
+static void
+_parser_active_list_push (glcpp_parser_t *parser,
+                         const char *identifier,
+                         token_node_t *marker)
 {
        active_list_t *node;
 
-       node = xtalloc (list, active_list_t);
-       node->identifier = xtalloc_strdup (node, identifier);
+       node = ralloc (parser->active, active_list_t);
+       node->identifier = ralloc_strdup (node, identifier);
        node->marker = marker;
-       node->next = list;
+       node->next = parser->active;
 
-       return node;
+       parser->active = node;
 }
 
-active_list_t *
-_active_list_pop (active_list_t *list)
+static void
+_parser_active_list_pop (glcpp_parser_t *parser)
 {
-       active_list_t *node = list;
+       active_list_t *node = parser->active;
 
-       if (node == NULL)
-               return NULL;
+       if (node == NULL) {
+               parser->active = NULL;
+               return;
+       }
 
-       node = list->next;
-       talloc_free (list);
+       node = parser->active->next;
+       ralloc_free (parser->active);
 
-       return node;
+       parser->active = node;
 }
 
-int
-_active_list_contains (active_list_t *list, const char *identifier)
+static int
+_parser_active_list_contains (glcpp_parser_t *parser, const char *identifier)
 {
        active_list_t *node;
 
-       if (list == NULL)
+       if (parser->active == NULL)
                return 0;
 
-       for (node = list; node; node = node->next)
+       for (node = parser->active; node; node = node->next)
                if (strcmp (node->identifier, identifier) == 0)
                        return 1;
 
@@ -1353,6 +1664,7 @@ _glcpp_parser_expand_token_list (glcpp_parser_t *parser,
        token_node_t *node_prev;
        token_node_t *node, *last = NULL;
        token_list_t *expansion;
+       active_list_t *active_initial = parser->active;
 
        if (list == NULL)
                return;
@@ -1365,10 +1677,8 @@ _glcpp_parser_expand_token_list (glcpp_parser_t *parser,
        while (node) {
 
                while (parser->active && parser->active->marker == node)
-                       parser->active = _active_list_pop (parser->active);
+                       _parser_active_list_pop (parser);
 
-               /* Find the expansion for node, which will replace all
-                * nodes from node to last, inclusive. */
                expansion = _glcpp_parser_expand_node (parser, node, &last);
                if (expansion) {
                        token_node_t *n;
@@ -1377,12 +1687,12 @@ _glcpp_parser_expand_token_list (glcpp_parser_t *parser,
                                while (parser->active &&
                                       parser->active->marker == n)
                                {
-                                       parser->active = _active_list_pop (parser->active);
+                                       _parser_active_list_pop (parser);
                                }
 
-                       parser->active = _active_list_push (parser->active,
-                                                           node->token->value.str,
-                                                           last->next);
+                       _parser_active_list_push (parser,
+                                                 node->token->value.str,
+                                                 last->next);
                        
                        /* Splice expansion into list, supporting a
                         * simple deletion if the expansion is
@@ -1409,8 +1719,11 @@ _glcpp_parser_expand_token_list (glcpp_parser_t *parser,
                node = node_prev ? node_prev->next : list->head;
        }
 
-       while (parser->active)
-               parser->active = _active_list_pop (parser->active);
+       /* Remove any lingering effects of this invocation on the
+        * active list. That is, pop until the list looks like it did
+        * at the beginning of this function. */
+       while (parser->active && parser->active != active_initial)
+               _parser_active_list_pop (parser);
 
        list->non_space_tail = list->tail;
 }
@@ -1436,31 +1749,57 @@ _check_for_reserved_macro_name (glcpp_parser_t *parser, YYLTYPE *loc,
        /* According to the GLSL specification, macro names starting with "__"
         * or "GL_" are reserved for future use.  So, don't allow them.
         */
-       if (strncmp(identifier, "__", 2) == 0) {
-               glcpp_error (loc, parser, "Macro names starting with \"__\" are reserved.\n");
+       if (strstr(identifier, "__")) {
+               glcpp_error (loc, parser, "Macro names containing \"__\" are reserved.\n");
        }
        if (strncmp(identifier, "GL_", 3) == 0) {
                glcpp_error (loc, parser, "Macro names starting with \"GL_\" are reserved.\n");
        }
 }
 
+static int
+_macro_equal (macro_t *a, macro_t *b)
+{
+       if (a->is_function != b->is_function)
+               return 0;
+
+       if (a->is_function) {
+               if (! _string_list_equal (a->parameters, b->parameters))
+                       return 0;
+       }
+
+       return _token_list_equal_ignoring_space (a->replacements,
+                                                b->replacements);
+}
+
 void
 _define_object_macro (glcpp_parser_t *parser,
                      YYLTYPE *loc,
                      const char *identifier,
                      token_list_t *replacements)
 {
-       macro_t *macro;
+       macro_t *macro, *previous;
 
        if (loc != NULL)
                _check_for_reserved_macro_name(parser, loc, identifier);
 
-       macro = xtalloc (parser, macro_t);
+       macro = ralloc (parser, macro_t);
 
        macro->is_function = 0;
        macro->parameters = NULL;
-       macro->identifier = talloc_strdup (macro, identifier);
-       macro->replacements = talloc_steal (macro, replacements);
+       macro->identifier = ralloc_strdup (macro, identifier);
+       macro->replacements = replacements;
+       ralloc_steal (macro, replacements);
+
+       previous = hash_table_find (parser->defines, identifier);
+       if (previous) {
+               if (_macro_equal (macro, previous)) {
+                       ralloc_free (macro);
+                       return;
+               }
+               glcpp_error (loc, parser, "Redefinition of macro %s\n",
+                            identifier);
+       }
 
        hash_table_insert (parser->defines, macro, identifier);
 }
@@ -1472,16 +1811,27 @@ _define_function_macro (glcpp_parser_t *parser,
                        string_list_t *parameters,
                        token_list_t *replacements)
 {
-       macro_t *macro;
+       macro_t *macro, *previous;
 
        _check_for_reserved_macro_name(parser, loc, identifier);
 
-       macro = xtalloc (parser, macro_t);
+       macro = ralloc (parser, macro_t);
+       ralloc_steal (macro, parameters);
+       ralloc_steal (macro, replacements);
 
        macro->is_function = 1;
-       macro->parameters = talloc_steal (macro, parameters);
-       macro->identifier = talloc_strdup (macro, identifier);
-       macro->replacements = talloc_steal (macro, replacements);
+       macro->parameters = parameters;
+       macro->identifier = ralloc_strdup (macro, identifier);
+       macro->replacements = replacements;
+       previous = hash_table_find (parser->defines, identifier);
+       if (previous) {
+               if (_macro_equal (macro, previous)) {
+                       ralloc_free (macro);
+                       return;
+               }
+               glcpp_error (loc, parser, "Redefinition of macro %s\n",
+                            identifier);
+       }
 
        hash_table_insert (parser->defines, macro, identifier);
 }
@@ -1527,7 +1877,7 @@ glcpp_parser_lex (YYSTYPE *yylval, YYLTYPE *yylloc, glcpp_parser_t *parser)
                        if (ret == NEWLINE)
                                parser->in_control_line = 0;
                }
-               else if (ret == HASH_DEFINE_OBJ || ret == HASH_DEFINE_FUNC ||
+               else if (ret == HASH_DEFINE ||
                           ret == HASH_UNDEF || ret == HASH_IF ||
                           ret == HASH_IFDEF || ret == HASH_IFNDEF ||
                           ret == HASH_ELIF || ret == HASH_ELSE ||
@@ -1552,7 +1902,7 @@ glcpp_parser_lex (YYSTYPE *yylval, YYLTYPE *yylloc, glcpp_parser_t *parser)
        node = parser->lex_from_node;
 
        if (node == NULL) {
-               talloc_free (parser->lex_from_list);
+               ralloc_free (parser->lex_from_list);
                parser->lex_from_list = NULL;
                return NEWLINE;
        }
@@ -1581,13 +1931,13 @@ glcpp_parser_lex_from (glcpp_parser_t *parser, token_list_t *list)
                _token_list_append (parser->lex_from_list, node->token);
        }
 
-       talloc_free (list);
+       ralloc_free (list);
 
        parser->lex_from_node = parser->lex_from_list->head;
 
        /* It's possible the list consisted of nothing but whitespace. */
        if (parser->lex_from_node == NULL) {
-               talloc_free (parser->lex_from_list);
+               ralloc_free (parser->lex_from_list);
                parser->lex_from_list = NULL;
        }
 }
@@ -1602,7 +1952,7 @@ _glcpp_parser_skip_stack_push_if (glcpp_parser_t *parser, YYLTYPE *loc,
        if (parser->skip_stack)
                current = parser->skip_stack->type;
 
-       node = xtalloc (parser, skip_node_t);
+       node = ralloc (parser, skip_node_t);
        node->loc = *loc;
 
        if (current == SKIP_NO_SKIP) {
@@ -1647,5 +1997,5 @@ _glcpp_parser_skip_stack_pop (glcpp_parser_t *parser, YYLTYPE *loc)
 
        node = parser->skip_stack;
        parser->skip_stack = node->next;
-       talloc_free (node);
+       ralloc_free (node);
 }