]> git.saurik.com Git - bison.git/blobdiff - src/scan-gram.l
Add %option nounput, since we no longer use unput.
[bison.git] / src / scan-gram.l
index a96b18b9ba8af7cc4b8646e838e329abacb07294..4416730cbe321b621de40c4623f91f2739b983d1 100644 (file)
@@ -1,5 +1,6 @@
 /* Bison Grammar Scanner                             -*- C -*-
 /* Bison Grammar Scanner                             -*- C -*-
-   Copyright (C) 2002 Free Software Foundation, Inc.
+
+   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
 
    This file is part of Bison, the GNU Compiler Compiler.
 
 
    This file is part of Bison, the GNU Compiler Compiler.
 
    02111-1307  USA
 */
 
    02111-1307  USA
 */
 
-%option debug nodefault noyywrap never-interactive
+%option debug nodefault nounput noyywrap never-interactive
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
 #include "system.h"
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
 #include "system.h"
-#include "mbswidth.h"
+
+#include <mbswidth.h>
+#include <get-errno.h>
+#include <quote.h>
+
 #include "complain.h"
 #include "files.h"
 #include "complain.h"
 #include "files.h"
-#include "quote.h"
-#include "struniq.h"
 #include "getargs.h"
 #include "gram.h"
 #include "reader.h"
 #include "getargs.h"
 #include "gram.h"
 #include "reader.h"
+#include "uniqstr.h"
 
 #define YY_USER_INIT                                   \
   do                                                   \
 
 #define YY_USER_INIT                                   \
   do                                                   \
       scanner_cursor.file = current_file;              \
       scanner_cursor.line = 1;                         \
       scanner_cursor.column = 1;                       \
       scanner_cursor.file = current_file;              \
       scanner_cursor.line = 1;                         \
       scanner_cursor.column = 1;                       \
+      code_start = scanner_cursor;                     \
     }                                                  \
   while (0)
 
 /* Location of scanner cursor.  */
 boundary scanner_cursor;
 
     }                                                  \
   while (0)
 
 /* Location of scanner cursor.  */
 boundary scanner_cursor;
 
-static void adjust_location (location_t *, char const *, size_t);
+static void adjust_location (location *, char const *, size_t);
 #define YY_USER_ACTION  adjust_location (loc, yytext, yyleng);
 
 static size_t no_cr_read (FILE *, char *, size_t);
 #define YY_INPUT(buf, result, size) ((result) = no_cr_read (yyin, buf, size))
 
 
 #define YY_USER_ACTION  adjust_location (loc, yytext, yyleng);
 
 static size_t no_cr_read (FILE *, char *, size_t);
 #define YY_INPUT(buf, result, size) ((result) = no_cr_read (yyin, buf, size))
 
 
-/* STRING_OBSTACK -- Used to store all the characters that we need to
+/* OBSTACK_FOR_STRING -- Used to store all the characters that we need to
    keep (to construct ID, STRINGS etc.).  Use the following macros to
    use it.
 
    keep (to construct ID, STRINGS etc.).  Use the following macros to
    use it.
 
@@ -61,23 +66,23 @@ static size_t no_cr_read (FILE *, char *, size_t);
    STRING_FINISH also stores this string in LAST_STRING, which can be
    used, and which is used by STRING_FREE to free the last string.  */
 
    STRING_FINISH also stores this string in LAST_STRING, which can be
    used, and which is used by STRING_FREE to free the last string.  */
 
-static struct obstack string_obstack;
+static struct obstack obstack_for_string;
 
 /* A string representing the most recently saved token.  */
 static char *last_string;
 
 
 #define STRING_GROW   \
 
 /* A string representing the most recently saved token.  */
 static char *last_string;
 
 
 #define STRING_GROW   \
-  obstack_grow (&string_obstack, yytext, yyleng)
+  obstack_grow (&obstack_for_string, yytext, yyleng)
 
 #define STRING_FINISH                                  \
   do {                                                 \
 
 #define STRING_FINISH                                  \
   do {                                                 \
-    obstack_1grow (&string_obstack, '\0');             \
-    last_string = obstack_finish (&string_obstack);    \
+    obstack_1grow (&obstack_for_string, '\0');         \
+    last_string = obstack_finish (&obstack_for_string);        \
   } while (0)
 
 #define STRING_FREE \
   } while (0)
 
 #define STRING_FREE \
-  obstack_free (&string_obstack, last_string)
+  obstack_free (&obstack_for_string, last_string)
 
 void
 scanner_last_string_free (void)
 
 void
 scanner_last_string_free (void)
@@ -93,25 +98,23 @@ scanner_last_string_free (void)
    Outside of well-formed rules, RULE_LENGTH has an undefined value.  */
 static int rule_length;
 
    Outside of well-formed rules, RULE_LENGTH has an undefined value.  */
 static int rule_length;
 
-static void handle_dollar (braced_code_t code_kind,
-                          char *cp, location_t location);
-static void handle_at (braced_code_t code_kind,
-                      char *cp, location_t location);
+static void handle_dollar (int token_type, char *cp, location loc);
+static void handle_at (int token_type, char *cp, location loc);
 static void handle_syncline (char *args);
 static int convert_ucn_to_byte (char const *hex_text);
 static void handle_syncline (char *args);
 static int convert_ucn_to_byte (char const *hex_text);
-static void unexpected_end_of_file (boundary, char const *);
+static void unexpected_eof (boundary, char const *);
 
 %}
 %x SC_COMMENT SC_LINE_COMMENT SC_YACC_COMMENT
 %x SC_STRING SC_CHARACTER
 %x SC_AFTER_IDENTIFIER
 %x SC_ESCAPED_STRING SC_ESCAPED_CHARACTER
 
 %}
 %x SC_COMMENT SC_LINE_COMMENT SC_YACC_COMMENT
 %x SC_STRING SC_CHARACTER
 %x SC_AFTER_IDENTIFIER
 %x SC_ESCAPED_STRING SC_ESCAPED_CHARACTER
-%x SC_BRACED_CODE SC_PROLOGUE SC_EPILOGUE
+%x SC_PRE_CODE SC_BRACED_CODE SC_PROLOGUE SC_EPILOGUE
 
 letter   [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
 id       {letter}({letter}|[0-9])*
 directive %{letter}({letter}|[0-9]|-)*
 
 letter   [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
 id       {letter}({letter}|[0-9])*
 directive %{letter}({letter}|[0-9]|-)*
-int      [0-9]+
+int      [0-9]+
 
 /* POSIX says that a tag must be both an id and a C union member, but
    historically almost any character is allowed in a tag.  We disallow
 
 /* POSIX says that a tag must be both an id and a C union member, but
    historically almost any character is allowed in a tag.  We disallow
@@ -130,15 +133,20 @@ splice     (\\[ \f\t\v]*\n)*
   /* Parent context state, when applicable.  */
   int context_state IF_LINT (= 0);
 
   /* Parent context state, when applicable.  */
   int context_state IF_LINT (= 0);
 
+  /* Token type to return, when applicable.  */
+  int token_type IF_LINT (= 0);
+
   /* Location of most recent identifier, when applicable.  */
   /* Location of most recent identifier, when applicable.  */
-  location_t id_loc IF_LINT (= *loc);
+  location id_loc IF_LINT (= empty_location);
 
 
-  /* Location where containing code started, when applicable.  */
-  boundary code_start IF_LINT (= loc->start);
+  /* Where containing code started, when applicable.  Its initial
+     value is relevant only when yylex is invoked in the SC_EPILOGUE
+     start condition.  */
+  boundary code_start = scanner_cursor;
 
 
-  /* Location where containing comment or string or character literal
-     started, when applicable.  */
-  boundary token_start IF_LINT (= loc->start);
+  /* Where containing comment or string or character literal started,
+     when applicable.  */
+  boundary token_start IF_LINT (= scanner_cursor);
 %}
 
 
 %}
 
 
@@ -146,13 +154,18 @@ splice     (\\[ \f\t\v]*\n)*
   | Scanning white space.  |
   `-----------------------*/
 
   | Scanning white space.  |
   `-----------------------*/
 
-<INITIAL,SC_AFTER_IDENTIFIER>
+<INITIAL,SC_AFTER_IDENTIFIER,SC_PRE_CODE>
 {
   [ \f\n\t\v]  ;
 {
   [ \f\n\t\v]  ;
+  ","         warn_at (*loc, _("stray `,' treated as white space"));
 
   /* Comments. */
 
   /* Comments. */
-  "/*"         token_start = loc->start; context_state = YY_START; BEGIN SC_YACC_COMMENT;
   "//".*       ;
   "//".*       ;
+  "/*" {
+    token_start = loc->start;
+    context_state = YY_START;
+    BEGIN SC_YACC_COMMENT;
+  }
 
   /* #line directives are not documented, and may be withdrawn or
      modified in future versions of Bison.  */
 
   /* #line directives are not documented, and may be withdrawn or
      modified in future versions of Bison.  */
@@ -171,7 +184,7 @@ splice       (\\[ \f\t\v]*\n)*
   "%debug"                return PERCENT_DEBUG;
   "%define"               return PERCENT_DEFINE;
   "%defines"              return PERCENT_DEFINES;
   "%debug"                return PERCENT_DEBUG;
   "%define"               return PERCENT_DEFINE;
   "%defines"              return PERCENT_DEFINES;
-  "%destructor"           return PERCENT_DESTRUCTOR;
+  "%destructor"                  token_type = PERCENT_DESTRUCTOR; BEGIN SC_PRE_CODE;
   "%dprec"               return PERCENT_DPREC;
   "%error"[-_]"verbose"   return PERCENT_ERROR_VERBOSE;
   "%expect"               return PERCENT_EXPECT;
   "%dprec"               return PERCENT_DPREC;
   "%error"[-_]"verbose"   return PERCENT_ERROR_VERBOSE;
   "%expect"               return PERCENT_EXPECT;
@@ -179,6 +192,7 @@ splice       (\\[ \f\t\v]*\n)*
   "%fixed"[-_]"output"[-_]"files"   return PERCENT_YACC;
   "%glr-parser"           return PERCENT_GLR_PARSER;
   "%left"                 return PERCENT_LEFT;
   "%fixed"[-_]"output"[-_]"files"   return PERCENT_YACC;
   "%glr-parser"           return PERCENT_GLR_PARSER;
   "%left"                 return PERCENT_LEFT;
+  "%lex-param"           token_type = PERCENT_LEX_PARAM; BEGIN SC_PRE_CODE;
   "%locations"            return PERCENT_LOCATIONS;
   "%merge"               return PERCENT_MERGE;
   "%name"[-_]"prefix"     return PERCENT_NAME_PREFIX;
   "%locations"            return PERCENT_LOCATIONS;
   "%merge"               return PERCENT_MERGE;
   "%name"[-_]"prefix"     return PERCENT_NAME_PREFIX;
@@ -186,19 +200,18 @@ splice     (\\[ \f\t\v]*\n)*
   "%nonassoc"             return PERCENT_NONASSOC;
   "%nterm"                return PERCENT_NTERM;
   "%output"               return PERCENT_OUTPUT;
   "%nonassoc"             return PERCENT_NONASSOC;
   "%nterm"                return PERCENT_NTERM;
   "%output"               return PERCENT_OUTPUT;
-  "%parse-param"          return PERCENT_PARSE_PARAM;
+  "%parse-param"         token_type = PERCENT_PARSE_PARAM; BEGIN SC_PRE_CODE;
   "%prec"                 rule_length--; return PERCENT_PREC;
   "%prec"                 rule_length--; return PERCENT_PREC;
-  "%printer"              return PERCENT_PRINTER;
+  "%printer"              token_type = PERCENT_PRINTER; BEGIN SC_PRE_CODE;
   "%pure"[-_]"parser"     return PERCENT_PURE_PARSER;
   "%right"                return PERCENT_RIGHT;
   "%pure"[-_]"parser"     return PERCENT_PURE_PARSER;
   "%right"                return PERCENT_RIGHT;
-  "%lex-param"            return PERCENT_LEX_PARAM;
   "%skeleton"             return PERCENT_SKELETON;
   "%start"                return PERCENT_START;
   "%term"                 return PERCENT_TOKEN;
   "%token"                return PERCENT_TOKEN;
   "%token"[-_]"table"     return PERCENT_TOKEN_TABLE;
   "%type"                 return PERCENT_TYPE;
   "%skeleton"             return PERCENT_SKELETON;
   "%start"                return PERCENT_START;
   "%term"                 return PERCENT_TOKEN;
   "%token"                return PERCENT_TOKEN;
   "%token"[-_]"table"     return PERCENT_TOKEN_TABLE;
   "%type"                 return PERCENT_TYPE;
-  "%union"                return PERCENT_UNION;
+  "%union"               token_type = PERCENT_UNION; BEGIN SC_PRE_CODE;
   "%verbose"              return PERCENT_VERBOSE;
   "%yacc"                 return PERCENT_YACC;
 
   "%verbose"              return PERCENT_VERBOSE;
   "%yacc"                 return PERCENT_YACC;
 
@@ -210,10 +223,6 @@ splice      (\\[ \f\t\v]*\n)*
   "|"                     rule_length = 0; return PIPE;
   ";"                     return SEMICOLON;
 
   "|"                     rule_length = 0; return PIPE;
   ";"                     return SEMICOLON;
 
-  "," {
-    warn_at (*loc, _("stray `,' treated as white space"));
-  }
-
   {id} {
     val->symbol = symbol_get (yytext, *loc);
     id_loc = *loc;
   {id} {
     val->symbol = symbol_get (yytext, *loc);
     id_loc = *loc;
@@ -223,9 +232,9 @@ splice       (\\[ \f\t\v]*\n)*
 
   {int} {
     unsigned long num;
 
   {int} {
     unsigned long num;
-    errno = 0;
+    set_errno (0);
     num = strtoul (yytext, 0, 10);
     num = strtoul (yytext, 0, 10);
-    if (INT_MAX < num || errno)
+    if (INT_MAX < num || get_errno ())
       {
        complain_at (*loc, _("integer out of range: %s"), quote (yytext));
        num = INT_MAX;
       {
        complain_at (*loc, _("integer out of range: %s"), quote (yytext));
        num = INT_MAX;
@@ -246,6 +255,7 @@ splice       (\\[ \f\t\v]*\n)*
   /* Code in between braces.  */
   "{" {
     STRING_GROW;
   /* Code in between braces.  */
   "{" {
     STRING_GROW;
+    token_type = BRACED_CODE;
     braces_level = 0;
     code_start = loc->start;
     BEGIN SC_BRACED_CODE;
     braces_level = 0;
     code_start = loc->start;
     BEGIN SC_BRACED_CODE;
@@ -253,9 +263,9 @@ splice       (\\[ \f\t\v]*\n)*
 
   /* A type. */
   "<"{tag}">" {
 
   /* A type. */
   "<"{tag}">" {
-    obstack_grow (&string_obstack, yytext + 1, yyleng - 2);
+    obstack_grow (&obstack_for_string, yytext + 1, yyleng - 2);
     STRING_FINISH;
     STRING_FINISH;
-    val->struniq = struniq_new (last_string);
+    val->uniqstr = uniqstr_new (last_string);
     STRING_FREE;
     return TYPE;
   }
     STRING_FREE;
     return TYPE;
   }
@@ -263,16 +273,18 @@ splice     (\\[ \f\t\v]*\n)*
   "%%" {
     static int percent_percent_count;
     if (++percent_percent_count == 2)
   "%%" {
     static int percent_percent_count;
     if (++percent_percent_count == 2)
-      {
-       code_start = loc->start;
-       BEGIN SC_EPILOGUE;
-      }
+      BEGIN SC_EPILOGUE;
     return PERCENT_PERCENT;
   }
 
   . {
     complain_at (*loc, _("invalid character: %s"), quote (yytext));
   }
     return PERCENT_PERCENT;
   }
 
   . {
     complain_at (*loc, _("invalid character: %s"), quote (yytext));
   }
+
+  <<EOF>> {
+    loc->start = loc->end = scanner_cursor;
+    yyterminate ();
+  }
 }
 
 
 }
 
 
@@ -311,7 +323,7 @@ splice       (\\[ \f\t\v]*\n)*
 {
   "*/"     BEGIN context_state;
   .|\n    ;
 {
   "*/"     BEGIN context_state;
   .|\n    ;
-  <<EOF>>  unexpected_end_of_file (token_start, "*/");
+  <<EOF>>  unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
 }
 
 
@@ -322,7 +334,7 @@ splice       (\\[ \f\t\v]*\n)*
 <SC_COMMENT>
 {
   "*"{splice}"/"  STRING_GROW; BEGIN context_state;
 <SC_COMMENT>
 {
   "*"{splice}"/"  STRING_GROW; BEGIN context_state;
-  <<EOF>>        unexpected_end_of_file (token_start, "*/");
+  <<EOF>>        unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
 }
 
 
@@ -349,14 +361,14 @@ splice     (\\[ \f\t\v]*\n)*
     STRING_GROW;
     STRING_FINISH;
     loc->start = token_start;
     STRING_GROW;
     STRING_FINISH;
     loc->start = token_start;
-    val->string = last_string;
+    val->chars = last_string;
     rule_length++;
     BEGIN INITIAL;
     return STRING;
   }
 
   .|\n     STRING_GROW;
     rule_length++;
     BEGIN INITIAL;
     return STRING;
   }
 
   .|\n     STRING_GROW;
-  <<EOF>>   unexpected_end_of_file (token_start, "\"");
+  <<EOF>>   unexpected_eof (token_start, "\""); BEGIN INITIAL;
 }
 
   /*---------------------------------------------------------------.
 }
 
   /*---------------------------------------------------------------.
@@ -367,13 +379,14 @@ splice     (\\[ \f\t\v]*\n)*
 <SC_ESCAPED_CHARACTER>
 {
   "'" {
 <SC_ESCAPED_CHARACTER>
 {
   "'" {
+    unsigned char last_string_1;
     STRING_GROW;
     STRING_FINISH;
     loc->start = token_start;
     val->symbol = symbol_get (last_string, *loc);
     symbol_class_set (val->symbol, token_sym, *loc);
     STRING_GROW;
     STRING_FINISH;
     loc->start = token_start;
     val->symbol = symbol_get (last_string, *loc);
     symbol_class_set (val->symbol, token_sym, *loc);
-    symbol_user_token_number_set (val->symbol,
-                                 (unsigned char) last_string[1], *loc);
+    last_string_1 = last_string[1];
+    symbol_user_token_number_set (val->symbol, last_string_1, *loc);
     STRING_FREE;
     rule_length++;
     BEGIN INITIAL;
     STRING_FREE;
     rule_length++;
     BEGIN INITIAL;
@@ -381,7 +394,7 @@ splice       (\\[ \f\t\v]*\n)*
   }
 
   .|\n     STRING_GROW;
   }
 
   .|\n     STRING_GROW;
-  <<EOF>>   unexpected_end_of_file (token_start, "'");
+  <<EOF>>   unexpected_eof (token_start, "'"); BEGIN INITIAL;
 }
 
 
 }
 
 
@@ -396,36 +409,36 @@ splice     (\\[ \f\t\v]*\n)*
     if (UCHAR_MAX < c)
       complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
     else
     if (UCHAR_MAX < c)
       complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
     else
-      obstack_1grow (&string_obstack, c);
+      obstack_1grow (&obstack_for_string, c);
   }
 
   \\x[0-9abcdefABCDEF]+ {
     unsigned long c;
   }
 
   \\x[0-9abcdefABCDEF]+ {
     unsigned long c;
-    errno = 0;
+    set_errno (0);
     c = strtoul (yytext + 2, 0, 16);
     c = strtoul (yytext + 2, 0, 16);
-    if (UCHAR_MAX < c || errno)
+    if (UCHAR_MAX < c || get_errno ())
       complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
     else
       complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
     else
-      obstack_1grow (&string_obstack, c);
+      obstack_1grow (&obstack_for_string, c);
   }
 
   }
 
-  \\a  obstack_1grow (&string_obstack, '\a');
-  \\b  obstack_1grow (&string_obstack, '\b');
-  \\f  obstack_1grow (&string_obstack, '\f');
-  \\n  obstack_1grow (&string_obstack, '\n');
-  \\r  obstack_1grow (&string_obstack, '\r');
-  \\t  obstack_1grow (&string_obstack, '\t');
-  \\v  obstack_1grow (&string_obstack, '\v');
+  \\a  obstack_1grow (&obstack_for_string, '\a');
+  \\b  obstack_1grow (&obstack_for_string, '\b');
+  \\f  obstack_1grow (&obstack_for_string, '\f');
+  \\n  obstack_1grow (&obstack_for_string, '\n');
+  \\r  obstack_1grow (&obstack_for_string, '\r');
+  \\t  obstack_1grow (&obstack_for_string, '\t');
+  \\v  obstack_1grow (&obstack_for_string, '\v');
 
   /* \\[\"\'?\\] would be shorter, but it confuses xgettext.  */
 
   /* \\[\"\'?\\] would be shorter, but it confuses xgettext.  */
-  \\("\""|"'"|"?"|"\\")  obstack_1grow (&string_obstack, yytext[1]);
+  \\("\""|"'"|"?"|"\\")  obstack_1grow (&obstack_for_string, yytext[1]);
 
   \\(u|U[0-9abcdefABCDEF]{4})[0-9abcdefABCDEF]{4} {
     int c = convert_ucn_to_byte (yytext);
     if (c < 0)
       complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
     else
 
   \\(u|U[0-9abcdefABCDEF]{4})[0-9abcdefABCDEF]{4} {
     int c = convert_ucn_to_byte (yytext);
     if (c < 0)
       complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
     else
-      obstack_1grow (&string_obstack, c);
+      obstack_1grow (&obstack_for_string, c);
   }
   \\(.|\n)     {
     complain_at (*loc, _("unrecognized escape sequence: %s"), quote (yytext));
   }
   \\(.|\n)     {
     complain_at (*loc, _("unrecognized escape sequence: %s"), quote (yytext));
@@ -443,7 +456,7 @@ splice       (\\[ \f\t\v]*\n)*
 {
   "'"                  STRING_GROW; BEGIN context_state;
   \\{splice}[^$@\[\]]  STRING_GROW;
 {
   "'"                  STRING_GROW; BEGIN context_state;
   \\{splice}[^$@\[\]]  STRING_GROW;
-  <<EOF>>              unexpected_end_of_file (token_start, "'");
+  <<EOF>>              unexpected_eof (token_start, "'"); BEGIN context_state;
 }
 
 
 }
 
 
@@ -456,7 +469,10 @@ splice      (\\[ \f\t\v]*\n)*
 {
   "\""                 STRING_GROW; BEGIN context_state;
   \\{splice}[^$@\[\]]  STRING_GROW;
 {
   "\""                 STRING_GROW; BEGIN context_state;
   \\{splice}[^$@\[\]]  STRING_GROW;
-  <<EOF>>              unexpected_end_of_file (token_start, "\"");
+  <<EOF>> {
+    unexpected_eof (token_start, "\"");
+    BEGIN context_state;
+  }
 }
 
 
 }
 
 
@@ -492,6 +508,41 @@ splice      (\\[ \f\t\v]*\n)*
 }
 
 
 }
 
 
+  /*---------------------------------------------------------------.
+  | Scanning after %union etc., possibly followed by white space.  |
+  | For %union only, allow arbitrary C code to appear before the   |
+  | following brace, as an extension to POSIX.                    |
+  `---------------------------------------------------------------*/
+
+<SC_PRE_CODE>
+{
+  . {
+    bool valid = yytext[0] == '{' || token_type == PERCENT_UNION;
+    scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);
+    yyless (0);
+
+    if (valid)
+      {
+       braces_level = -1;
+       code_start = loc->start;
+       BEGIN SC_BRACED_CODE;
+      }
+    else
+      {
+       complain_at (*loc, _("missing `{' in `%s'"),
+                    token_name (token_type));
+       obstack_sgrow (&obstack_for_string, "{}");
+       STRING_FINISH;
+       val->chars = last_string;
+       BEGIN INITIAL;
+       return token_type;
+      }
+  }
+
+  <<EOF>>  unexpected_eof (scanner_cursor, "{}"); BEGIN INITIAL;
+}
+
+
   /*---------------------------------------------------------------.
   | Scanning some code in braces (%union and actions). The initial |
   | "{" is already eaten.                                          |
   /*---------------------------------------------------------------.
   | Scanning some code in braces (%union and actions). The initial |
   | "{" is already eaten.                                          |
@@ -502,16 +553,35 @@ splice     (\\[ \f\t\v]*\n)*
   "{"|"<"{splice}"%"  STRING_GROW; braces_level++;
   "%"{splice}">"      STRING_GROW; braces_level--;
   "}" {
   "{"|"<"{splice}"%"  STRING_GROW; braces_level++;
   "%"{splice}">"      STRING_GROW; braces_level--;
   "}" {
-    STRING_GROW;
-    braces_level--;
-    if (braces_level < 0)
+    bool outer_brace = --braces_level < 0;
+
+    /* As an undocumented Bison extension, append `;' before the last
+       brace in braced code, so that the user code can omit trailing
+       `;'.  But do not append `;' if emulating Yacc, since Yacc does
+       not append one.
+
+       FIXME: Bison should warn if a semicolon seems to be necessary
+       here, and should omit the semicolon if it seems unnecessary
+       (e.g., after ';', '{', or '}', each followed by comments or
+       white space).  Such a warning shouldn't depend on --yacc; it
+       should depend on a new --pedantic option, which would cause
+       Bison to warn if it detects an extension to POSIX.  --pedantic
+       should also diagnose other Bison extensions like %yacc.
+       Perhaps there should also be a GCC-style --pedantic-errors
+       option, so that such warnings are diagnosed as errors.  */
+    if (outer_brace && token_type == BRACED_CODE && ! yacc_flag)
+      obstack_1grow (&obstack_for_string, ';');
+
+    obstack_1grow (&obstack_for_string, '}');
+
+    if (outer_brace)
       {
        STRING_FINISH;
       {
        STRING_FINISH;
-       loc->start = code_start;
-       val->string = last_string;
        rule_length++;
        rule_length++;
+       loc->start = code_start;
+       val->chars = last_string;
        BEGIN INITIAL;
        BEGIN INITIAL;
-       return BRACED_CODE;
+       return token_type;
       }
   }
 
       }
   }
 
@@ -519,12 +589,10 @@ splice     (\\[ \f\t\v]*\n)*
      (as `<' `<%').  */
   "<"{splice}"<"  STRING_GROW;
 
      (as `<' `<%').  */
   "<"{splice}"<"  STRING_GROW;
 
-  "$"("<"{tag}">")?(-?[0-9]+|"$") { handle_dollar (current_braced_code,
-                                                  yytext, *loc); }
-  "@"(-?[0-9]+|"$")               { handle_at (current_braced_code,
-                                              yytext, *loc); }
+  "$"("<"{tag}">")?(-?[0-9]+|"$")  handle_dollar (token_type, yytext, *loc);
+  "@"(-?[0-9]+|"$")               handle_at (token_type, yytext, *loc);
 
 
-  <<EOF>>  unexpected_end_of_file (code_start, "}");
+  <<EOF>>  unexpected_eof (code_start, "}"); BEGIN INITIAL;
 }
 
 
 }
 
 
@@ -537,12 +605,12 @@ splice     (\\[ \f\t\v]*\n)*
   "%}" {
     STRING_FINISH;
     loc->start = code_start;
   "%}" {
     STRING_FINISH;
     loc->start = code_start;
-    val->string = last_string;
+    val->chars = last_string;
     BEGIN INITIAL;
     return PROLOGUE;
   }
 
     BEGIN INITIAL;
     return PROLOGUE;
   }
 
-  <<EOF>>  unexpected_end_of_file (code_start, "%}");
+  <<EOF>>  unexpected_eof (code_start, "%}"); BEGIN INITIAL;
 }
 
 
 }
 
 
@@ -556,7 +624,7 @@ splice       (\\[ \f\t\v]*\n)*
   <<EOF>> {
     STRING_FINISH;
     loc->start = code_start;
   <<EOF>> {
     STRING_FINISH;
     loc->start = code_start;
-    val->string = last_string;
+    val->chars = last_string;
     BEGIN INITIAL;
     return EPILOGUE;
   }
     BEGIN INITIAL;
     return EPILOGUE;
   }
@@ -570,10 +638,10 @@ splice     (\\[ \f\t\v]*\n)*
 
 <SC_COMMENT,SC_LINE_COMMENT,SC_STRING,SC_CHARACTER,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
 {
 
 <SC_COMMENT,SC_LINE_COMMENT,SC_STRING,SC_CHARACTER,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
 {
-  \$   obstack_sgrow (&string_obstack, "$][");
-  \@   obstack_sgrow (&string_obstack, "@@");
-  \[   obstack_sgrow (&string_obstack, "@{");
-  \]   obstack_sgrow (&string_obstack, "@}");
+  \$   obstack_sgrow (&obstack_for_string, "$][");
+  \@   obstack_sgrow (&obstack_for_string, "@@");
+  \[   obstack_sgrow (&obstack_for_string, "@{");
+  \]   obstack_sgrow (&obstack_for_string, "@}");
   .|\n  STRING_GROW;
 }
 
   .|\n  STRING_GROW;
 }
 
@@ -584,7 +652,7 @@ splice       (\\[ \f\t\v]*\n)*
    size SIZE.  */
 
 static void
    size SIZE.  */
 
 static void
-adjust_location (location_t *loc, char const *token, size_t size)
+adjust_location (location *loc, char const *token, size_t size)
 {
   int line = scanner_cursor.line;
   int column = scanner_cursor.column;
 {
   int line = scanner_cursor.line;
   int column = scanner_cursor.column;
@@ -624,14 +692,14 @@ adjust_location (location_t *loc, char const *token, size_t size)
 static size_t
 no_cr_read (FILE *fp, char *buf, size_t size)
 {
 static size_t
 no_cr_read (FILE *fp, char *buf, size_t size)
 {
-  size_t s = fread (buf, 1, size, fp);
-  if (s)
+  size_t bytes_read = fread (buf, 1, size, fp);
+  if (bytes_read)
     {
     {
-      char *w = memchr (buf, '\r', s);
+      char *w = memchr (buf, '\r', bytes_read);
       if (w)
        {
          char const *r = ++w;
       if (w)
        {
          char const *r = ++w;
-         char const *lim = buf + s;
+         char const *lim = buf + bytes_read;
 
          for (;;)
            {
 
          for (;;)
            {
@@ -660,7 +728,7 @@ no_cr_read (FILE *fp, char *buf, size_t size)
        }
     }
 
        }
     }
 
-  return s;
+  return bytes_read;
 }
 
 
 }
 
 
@@ -669,15 +737,18 @@ no_cr_read (FILE *fp, char *buf, size_t size)
 |                                                                   |
 | Possible inputs: $[<TYPENAME>]($|integer)                         |
 |                                                                   |
 |                                                                   |
 | Possible inputs: $[<TYPENAME>]($|integer)                         |
 |                                                                   |
-| Output to the STRING_OBSTACK a reference to this semantic value.  |
+| Output to OBSTACK_FOR_STRING a reference to this semantic value.  |
 `------------------------------------------------------------------*/
 
 `------------------------------------------------------------------*/
 
-static inline void
-handle_action_dollar (char *text, location_t location)
+static inline bool
+handle_action_dollar (char *text, location loc)
 {
   const char *type_name = NULL;
   char *cp = text + 1;
 
 {
   const char *type_name = NULL;
   char *cp = text + 1;
 
+  if (! current_rule)
+    return false;
+
   /* Get the type name if explicit. */
   if (*cp == '<')
     {
   /* Get the type name if explicit. */
   if (*cp == '<')
     {
@@ -691,127 +762,108 @@ handle_action_dollar (char *text, location_t location)
   if (*cp == '$')
     {
       if (!type_name)
   if (*cp == '$')
     {
       if (!type_name)
-       type_name = symbol_list_n_type_name_get (current_rule, location, 0);
+       type_name = symbol_list_n_type_name_get (current_rule, loc, 0);
       if (!type_name && typed)
       if (!type_name && typed)
-       complain_at (location, _("$$ of `%s' has no declared type"),
+       complain_at (loc, _("$$ of `%s' has no declared type"),
                     current_rule->sym->tag);
       if (!type_name)
        type_name = "";
                     current_rule->sym->tag);
       if (!type_name)
        type_name = "";
-      obstack_fgrow1 (&string_obstack,
+      obstack_fgrow1 (&obstack_for_string,
                      "]b4_lhs_value([%s])[", type_name);
     }
   else
     {
       long num;
                      "]b4_lhs_value([%s])[", type_name);
     }
   else
     {
       long num;
-      errno = 0;
+      set_errno (0);
       num = strtol (cp, 0, 10);
 
       num = strtol (cp, 0, 10);
 
-      if (INT_MIN <= num && num <= rule_length && ! errno)
+      if (INT_MIN <= num && num <= rule_length && ! get_errno ())
        {
          int n = num;
          if (!type_name && n > 0)
        {
          int n = num;
          if (!type_name && n > 0)
-           type_name = symbol_list_n_type_name_get (current_rule, location,
-                                                    n);
+           type_name = symbol_list_n_type_name_get (current_rule, loc, n);
          if (!type_name && typed)
          if (!type_name && typed)
-           complain_at (location, _("$%d of `%s' has no declared type"),
-                     n, current_rule->sym->tag);
+           complain_at (loc, _("$%d of `%s' has no declared type"),
+                        n, current_rule->sym->tag);
          if (!type_name)
            type_name = "";
          if (!type_name)
            type_name = "";
-         obstack_fgrow3 (&string_obstack,
+         obstack_fgrow3 (&obstack_for_string,
                          "]b4_rhs_value([%d], [%d], [%s])[",
                          rule_length, n, type_name);
        }
       else
                          "]b4_rhs_value([%d], [%d], [%s])[",
                          rule_length, n, type_name);
        }
       else
-       complain_at (location, _("integer out of range: %s"), quote (text));
+       complain_at (loc, _("integer out of range: %s"), quote (text));
     }
     }
-}
-
-
-/*---------------------------------------------------------------.
-| TEXT is expected to be $$ in some code associated to a symbol: |
-| destructor or printer.                                         |
-`---------------------------------------------------------------*/
 
 
-static inline void
-handle_symbol_code_dollar (char *text, location_t location)
-{
-  char *cp = text + 1;
-  if (*cp == '$')
-    obstack_sgrow (&string_obstack, "]b4_dollar_dollar[");
-  else
-    complain_at (location, _("invalid value: %s"), quote (text));
+  return true;
 }
 
 
 /*-----------------------------------------------------------------.
 | Dispatch onto handle_action_dollar, or handle_destructor_dollar, |
 }
 
 
 /*-----------------------------------------------------------------.
 | Dispatch onto handle_action_dollar, or handle_destructor_dollar, |
-| depending upon CODE_KIND.                                        |
+| depending upon TOKEN_TYPE.                                       |
 `-----------------------------------------------------------------*/
 
 static void
 `-----------------------------------------------------------------*/
 
 static void
-handle_dollar (braced_code_t braced_code_kind,
-              char *text, location_t location)
+handle_dollar (int token_type, char *text, location loc)
 {
 {
-  switch (braced_code_kind)
+  switch (token_type)
     {
     {
-    case action_braced_code:
-      handle_action_dollar (text, location);
+    case BRACED_CODE:
+      if (handle_action_dollar (text, loc))
+       return;
       break;
 
       break;
 
-    case destructor_braced_code:
-    case printer_braced_code:
-      handle_symbol_code_dollar (text, location);
+    case PERCENT_DESTRUCTOR:
+    case PERCENT_PRINTER:
+      if (text[1] == '$')
+       {
+         obstack_sgrow (&obstack_for_string, "]b4_dollar_dollar[");
+         return;
+       }
+      break;
+
+    default:
       break;
     }
       break;
     }
+
+  complain_at (loc, _("invalid value: %s"), quote (text));
 }
 
 
 /*------------------------------------------------------.
 | TEXT is a location token (i.e., a `@...').  Output to |
 }
 
 
 /*------------------------------------------------------.
 | TEXT is a location token (i.e., a `@...').  Output to |
-| STRING_OBSTACK a reference to this location.          |
+| OBSTACK_FOR_STRING a reference to this location.      |
 `------------------------------------------------------*/
 
 `------------------------------------------------------*/
 
-static inline void
-handle_action_at (char *text, location_t location)
+static inline bool
+handle_action_at (char *text, location loc)
 {
   char *cp = text + 1;
   locations_flag = 1;
 
 {
   char *cp = text + 1;
   locations_flag = 1;
 
+  if (! current_rule)
+    return false;
+
   if (*cp == '$')
   if (*cp == '$')
-    {
-      obstack_sgrow (&string_obstack, "]b4_lhs_location[");
-    }
+    obstack_sgrow (&obstack_for_string, "]b4_lhs_location[");
   else
     {
       long num;
   else
     {
       long num;
-      errno = 0;
+      set_errno (0);
       num = strtol (cp, 0, 10);
 
       num = strtol (cp, 0, 10);
 
-      if (INT_MIN <= num && num <= rule_length && ! errno)
+      if (INT_MIN <= num && num <= rule_length && ! get_errno ())
        {
          int n = num;
        {
          int n = num;
-         obstack_fgrow2 (&string_obstack, "]b4_rhs_location([%d], [%d])[",
+         obstack_fgrow2 (&obstack_for_string, "]b4_rhs_location([%d], [%d])[",
                          rule_length, n);
        }
       else
                          rule_length, n);
        }
       else
-       complain_at (location, _("integer out of range: %s"), quote (text));
+       complain_at (loc, _("integer out of range: %s"), quote (text));
     }
     }
-}
-
 
 
-/*---------------------------------------------------------------.
-| TEXT is expected to be @$ in some code associated to a symbol: |
-| destructor or printer.                                         |
-`---------------------------------------------------------------*/
-
-static inline void
-handle_symbol_code_at (char *text, location_t location)
-{
-  char *cp = text + 1;
-  if (*cp == '$')
-    obstack_sgrow (&string_obstack, "]b4_at_dollar[");
-  else
-    complain_at (location, _("invalid value: %s"), quote (text));
+  return true;
 }
 
 
 }
 
 
@@ -821,20 +873,28 @@ handle_symbol_code_at (char *text, location_t location)
 `-------------------------------------------------------------------*/
 
 static void
 `-------------------------------------------------------------------*/
 
 static void
-handle_at (braced_code_t braced_code_kind,
-          char *text, location_t location)
+handle_at (int token_type, char *text, location loc)
 {
 {
-  switch (braced_code_kind)
+  switch (token_type)
     {
     {
-    case action_braced_code:
-      handle_action_at (text, location);
+    case BRACED_CODE:
+      handle_action_at (text, loc);
+      return;
+
+    case PERCENT_DESTRUCTOR:
+    case PERCENT_PRINTER:
+      if (text[1] == '$')
+       {
+         obstack_sgrow (&obstack_for_string, "]b4_at_dollar[");
+         return;
+       }
       break;
 
       break;
 
-    case destructor_braced_code:
-    case printer_braced_code:
-      handle_symbol_code_at (text, location);
+    default:
       break;
     }
       break;
     }
+
+  complain_at (loc, _("invalid value: %s"), quote (text));
 }
 
 
 }
 
 
@@ -914,25 +974,15 @@ handle_syncline (char *args)
 /*------------------------------------------------------------------------.
 | Report an unexpected EOF in a token or comment starting at START.       |
 | An end of file was encountered and the expected TOKEN_END was missing.  |
 /*------------------------------------------------------------------------.
 | Report an unexpected EOF in a token or comment starting at START.       |
 | An end of file was encountered and the expected TOKEN_END was missing.  |
-| After reporting the problem, pretend that TOKEN_END was found.          |
 `------------------------------------------------------------------------*/
 
 static void
 `------------------------------------------------------------------------*/
 
 static void
-unexpected_end_of_file (boundary start, char const *token_end)
+unexpected_eof (boundary start, char const *token_end)
 {
 {
-  size_t i = strlen (token_end);
-
-  location_t location;
-  location.start = start;
-  location.end = scanner_cursor;
-  complain_at (location, _("missing `%s' at end of file"), token_end);
-
-  /* Adjust scanner cursor so that any later message does not count
-     the characters about to be inserted.  */
-  scanner_cursor.column -= i;
-
-  while (i != 0)
-    unput (token_end[--i]);
+  location loc;
+  loc.start = start;
+  loc.end = scanner_cursor;
+  complain_at (loc, _("missing `%s' at end of file"), token_end);
 }
 
 
 }
 
 
@@ -943,7 +993,7 @@ unexpected_end_of_file (boundary start, char const *token_end)
 void
 scanner_initialize (void)
 {
 void
 scanner_initialize (void)
 {
-  obstack_init (&string_obstack);
+  obstack_init (&obstack_for_string);
 }
 
 
 }
 
 
@@ -954,7 +1004,7 @@ scanner_initialize (void)
 void
 scanner_free (void)
 {
 void
 scanner_free (void)
 {
-  obstack_free (&string_obstack, 0);
+  obstack_free (&obstack_for_string, 0);
   /* Reclaim Flex's buffers.  */
   yy_delete_buffer (YY_CURRENT_BUFFER);
 }
   /* Reclaim Flex's buffers.  */
   yy_delete_buffer (YY_CURRENT_BUFFER);
 }