]> git.saurik.com Git - bison.git/blobdiff - src/scan-gram.l
* src/gram.h, src/gram.c (pure_parser, glr_parser): Move to...
[bison.git] / src / scan-gram.l
index a96b18b9ba8af7cc4b8646e838e329abacb07294..337f9db195872339e1bfb59be0f0b2bfc97c55b1 100644 (file)
@@ -1,5 +1,6 @@
 /* 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.
 
    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"
-#include "mbswidth.h"
+
+#include <mbswidth.h>
+#include <get-errno.h>
+#include <quote.h>
+
 #include "complain.h"
 #include "files.h"
-#include "quote.h"
-#include "struniq.h"
 #include "getargs.h"
 #include "gram.h"
 #include "reader.h"
+#include "uniqstr.h"
 
 #define YY_USER_INIT                                   \
   do                                                   \
       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;
 
-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))
 
 
-/* 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.
 
@@ -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.  */
 
-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   \
-  obstack_grow (&string_obstack, yytext, yyleng)
+  obstack_grow (&obstack_for_string, yytext, yyleng)
 
 #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 \
-  obstack_free (&string_obstack, last_string)
+  obstack_free (&obstack_for_string, last_string)
 
 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;
 
-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 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_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]|-)*
-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
@@ -130,15 +133,20 @@ splice     (\\[ \f\t\v]*\n)*
   /* 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_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.  |
   `-----------------------*/
 
-<INITIAL,SC_AFTER_IDENTIFIER>
+<INITIAL,SC_AFTER_IDENTIFIER,SC_PRE_CODE>
 {
   [ \f\n\t\v]  ;
+  ","         warn_at (*loc, _("stray `,' treated as white space"));
 
   /* 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.  */
@@ -171,7 +184,7 @@ splice       (\\[ \f\t\v]*\n)*
   "%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;
@@ -179,26 +192,27 @@ splice     (\\[ \f\t\v]*\n)*
   "%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;
   "%no"[-_]"lines"        return PERCENT_NO_LINES;
   "%nonassoc"             return PERCENT_NONASSOC;
+  "%nondeterministic-parser"   return PERCENT_NONDETERMINISTIC_PARSER;
   "%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;
-  "%printer"              return PERCENT_PRINTER;
+  "%printer"              token_type = PERCENT_PRINTER; BEGIN SC_PRE_CODE;
   "%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;
-  "%union"                return PERCENT_UNION;
+  "%union"               token_type = PERCENT_UNION; BEGIN SC_PRE_CODE;
   "%verbose"              return PERCENT_VERBOSE;
   "%yacc"                 return PERCENT_YACC;
 
@@ -210,10 +224,6 @@ splice      (\\[ \f\t\v]*\n)*
   "|"                     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;
@@ -223,9 +233,9 @@ splice       (\\[ \f\t\v]*\n)*
 
   {int} {
     unsigned long num;
-    errno = 0;
+    set_errno (0);
     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;
@@ -246,6 +256,7 @@ splice       (\\[ \f\t\v]*\n)*
   /* Code in between braces.  */
   "{" {
     STRING_GROW;
+    token_type = BRACED_CODE;
     braces_level = 0;
     code_start = loc->start;
     BEGIN SC_BRACED_CODE;
@@ -253,9 +264,9 @@ splice       (\\[ \f\t\v]*\n)*
 
   /* A type. */
   "<"{tag}">" {
-    obstack_grow (&string_obstack, yytext + 1, yyleng - 2);
+    obstack_grow (&obstack_for_string, yytext + 1, yyleng - 2);
     STRING_FINISH;
-    val->struniq = struniq_new (last_string);
+    val->uniqstr = uniqstr_new (last_string);
     STRING_FREE;
     return TYPE;
   }
@@ -263,16 +274,18 @@ splice     (\\[ \f\t\v]*\n)*
   "%%" {
     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));
   }
+
+  <<EOF>> {
+    loc->start = loc->end = scanner_cursor;
+    yyterminate ();
+  }
 }
 
 
@@ -311,7 +324,7 @@ splice       (\\[ \f\t\v]*\n)*
 {
   "*/"     BEGIN context_state;
   .|\n    ;
-  <<EOF>>  unexpected_end_of_file (token_start, "*/");
+  <<EOF>>  unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
@@ -322,7 +335,7 @@ splice       (\\[ \f\t\v]*\n)*
 <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 +362,14 @@ splice     (\\[ \f\t\v]*\n)*
     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;
-  <<EOF>>   unexpected_end_of_file (token_start, "\"");
+  <<EOF>>   unexpected_eof (token_start, "\""); BEGIN INITIAL;
 }
 
   /*---------------------------------------------------------------.
@@ -367,13 +380,14 @@ splice     (\\[ \f\t\v]*\n)*
 <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);
-    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;
@@ -381,7 +395,7 @@ splice       (\\[ \f\t\v]*\n)*
   }
 
   .|\n     STRING_GROW;
-  <<EOF>>   unexpected_end_of_file (token_start, "'");
+  <<EOF>>   unexpected_eof (token_start, "'"); BEGIN INITIAL;
 }
 
 
@@ -396,36 +410,36 @@ splice     (\\[ \f\t\v]*\n)*
     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;
-    errno = 0;
+    set_errno (0);
     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
-      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.  */
-  \\("\""|"'"|"?"|"\\")  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
-      obstack_1grow (&string_obstack, c);
+      obstack_1grow (&obstack_for_string, c);
   }
   \\(.|\n)     {
     complain_at (*loc, _("unrecognized escape sequence: %s"), quote (yytext));
@@ -443,7 +457,7 @@ splice       (\\[ \f\t\v]*\n)*
 {
   "'"                  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 +470,10 @@ splice      (\\[ \f\t\v]*\n)*
 {
   "\""                 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 +509,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.                                          |
@@ -502,16 +554,35 @@ splice     (\\[ \f\t\v]*\n)*
   "{"|"<"{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;
-       loc->start = code_start;
-       val->string = last_string;
        rule_length++;
+       loc->start = code_start;
+       val->chars = last_string;
        BEGIN INITIAL;
-       return BRACED_CODE;
+       return token_type;
       }
   }
 
@@ -519,12 +590,10 @@ splice     (\\[ \f\t\v]*\n)*
      (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 +606,12 @@ splice     (\\[ \f\t\v]*\n)*
   "%}" {
     STRING_FINISH;
     loc->start = code_start;
-    val->string = last_string;
+    val->chars = last_string;
     BEGIN INITIAL;
     return PROLOGUE;
   }
 
-  <<EOF>>  unexpected_end_of_file (code_start, "%}");
+  <<EOF>>  unexpected_eof (code_start, "%}"); BEGIN INITIAL;
 }
 
 
@@ -556,7 +625,7 @@ splice       (\\[ \f\t\v]*\n)*
   <<EOF>> {
     STRING_FINISH;
     loc->start = code_start;
-    val->string = last_string;
+    val->chars = last_string;
     BEGIN INITIAL;
     return EPILOGUE;
   }
@@ -570,10 +639,10 @@ splice     (\\[ \f\t\v]*\n)*
 
 <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;
 }
 
@@ -584,7 +653,7 @@ splice       (\\[ \f\t\v]*\n)*
    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;
@@ -624,14 +693,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)
 {
-  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;
-         char const *lim = buf + s;
+         char const *lim = buf + bytes_read;
 
          for (;;)
            {
@@ -660,7 +729,7 @@ no_cr_read (FILE *fp, char *buf, size_t size)
        }
     }
 
-  return s;
+  return bytes_read;
 }
 
 
@@ -669,15 +738,18 @@ no_cr_read (FILE *fp, char *buf, size_t size)
 |                                                                   |
 | 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;
 
+  if (! current_rule)
+    return false;
+
   /* Get the type name if explicit. */
   if (*cp == '<')
     {
@@ -691,127 +763,108 @@ handle_action_dollar (char *text, location_t location)
   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)
-       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 = "";
-      obstack_fgrow1 (&string_obstack,
+      obstack_fgrow1 (&obstack_for_string,
                      "]b4_lhs_value([%s])[", type_name);
     }
   else
     {
       long num;
-      errno = 0;
+      set_errno (0);
       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)
-           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)
-           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 = "";
-         obstack_fgrow3 (&string_obstack,
+         obstack_fgrow3 (&obstack_for_string,
                          "]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, |
-| depending upon CODE_KIND.                                        |
+| depending upon TOKEN_TYPE.                                       |
 `-----------------------------------------------------------------*/
 
 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;
 
-    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;
     }
+
+  complain_at (loc, _("invalid value: %s"), quote (text));
 }
 
 
 /*------------------------------------------------------.
 | 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;
 
+  if (! current_rule)
+    return false;
+
   if (*cp == '$')
-    {
-      obstack_sgrow (&string_obstack, "]b4_lhs_location[");
-    }
+    obstack_sgrow (&obstack_for_string, "]b4_lhs_location[");
   else
     {
       long num;
-      errno = 0;
+      set_errno (0);
       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;
-         obstack_fgrow2 (&string_obstack, "]b4_rhs_location([%d], [%d])[",
+         obstack_fgrow2 (&obstack_for_string, "]b4_rhs_location([%d], [%d])[",
                          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 +874,28 @@ handle_symbol_code_at (char *text, location_t location)
 `-------------------------------------------------------------------*/
 
 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;
 
-    case destructor_braced_code:
-    case printer_braced_code:
-      handle_symbol_code_at (text, location);
+    default:
       break;
     }
+
+  complain_at (loc, _("invalid value: %s"), quote (text));
 }
 
 
@@ -914,25 +975,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.  |
-| After reporting the problem, pretend that TOKEN_END was found.          |
 `------------------------------------------------------------------------*/
 
 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 +994,7 @@ unexpected_end_of_file (boundary start, char const *token_end)
 void
 scanner_initialize (void)
 {
-  obstack_init (&string_obstack);
+  obstack_init (&obstack_for_string);
 }
 
 
@@ -954,7 +1005,7 @@ scanner_initialize (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);
 }