]> git.saurik.com Git - bison.git/blobdiff - src/scan-gram.l
* NEWS: Bison now warns if it finds a stray `$' or `@' in an action.
[bison.git] / src / scan-gram.l
index 9670687305f20b31adbd8dea2dc6e5caddb01638..3c7d37c484d5932102804351b097cce301058175 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, 2004, 2005 Free Software Foundation, Inc.
 
    This file is part of Bison, the GNU Compiler Compiler.
 
 
    This file is part of Bison, the GNU Compiler Compiler.
 
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307  USA
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301  USA
 */
 
 */
 
-%option debug nodefault noyywrap nounput never-interactive stack
+%option debug nodefault nounput noyywrap never-interactive
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
+/* Work around a bug in flex 2.5.31.  See Debian bug 333231
+   <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>.  */
+#undef gram_wrap
+#define gram_wrap() 1
+
 #include "system.h"
 #include "system.h"
-#include "mbswidth.h"
+
+#include <mbswidth.h>
+#include <get-errno.h>
+#include <quote.h>
+
 #include "complain.h"
 #include "complain.h"
-#include "quote.h"
+#include "files.h"
 #include "getargs.h"
 #include "gram.h"
 #include "getargs.h"
 #include "gram.h"
+#include "quotearg.h"
 #include "reader.h"
 #include "reader.h"
-
-/* Each time we match a string, move the end cursor to its end. */
-#define YY_USER_INIT                           \
-do {                                           \
-  LOCATION_RESET (*yylloc);                    \
-  yylloc->file = infile;                       \
-   /* This is only to avoid GCC warnings. */   \
-  if (yycontrol) {;};                          \
-} while (0)
-
-#define YY_USER_ACTION  extend_location (yylloc, yytext, yyleng);
-#define YY_STEP         LOCATION_STEP (*yylloc)
-
+#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)
+
+/* Pacify "gcc -Wmissing-prototypes" when flex 2.5.31 is used.  */
+int gram_get_lineno (void);
+FILE *gram_get_in (void);
+FILE *gram_get_out (void);
+int gram_get_leng (void);
+char *gram_get_text (void);
+void gram_set_lineno (int);
+void gram_set_in (FILE *);
+void gram_set_out (FILE *);
+int gram_get_debug (void);
+void gram_set_debug (int);
+int gram_lex_destroy (void);
+
+/* Location of scanner cursor.  */
+boundary scanner_cursor;
+
+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_INPUT(buf, result, size) ((result) = no_cr_read (yyin, buf, size))
 
 
-/* Read bytes from FP into buffer BUF of size SIZE.  Return the
-   number of bytes read.  Remove '\r' from input, treating \r\n
-   and isolated \r as \n.  */
-
-static size_t
-no_cr_read (FILE *fp, char *buf, size_t size)
-{
-  size_t s = fread (buf, 1, size, fp);
-  if (s)
-    {
-      char *w = memchr (buf, '\r', s);
-      if (w)
-       {
-         char const *r = ++w;
-         char const *lim = buf + s;
-
-         for (;;)
-           {
-             /* Found an '\r'.  Treat it like '\n', but ignore any
-                '\n' that immediately follows.  */
-             w[-1] = '\n';
-             if (r == lim)
-               {
-                 int ch = getc (fp);
-                 if (ch != '\n' && ungetc (ch, fp) != ch)
-                   break;
-               }
-             else if (*r == '\n')
-               r++;
-
-             /* Copy until the next '\r'.  */
-             do
-               {
-                 if (r == lim)
-                   return w - buf;
-               }
-             while ((*w++ = *r++) != '\r');
-           }
-
-         return w - buf;
-       }
-    }
-
-  return s;
-}
-
-
-/* Extend *LOC to account for token TOKEN of size SIZE.  */
-
-static void
-extend_location (location_t *loc, char const *token, int size)
-{
-  int line = loc->last_line;
-  int column = loc->last_column;
-  char const *p0 = token;
-  char const *p = token;
-  char const *lim = token + size;
-
-  for (p = token; p < lim; p++)
-    switch (*p)
-      {
-      case '\r':
-       /* \r shouldn't survive no_cr_read.  */
-       abort ();
-
-      case '\n':
-       line++;
-       column = 1;
-       p0 = p + 1;
-       break;
-
-      case '\t':
-       column += mbsnwidth (p0, p - p0, 0);
-       column += 8 - ((column - 1) & 7);
-       p0 = p + 1;
-       break;
-      }
-
-  loc->last_line = line;
-  loc->last_column = column + mbsnwidth (p0, p - p0, 0);
-}
-
-
-
-/* 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.
 
-   Use YY_OBS_GROW to append what has just been matched, and
-   YY_OBS_FINISH to end the string (it puts the ending 0).
-   YY_OBS_FINISH also stores this string in LAST_STRING, which can be
-   used, and which is used by YY_OBS_FREE to free the last string.  */
+   Use STRING_GROW to append what has just been matched, and
+   STRING_FINISH to end the string (it puts the ending 0).
+   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 obstack_for_string;
+
+/* A string representing the most recently saved token.  */
+static char *last_string;
 
 
-static struct obstack string_obstack;
-char *last_string;
 
 
-#define YY_OBS_GROW   \
-  obstack_grow (&string_obstack, yytext, yyleng)
+#define STRING_GROW   \
+  obstack_grow (&obstack_for_string, yytext, yyleng)
 
 
-#define YY_OBS_FINISH                                  \
+#define STRING_FINISH                                  \
   do {                                                 \
   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)
 
   } while (0)
 
-#define YY_OBS_FREE                                            \
-  do {                                                         \
-    obstack_free (&string_obstack, last_string);               \
-  } while (0)
+#define STRING_FREE \
+  obstack_free (&obstack_for_string, last_string)
 
 void
 scanner_last_string_free (void)
 {
 
 void
 scanner_last_string_free (void)
 {
-  YY_OBS_FREE;
+  STRING_FREE;
 }
 
 }
 
-
-static int percent_percent_count = 0;
-
 /* Within well-formed rules, RULE_LENGTH is the number of values in
    the current rule so far, which says where to find `$0' with respect
    to the top of the stack.  It is not the same as the rule->length in
 /* Within well-formed rules, RULE_LENGTH is the number of values in
    the current rule so far, which says where to find `$0' with respect
    to the top of the stack.  It is not the same as the rule->length in
@@ -173,23 +117,25 @@ static int percent_percent_count = 0;
    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_syncline (char *args, 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 unsigned long int scan_integer (char const *p, int base, location loc);
 static int convert_ucn_to_byte (char const *hex_text);
 static int convert_ucn_to_byte (char const *hex_text);
+static void unexpected_eof (boundary, char const *);
+static void unexpected_newline (boundary, char const *);
 
 %}
 %x SC_COMMENT SC_LINE_COMMENT SC_YACC_COMMENT
 %x SC_STRING SC_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_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
@@ -202,14 +148,53 @@ splice     (\\[ \f\t\v]*\n)*
 
 %%
 %{
 
 %%
 %{
+  /* Nesting level of the current code in braces.  */
   int braces_level IF_LINT (= 0);
 
   int braces_level IF_LINT (= 0);
 
-  /* At each yylex invocation, mark the current position as the
-     start of the next token.  */
-  YY_STEP;
+  /* 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 id_loc IF_LINT (= empty_location);
+
+  /* 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;
+
+  /* Where containing comment or string or character literal started,
+     when applicable.  */
+  boundary token_start IF_LINT (= scanner_cursor);
 %}
 
 
 %}
 
 
+  /*-----------------------.
+  | Scanning white space.  |
+  `-----------------------*/
+
+<INITIAL,SC_AFTER_IDENTIFIER,SC_PRE_CODE>
+{
+  /* Comments and white space.  */
+  ","         warn_at (*loc, _("stray `,' treated as white space"));
+  [ \f\n\t\v]  |
+  "//".*       ;
+  "/*" {
+    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 "{int}" \"".*"\"\n" {
+    handle_syncline (yytext + sizeof "#line " - 1);
+  }
+}
+
+
   /*----------------------------.
   | Scanning Bison directives.  |
   `----------------------------*/
   /*----------------------------.
   | Scanning Bison directives.  |
   `----------------------------*/
@@ -217,121 +202,138 @@ splice   (\\[ \f\t\v]*\n)*
 {
   "%binary"               return PERCENT_NONASSOC;
   "%debug"                return PERCENT_DEBUG;
 {
   "%binary"               return PERCENT_NONASSOC;
   "%debug"                return PERCENT_DEBUG;
+  "%default"[-_]"prec"    return PERCENT_DEFAULT_PREC;
   "%define"               return PERCENT_DEFINE;
   "%defines"              return PERCENT_DEFINES;
   "%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;
+  "%expect"[-_]"rr"      return PERCENT_EXPECT_RR;
   "%file-prefix"          return PERCENT_FILE_PREFIX;
   "%fixed"[-_]"output"[-_]"files"   return PERCENT_YACC;
   "%file-prefix"          return PERCENT_FILE_PREFIX;
   "%fixed"[-_]"output"[-_]"files"   return PERCENT_YACC;
+  "%initial-action"       token_type = PERCENT_INITIAL_ACTION; BEGIN SC_PRE_CODE;
   "%glr-parser"           return PERCENT_GLR_PARSER;
   "%left"                 return PERCENT_LEFT;
   "%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;
+  "%no"[-_]"default"[-_]"prec" return PERCENT_NO_DEFAULT_PREC;
   "%no"[-_]"lines"        return PERCENT_NO_LINES;
   "%nonassoc"             return PERCENT_NONASSOC;
   "%no"[-_]"lines"        return PERCENT_NO_LINES;
   "%nonassoc"             return PERCENT_NONASSOC;
+  "%nondeterministic-parser"   return PERCENT_NONDETERMINISTIC_PARSER;
   "%nterm"                return PERCENT_NTERM;
   "%output"               return PERCENT_OUTPUT;
   "%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;
   "%pure"[-_]"parser"     return PERCENT_PURE_PARSER;
+  "%require"              return PERCENT_REQUIRE;
   "%right"                return PERCENT_RIGHT;
   "%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;
 
-  {directive}             {
-    complain_at (*yylloc, _("invalid directive: %s"), quote (yytext));
-    YY_STEP;
+  {directive} {
+    complain_at (*loc, _("invalid directive: %s"), quote (yytext));
   }
 
   }
 
-  ^"#line "{int}" \""[^\"]*"\"\n"   handle_syncline (yytext + strlen ("#line "), yylloc); YY_STEP;
-
   "="                     return EQUAL;
   "="                     return EQUAL;
-  ":"                     rule_length = 0; return COLON;
   "|"                     rule_length = 0; return PIPE;
   "|"                     rule_length = 0; return PIPE;
-  ","                     return COMMA;
   ";"                     return SEMICOLON;
 
   ";"                     return SEMICOLON;
 
-  [ \f\n\t\v]+  YY_STEP;
-
-  {id}        {
-    yylval->symbol = symbol_get (yytext, *yylloc);
+  {id} {
+    val->symbol = symbol_get (yytext, *loc);
+    id_loc = *loc;
     rule_length++;
     rule_length++;
-    return ID;
+    BEGIN SC_AFTER_IDENTIFIER;
   }
 
   {int} {
   }
 
   {int} {
-    unsigned long num;
-    errno = 0;
-    num = strtoul (yytext, 0, 10);
-    if (INT_MAX < num || errno)
-      {
-       complain_at (*yylloc, _("integer out of range: %s"), quote (yytext));
-       num = INT_MAX;
-      }
-    yylval->integer = num;
+    val->integer = scan_integer (yytext, 10, *loc);
+    return INT;
+  }
+  0[xX][0-9abcdefABCDEF]+ {
+    val->integer = scan_integer (yytext, 16, *loc);
     return INT;
   }
 
   /* Characters.  We don't check there is only one.  */
     return INT;
   }
 
   /* Characters.  We don't check there is only one.  */
-  "'"         YY_OBS_GROW; yy_push_state (SC_ESCAPED_CHARACTER);
+  "'"        STRING_GROW; token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
 
   /* Strings. */
 
   /* Strings. */
-  "\""        YY_OBS_GROW; yy_push_state (SC_ESCAPED_STRING);
-
-  /* Comments. */
-  "/*"        BEGIN SC_YACC_COMMENT;
-  "//".*      YY_STEP;
+  "\""       token_start = loc->start; BEGIN SC_ESCAPED_STRING;
 
   /* Prologue. */
 
   /* Prologue. */
-  "%{"        yy_push_state (SC_PROLOGUE);
+  "%{"        code_start = loc->start; BEGIN SC_PROLOGUE;
 
   /* Code in between braces.  */
 
   /* Code in between braces.  */
-  "{"         YY_OBS_GROW; braces_level = 0; yy_push_state (SC_BRACED_CODE);
+  "{" {
+    STRING_GROW;
+    token_type = BRACED_CODE;
+    braces_level = 0;
+    code_start = loc->start;
+    BEGIN SC_BRACED_CODE;
+  }
 
   /* A type. */
   "<"{tag}">" {
 
   /* A type. */
   "<"{tag}">" {
-    obstack_grow (&string_obstack, yytext + 1, yyleng - 2);
-    YY_OBS_FINISH;
-    yylval->string = last_string;
+    obstack_grow (&obstack_for_string, yytext + 1, yyleng - 2);
+    STRING_FINISH;
+    val->uniqstr = uniqstr_new (last_string);
+    STRING_FREE;
     return TYPE;
   }
 
     return TYPE;
   }
 
-
-  "%%"   {
+  "%%" {
+    static int percent_percent_count;
     if (++percent_percent_count == 2)
     if (++percent_percent_count == 2)
-      yy_push_state (SC_EPILOGUE);
+      BEGIN SC_EPILOGUE;
     return PERCENT_PERCENT;
   }
 
     return PERCENT_PERCENT;
   }
 
-  .           {
-    complain_at (*yylloc, _("invalid character: %s"), quote (yytext));
-    YY_STEP;
+  . {
+    complain_at (*loc, _("invalid character: %s"), quote (yytext));
+  }
+
+  <<EOF>> {
+    loc->start = loc->end = scanner_cursor;
+    yyterminate ();
   }
 }
 
 
   }
 }
 
 
-  /*-------------------------------------------------------------------.
-  | Whatever the start condition (but those which correspond to        |
-  | entities `swallowed' by Bison: SC_YACC_COMMENT, SC_ESCAPED_STRING, |
-  | and SC_ESCAPED_CHARACTER), no M4 character must escape as is.      |
-  `-------------------------------------------------------------------*/
+  /*-----------------------------------------------------------------.
+  | Scanning after an identifier, checking whether a colon is next.  |
+  `-----------------------------------------------------------------*/
 
 
-<SC_COMMENT,SC_LINE_COMMENT,SC_STRING,SC_CHARACTER,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
+<SC_AFTER_IDENTIFIER>
 {
 {
-  \[   obstack_sgrow (&string_obstack, "@<:@");
-  \]   obstack_sgrow (&string_obstack, "@:>@");
+  ":" {
+    rule_length = 0;
+    *loc = id_loc;
+    BEGIN INITIAL;
+    return ID_COLON;
+  }
+  . {
+    scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);
+    yyless (0);
+    *loc = id_loc;
+    BEGIN INITIAL;
+    return ID;
+  }
+  <<EOF>> {
+    *loc = id_loc;
+    BEGIN INITIAL;
+    return ID;
+  }
 }
 
 
 }
 
 
@@ -341,17 +343,9 @@ splice      (\\[ \f\t\v]*\n)*
 
 <SC_YACC_COMMENT>
 {
 
 <SC_YACC_COMMENT>
 {
-  "*/" {
-    YY_STEP;
-    BEGIN INITIAL;
-  }
-
-  [^*]+|"*"  ;
-
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a comment"));
-    BEGIN INITIAL;
-  }
+  "*/"     BEGIN context_state;
+  .|\n    ;
+  <<EOF>>  unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
 }
 
 
@@ -361,13 +355,8 @@ splice      (\\[ \f\t\v]*\n)*
 
 <SC_COMMENT>
 {
 
 <SC_COMMENT>
 {
-  "*"{splice}"/"  YY_OBS_GROW; yy_pop_state ();
-  [^*\[\]]+|"*"   YY_OBS_GROW;
-
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a comment"));
-    yy_pop_state ();
-  }
+  "*"{splice}"/"  STRING_GROW; BEGIN context_state;
+  <<EOF>>        unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
 }
 
 
@@ -377,74 +366,61 @@ splice     (\\[ \f\t\v]*\n)*
 
 <SC_LINE_COMMENT>
 {
 
 <SC_LINE_COMMENT>
 {
-  "\n"                  YY_OBS_GROW; yy_pop_state ();
-  ([^\n\[\]]|{splice})+  YY_OBS_GROW;
-  <<EOF>>               yy_pop_state ();
+  "\n"          STRING_GROW; BEGIN context_state;
+  {splice}      STRING_GROW;
+  <<EOF>>       BEGIN context_state;
 }
 
 
 }
 
 
-  /*----------------------------------------------------------------.
-  | Scanning a C string, including its escapes.  The initial `"' is |
-  | already eaten.                                                  |
-  `----------------------------------------------------------------*/
+  /*------------------------------------------------.
+  | Scanning a Bison string, including its escapes. |
+  | The initial quote is already eaten.             |
+  `------------------------------------------------*/
 
 <SC_ESCAPED_STRING>
 {
   "\"" {
 
 <SC_ESCAPED_STRING>
 {
   "\"" {
-    assert (yy_top_state () == INITIAL);
-    YY_OBS_GROW;
-    YY_OBS_FINISH;
-    yylval->string = last_string;
-    yy_pop_state ();
+    STRING_FINISH;
+    loc->start = token_start;
+    val->chars = last_string;
     rule_length++;
     rule_length++;
+    BEGIN INITIAL;
     return STRING;
   }
     return STRING;
   }
-
-  [^\"\\]+  YY_OBS_GROW;
-
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a string"));
-    assert (yy_top_state () == INITIAL);
-    YY_OBS_FINISH;
-    yylval->string = last_string;
-    yy_pop_state ();
-    return STRING;
-  }
+  \n           unexpected_newline (token_start, "\""); BEGIN INITIAL;
+  <<EOF>>      unexpected_eof (token_start, "\"");     BEGIN INITIAL;
 }
 
 }
 
-  /*---------------------------------------------------------------.
-  | Scanning a C character, decoding its escapes.  The initial "'" |
-  | is already eaten.                                              |
-  `---------------------------------------------------------------*/
+  /*----------------------------------------------------------.
+  | Scanning a Bison character literal, decoding its escapes. |
+  | The initial quote is already eaten.                              |
+  `----------------------------------------------------------*/
 
 <SC_ESCAPED_CHARACTER>
 {
   "'" {
 
 <SC_ESCAPED_CHARACTER>
 {
   "'" {
-    YY_OBS_GROW;
-    assert (yy_top_state () == INITIAL);
-    {
-      YY_OBS_FINISH;
-      yylval->symbol = symbol_get (last_string, *yylloc);
-      symbol_class_set (yylval->symbol, token_sym, *yylloc);
-      symbol_user_token_number_set (yylval->symbol,
-                                   (unsigned char) last_string[1], *yylloc);
-      YY_OBS_FREE;
-      yy_pop_state ();
-      rule_length++;
-      return ID;
-    }
+    unsigned char last_string_1;
+    STRING_GROW;
+    STRING_FINISH;
+    loc->start = token_start;
+    val->symbol = symbol_get (quotearg_style (escape_quoting_style,
+                                             last_string),
+                             *loc);
+    symbol_class_set (val->symbol, token_sym, *loc);
+    last_string_1 = last_string[1];
+    symbol_user_token_number_set (val->symbol, last_string_1, *loc);
+    STRING_FREE;
+    rule_length++;
+    BEGIN INITIAL;
+    return ID;
   }
   }
+  \n           unexpected_newline (token_start, "'");  BEGIN INITIAL;
+  <<EOF>>      unexpected_eof (token_start, "'");      BEGIN INITIAL;
+}
 
 
-  [^\'\\]+  YY_OBS_GROW;
-
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a character literal"));
-    assert (yy_top_state () == INITIAL);
-    YY_OBS_FINISH;
-    yylval->string = last_string;
-    yy_pop_state ();
-    return CHARACTER;
-  }
+<SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING>
+{
+  \0       complain_at (*loc, _("invalid null character"));
 }
 
 
 }
 
 
@@ -455,111 +431,74 @@ splice    (\\[ \f\t\v]*\n)*
 <SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>
 {
   \\[0-7]{1,3} {
 <SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>
 {
   \\[0-7]{1,3} {
-    unsigned long c = strtoul (yytext + 1, 0, 8);
+    unsigned long int c = strtoul (yytext + 1, 0, 8);
     if (UCHAR_MAX < c)
     if (UCHAR_MAX < c)
-      {
-       complain_at (*yylloc, _("invalid escape sequence: %s"),
-                    quote (yytext));
-       YY_STEP;
-      }
+      complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
+    else if (! c)
+      complain_at (*loc, _("invalid null character: %s"), quote (yytext));
     else
     else
-      obstack_1grow (&string_obstack, c);
+      obstack_1grow (&obstack_for_string, c);
   }
 
   }
 
-  \\x[0-9a-fA-F]+ {
-    unsigned long c;
-    errno = 0;
+  \\x[0-9abcdefABCDEF]+ {
+    unsigned long int c;
+    set_errno (0);
     c = strtoul (yytext + 2, 0, 16);
     c = strtoul (yytext + 2, 0, 16);
-    if (UCHAR_MAX < c || errno)
-      {
-       complain_at (*yylloc, _("invalid escape sequence: %s"),
-                    quote (yytext));
-       YY_STEP;
-      }
+    if (UCHAR_MAX < c || get_errno ())
+      complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
+    else if (! c)
+      complain_at (*loc, _("invalid null character: %s"), quote (yytext));
     else
     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');
-  \\[\"\'?\\]  obstack_1grow (&string_obstack, yytext[1]);
-  \\(u|U[0-9a-fA-F]{4})[0-9a-fA-F]{4} {
+  \\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 (&obstack_for_string, yytext[1]);
+
+  \\(u|U[0-9abcdefABCDEF]{4})[0-9abcdefABCDEF]{4} {
     int c = convert_ucn_to_byte (yytext);
     if (c < 0)
     int c = convert_ucn_to_byte (yytext);
     if (c < 0)
-      {
-       complain_at (*yylloc, _("invalid escape sequence: %s"),
-                    quote (yytext));
-       YY_STEP;
-      }
+      complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
+    else if (! c)
+      complain_at (*loc, _("invalid null character: %s"), quote (yytext));
     else
     else
-      obstack_1grow (&string_obstack, c);
+      obstack_1grow (&obstack_for_string, c);
   }
   \\(.|\n)     {
   }
   \\(.|\n)     {
-    complain_at (*yylloc, _("unrecognized escape sequence: %s"),
-                quote (yytext));
-    YY_OBS_GROW;
+    complain_at (*loc, _("unrecognized escape sequence: %s"), quote (yytext));
+    STRING_GROW;
   }
   }
-  /* FLex wants this rule, in case of a `\<<EOF>>'. */
-  \\                   YY_OBS_GROW;
 }
 
 }
 
+  /*--------------------------------------------.
+  | Scanning user-code characters and strings.  |
+  `--------------------------------------------*/
 
 
-  /*----------------------------------------------------------.
-  | Scanning a C character without decoding its escapes.  The |
-  | initial "'" is already eaten.                             |
-  `----------------------------------------------------------*/
+<SC_CHARACTER,SC_STRING>
+{
+  {splice}|\\{splice}[^\n$@\[\]]       STRING_GROW;
+}
 
 <SC_CHARACTER>
 {
 
 <SC_CHARACTER>
 {
-  "'" {
-    YY_OBS_GROW;
-    assert (yy_top_state () != INITIAL);
-    yy_pop_state ();
-  }
-
-  [^'\[\]\\]+         YY_OBS_GROW;
-  \\{splice}[^\[\]]    YY_OBS_GROW;
-  {splice}            YY_OBS_GROW;
-  /* Needed for `\<<EOF>>', `\\<<newline>>[', and `\\<<newline>>]'.  */
-  \\                   YY_OBS_GROW;
-
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a character literal"));
-    assert (yy_top_state () != INITIAL);
-    yy_pop_state ();
-  }
+  "'"          STRING_GROW; BEGIN context_state;
+  \n           unexpected_newline (token_start, "'"); BEGIN context_state;
+  <<EOF>>      unexpected_eof (token_start, "'"); BEGIN context_state;
 }
 
 }
 
-
-  /*----------------------------------------------------------------.
-  | Scanning a C string, without decoding its escapes.  The initial |
-  | `"' is already eaten.                                           |
-  `----------------------------------------------------------------*/
-
 <SC_STRING>
 {
 <SC_STRING>
 {
-  "\"" {
-    assert (yy_top_state () != INITIAL);
-    YY_OBS_GROW;
-    yy_pop_state ();
-  }
-
-  [^\"\[\]\\]+        YY_OBS_GROW;
-  \\{splice}[^\[\]]    YY_OBS_GROW;
-  {splice}            YY_OBS_GROW;
-  /* Needed for `\<<EOF>>', `\\<<newline>>[', and `\\<<newline>>]'.  */
-  \\                   YY_OBS_GROW;
-
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a string"));
-    assert (yy_top_state () != INITIAL);
-    yy_pop_state ();
-  }
+  "\""         STRING_GROW; BEGIN context_state;
+  \n           unexpected_newline (token_start, "\""); BEGIN context_state;
+  <<EOF>>      unexpected_eof (token_start, "\""); BEGIN context_state;
 }
 
 
 }
 
 
@@ -569,18 +508,64 @@ splice     (\\[ \f\t\v]*\n)*
 
 <SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
 {
 
 <SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
 {
-  /* Characters.  We don't check there is only one.  */
-  "'"         YY_OBS_GROW; yy_push_state (SC_CHARACTER);
+  "'" {
+    STRING_GROW;
+    context_state = YY_START;
+    token_start = loc->start;
+    BEGIN SC_CHARACTER;
+  }
+  "\"" {
+    STRING_GROW;
+    context_state = YY_START;
+    token_start = loc->start;
+    BEGIN SC_STRING;
+  }
+  "/"{splice}"*" {
+    STRING_GROW;
+    context_state = YY_START;
+    token_start = loc->start;
+    BEGIN SC_COMMENT;
+  }
+  "/"{splice}"/" {
+    STRING_GROW;
+    context_state = YY_START;
+    BEGIN SC_LINE_COMMENT;
+  }
+}
 
 
-  /* Strings. */
-  "\""        YY_OBS_GROW; yy_push_state (SC_STRING);
 
 
-  /* Comments. */
-  "/"{splice}"*"  YY_OBS_GROW; yy_push_state (SC_COMMENT);
-  "/"{splice}"/"  YY_OBS_GROW; yy_push_state (SC_LINE_COMMENT);
+  /*---------------------------------------------------------------.
+  | 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;
+      }
+  }
 
 
-  /* Not comments. */
-  "/"         YY_OBS_GROW;
+  <<EOF>>  unexpected_eof (scanner_cursor, "{}"); BEGIN INITIAL;
 }
 
 
 }
 
 
@@ -591,38 +576,58 @@ splice     (\\[ \f\t\v]*\n)*
 
 <SC_BRACED_CODE>
 {
 
 <SC_BRACED_CODE>
 {
-  "{"|"<"{splice}"%"  YY_OBS_GROW; braces_level++;
-  "%"{splice}">"      YY_OBS_GROW; braces_level--;
+  "{"|"<"{splice}"%"  STRING_GROW; braces_level++;
+  "%"{splice}">"      STRING_GROW; braces_level--;
   "}" {
   "}" {
-    YY_OBS_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)
       {
       {
-       yy_pop_state ();
-       YY_OBS_FINISH;
-       yylval->string = last_string;
+       STRING_FINISH;
        rule_length++;
        rule_length++;
-       return BRACED_CODE;
+       loc->start = code_start;
+       val->chars = last_string;
+       BEGIN INITIAL;
+       return token_type;
       }
   }
 
       }
   }
 
-  "$"("<"{tag}">")?(-?[0-9]+|"$") { handle_dollar (current_braced_code,
-                                                  yytext, *yylloc); }
-  "@"(-?[0-9]+|"$")               { handle_at (current_braced_code,
-                                              yytext, *yylloc); }
+  /* Tokenize `<<%' correctly (as `<<' `%') rather than incorrrectly
+     (as `<' `<%').  */
+  "<"{splice}"<"  STRING_GROW;
 
 
-  /* `"<"{splice}"<"' tokenizes `<<%' correctly (as `<<' `%') rather
-     than incorrrectly (as `<' `<%').  */
-  [^\"$%\'/<@\[\]\{\}]+|[$%/<@]|"<"{splice}"<"  YY_OBS_GROW;
+  "$"("<"{tag}">")?(-?[0-9]+|"$")  handle_dollar (token_type, yytext, *loc);
+  "@"(-?[0-9]+|"$")               handle_at (token_type, yytext, *loc);
 
 
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in `{ ... }'"));
-    yy_pop_state ();
-    YY_OBS_FINISH;
-    yylval->string = last_string;
-    return BRACED_CODE;
+  "$"  {
+    warn_at (*loc, _("stray `$'"));
+    obstack_sgrow (&obstack_for_string, "$][");
+  }
+  "@"  {
+    warn_at (*loc, _("stray `@'"));
+    obstack_sgrow (&obstack_for_string, "@@");
   }
 
   }
 
+  <<EOF>>  unexpected_eof (code_start, "}"); BEGIN INITIAL;
 }
 
 
 }
 
 
@@ -633,22 +638,14 @@ splice     (\\[ \f\t\v]*\n)*
 <SC_PROLOGUE>
 {
   "%}" {
 <SC_PROLOGUE>
 {
   "%}" {
-    yy_pop_state ();
-    YY_OBS_FINISH;
-    yylval->string = last_string;
+    STRING_FINISH;
+    loc->start = code_start;
+    val->chars = last_string;
+    BEGIN INITIAL;
     return PROLOGUE;
   }
 
     return PROLOGUE;
   }
 
-  [^%\[\]/\'\"]+     YY_OBS_GROW;
-  "%"                YY_OBS_GROW;
-
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in `%%{ ... %%}'"));
-    yy_pop_state ();
-    YY_OBS_FINISH;
-    yylval->string = last_string;
-    return PROLOGUE;
-  }
+  <<EOF>>  unexpected_eof (code_start, "%}"); BEGIN INITIAL;
 }
 
 
 }
 
 
@@ -659,33 +656,144 @@ splice    (\\[ \f\t\v]*\n)*
 
 <SC_EPILOGUE>
 {
 
 <SC_EPILOGUE>
 {
-  [^\[\]]+  YY_OBS_GROW;
-
   <<EOF>> {
   <<EOF>> {
-    yy_pop_state ();
-    YY_OBS_FINISH;
-    yylval->string = last_string;
+    STRING_FINISH;
+    loc->start = code_start;
+    val->chars = last_string;
+    BEGIN INITIAL;
     return EPILOGUE;
   }
 }
 
 
     return EPILOGUE;
   }
 }
 
 
+  /*-----------------------------------------.
+  | Escape M4 quoting characters in C code.  |
+  `-----------------------------------------*/
+
+<SC_COMMENT,SC_LINE_COMMENT,SC_STRING,SC_CHARACTER,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
+{
+  \$   obstack_sgrow (&obstack_for_string, "$][");
+  \@   obstack_sgrow (&obstack_for_string, "@@");
+  \[   obstack_sgrow (&obstack_for_string, "@{");
+  \]   obstack_sgrow (&obstack_for_string, "@}");
+}
+
+
+  /*-----------------------------------------------------.
+  | By default, grow the string obstack with the input.  |
+  `-----------------------------------------------------*/
+
+<SC_COMMENT,SC_LINE_COMMENT,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE,SC_STRING,SC_CHARACTER,SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>.     |
+<SC_COMMENT,SC_LINE_COMMENT,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>\n  STRING_GROW;
+
 %%
 
 %%
 
+/* Keeps track of the maximum number of semantic values to the left of
+   a handle (those referenced by $0, $-1, etc.) are required by the
+   semantic actions of this grammar. */
+int max_left_semantic_context = 0;
+
+/* Set *LOC and adjust scanner cursor to account for token TOKEN of
+   size SIZE.  */
+
+static void
+adjust_location (location *loc, char const *token, size_t size)
+{
+  int line = scanner_cursor.line;
+  int column = scanner_cursor.column;
+  char const *p0 = token;
+  char const *p = token;
+  char const *lim = token + size;
+
+  loc->start = scanner_cursor;
+
+  for (p = token; p < lim; p++)
+    switch (*p)
+      {
+      case '\n':
+       line++;
+       column = 1;
+       p0 = p + 1;
+       break;
+
+      case '\t':
+       column += mbsnwidth (p0, p - p0, 0);
+       column += 8 - ((column - 1) & 7);
+       p0 = p + 1;
+       break;
+      }
+
+  scanner_cursor.line = line;
+  scanner_cursor.column = column + mbsnwidth (p0, p - p0, 0);
+
+  loc->end = scanner_cursor;
+}
+
+
+/* Read bytes from FP into buffer BUF of size SIZE.  Return the
+   number of bytes read.  Remove '\r' from input, treating \r\n
+   and isolated \r as \n.  */
+
+static size_t
+no_cr_read (FILE *fp, char *buf, size_t size)
+{
+  size_t bytes_read = fread (buf, 1, size, fp);
+  if (bytes_read)
+    {
+      char *w = memchr (buf, '\r', bytes_read);
+      if (w)
+       {
+         char const *r = ++w;
+         char const *lim = buf + bytes_read;
+
+         for (;;)
+           {
+             /* Found an '\r'.  Treat it like '\n', but ignore any
+                '\n' that immediately follows.  */
+             w[-1] = '\n';
+             if (r == lim)
+               {
+                 int ch = getc (fp);
+                 if (ch != '\n' && ungetc (ch, fp) != ch)
+                   break;
+               }
+             else if (*r == '\n')
+               r++;
+
+             /* Copy until the next '\r'.  */
+             do
+               {
+                 if (r == lim)
+                   return w - buf;
+               }
+             while ((*w++ = *r++) != '\r');
+           }
+
+         return w - buf;
+       }
+    }
+
+  return bytes_read;
+}
+
+
 /*------------------------------------------------------------------.
 | TEXT is pointing to a wannabee semantic value (i.e., a `$').      |
 |                                                                   |
 | Possible inputs: $[<TYPENAME>]($|integer)                         |
 |                                                                   |
 /*------------------------------------------------------------------.
 | TEXT is pointing to a wannabee semantic value (i.e., a `$').      |
 |                                                                   |
 | 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 == '<')
     {
@@ -699,150 +807,162 @@ 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
     {
                      "]b4_lhs_value([%s])[", type_name);
     }
   else
     {
-      long num;
-      errno = 0;
+      long int num;
+      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;
+         if (1-n > max_left_semantic_context)
+           max_left_semantic_context = 1-n;
          if (!type_name && n > 0)
          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,
-                         "]b4_rhs_value([%d], [%d], [%s])[",
+         obstack_fgrow3 (&obstack_for_string,
+                         "]b4_rhs_value(%d, %d, [%s])[",
                          rule_length, n, type_name);
        }
       else
                          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.                                        |
-`-----------------------------------------------------------------*/
+/*----------------------------------------------------------------.
+| Map `$?' onto the proper M4 symbol, depending on its TOKEN_TYPE |
+| (are we in an action?).                                         |
+`----------------------------------------------------------------*/
 
 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;
+
+    case PERCENT_DESTRUCTOR:
+    case PERCENT_INITIAL_ACTION:
+    case PERCENT_PRINTER:
+      if (text[1] == '$')
+       {
+         obstack_sgrow (&obstack_for_string, "]b4_dollar_dollar[");
+         return;
+       }
       break;
 
       break;
 
-    case destructor_braced_code:
-    case printer_braced_code:
-      handle_symbol_code_dollar (text, location);
+    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;
 {
   char *cp = text + 1;
-  locations_flag = 1;
+  locations_flag = true;
+
+  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
     {
   else
     {
-      long num;
-      errno = 0;
+      long int num;
+      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;
 }
 
 
 }
 
 
-/*-------------------------------------------------------------------.
-| Dispatch onto handle_action_at, or handle_destructor_at, depending |
-| upon CODE_KIND.                                                    |
-`-------------------------------------------------------------------*/
+/*----------------------------------------------------------------.
+| Map `@?' onto the proper M4 symbol, depending on its TOKEN_TYPE |
+| (are we in an action?).                                         |
+`----------------------------------------------------------------*/
 
 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_INITIAL_ACTION:
+    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));
+}
+
+
+/*------------------------------------------------------.
+| Scan NUMBER for a base-BASE integer at location LOC.  |
+`------------------------------------------------------*/
+
+static unsigned long int
+scan_integer (char const *number, int base, location loc)
+{
+  unsigned long int num;
+  set_errno (0);
+  num = strtoul (number, 0, base);
+  if (INT_MAX < num || get_errno ())
+    {
+      complain_at (loc, _("integer out of range: %s"), quote (number));
+      num = INT_MAX;
+    }
+  return num;
 }
 
 
 }
 
 
@@ -855,7 +975,7 @@ handle_at (braced_code_t braced_code_kind,
 static int
 convert_ucn_to_byte (char const *ucn)
 {
 static int
 convert_ucn_to_byte (char const *ucn)
 {
-  unsigned long code = strtoul (ucn + 2, 0, 16);
+  unsigned long int code = strtoul (ucn + 2, 0, 16);
 
   /* FIXME: Currently we assume Unicode-compatible unibyte characters
      on ASCII hosts (i.e., Latin-1 on hosts with 8-bit bytes).  On
 
   /* FIXME: Currently we assume Unicode-compatible unibyte characters
      on ASCII hosts (i.e., Latin-1 on hosts with 8-bit bytes).  On
@@ -871,7 +991,7 @@ convert_ucn_to_byte (char const *ucn)
     /* A non-ASCII host.  Use CODE to index into a table of the C
        basic execution character set, which is guaranteed to exist on
        all Standard C platforms.  This table also includes '$', '@',
     /* A non-ASCII host.  Use CODE to index into a table of the C
        basic execution character set, which is guaranteed to exist on
        all Standard C platforms.  This table also includes '$', '@',
-       and '`', which not in the basic execution character set but
+       and '`', which are not in the basic execution character set but
        which are unibyte characters on all the platforms that we know
        about.  */
     static signed char const table[] =
        which are unibyte characters on all the platforms that we know
        about.  */
     static signed char const table[] =
@@ -907,19 +1027,57 @@ convert_ucn_to_byte (char const *ucn)
 `----------------------------------------------------------------*/
 
 static void
 `----------------------------------------------------------------*/
 
 static void
-handle_syncline (char *args, location_t *location)
+handle_syncline (char *args)
 {
   int lineno = strtol (args, &args, 10);
   const char *file = NULL;
   file = strchr (args, '"') + 1;
   *strchr (file, '"') = 0;
 {
   int lineno = strtol (args, &args, 10);
   const char *file = NULL;
   file = strchr (args, '"') + 1;
   *strchr (file, '"') = 0;
-  /* FIXME: Leaking...  Can't free, as some locations are still
-     pointing to the old file name.  */
-  infile = xstrdup (file);
-  location->file = infile;
-  location->last_line = lineno;
+  scanner_cursor.file = current_file = uniqstr_new (file);
+  scanner_cursor.line = lineno;
+  scanner_cursor.column = 1;
 }
 
 }
 
+
+/*----------------------------------------------------------------.
+| For a token or comment starting at START, report message MSGID, |
+| which should say that an end marker was found before           |
+| the expected TOKEN_END.                                        |
+`----------------------------------------------------------------*/
+
+static void
+unexpected_end (boundary start, char const *msgid, char const *token_end)
+{
+  location loc;
+  loc.start = start;
+  loc.end = scanner_cursor;
+  complain_at (loc, _(msgid), token_end);
+}
+
+
+/*------------------------------------------------------------------------.
+| 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.  |
+`------------------------------------------------------------------------*/
+
+static void
+unexpected_eof (boundary start, char const *token_end)
+{
+  unexpected_end (start, N_("missing `%s' at end of file"), token_end);
+}
+
+
+/*----------------------------------------.
+| Likewise, but for unexpected newlines.  |
+`----------------------------------------*/
+
+static void
+unexpected_newline (boundary start, char const *token_end)
+{
+  unexpected_end (start, N_("missing `%s' at end of line"), token_end);
+}
+
+
 /*-------------------------.
 | Initialize the scanner.  |
 `-------------------------*/
 /*-------------------------.
 | Initialize the scanner.  |
 `-------------------------*/
@@ -927,7 +1085,7 @@ handle_syncline (char *args, location_t *location)
 void
 scanner_initialize (void)
 {
 void
 scanner_initialize (void)
 {
-  obstack_init (&string_obstack);
+  obstack_init (&obstack_for_string);
 }
 
 
 }
 
 
@@ -938,7 +1096,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);
 }