]> git.saurik.com Git - bison.git/blobdiff - src/scan-gram.l
* src/main.c (main): Free `infile'.
[bison.git] / src / scan-gram.l
index 7c4b3487b998e126887655ef3fee370058b26da6..9670687305f20b31adbd8dea2dc6e5caddb01638 100644 (file)
@@ -24,6 +24,7 @@
 
 %{
 #include "system.h"
+#include "mbswidth.h"
 #include "complain.h"
 #include "quote.h"
 #include "getargs.h"
 #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  LOCATION_COLUMNS (*yylloc, yyleng)
-#define YY_LINES        LOCATION_LINES (*yylloc, yyleng);
+#define YY_USER_ACTION  extend_location (yylloc, yytext, yyleng);
 #define YY_STEP         LOCATION_STEP (*yylloc)
 
+#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
    keep (to construct ID, STRINGS etc.).  Use the following macros to
    use it.
@@ -75,41 +163,50 @@ scanner_last_string_free (void)
 }
 
 
-static int braces_level = 0;
 static int percent_percent_count = 0;
 
-static void handle_dollar PARAMS ((braced_code_t code_kind,
-                                  char *cp, location_t location));
-static void handle_at PARAMS ((braced_code_t code_kind,
-                              char *cp, location_t location));
+/* 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
+   the case of mid rule actions.
+
+   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 int convert_ucn_to_byte (char const *hex_text);
 
 %}
-%x SC_COMMENT
+%x SC_COMMENT SC_LINE_COMMENT SC_YACC_COMMENT
 %x SC_STRING SC_CHARACTER
 %x SC_ESCAPED_STRING SC_ESCAPED_CHARACTER
 %x SC_BRACED_CODE SC_PROLOGUE SC_EPILOGUE
 
-id      [.a-zA-Z][.a-zA-Z_0-9]*
-int     [0-9]+
-eols     (\n|\r|\n\r|\r\n)+
-blanks   [ \t\f]+
+letter   [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
+id       {letter}({letter}|[0-9])*
+directive %{letter}({letter}|[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
+   NUL and newline, as this simplifies our implementation.  */
+tag     [^\0\n>]+
+
+/* Zero or more instances of backslash-newline.  Following GCC, allow
+   white space between the backslash and the newline.  */
+splice  (\\[ \f\t\v]*\n)*
 
 %%
 %{
+  int braces_level IF_LINT (= 0);
+
   /* At each yylex invocation, mark the current position as the
      start of the next token.  */
-#define TR_POS 0
-#if TR_POS
-  fprintf (stderr, "FOO1: %p: ", yylloc);
-  LOCATION_PRINT (stderr, *yylloc);
-  fprintf (stderr, "\n");
-#endif
   YY_STEP;
-#if TR_POS
-  fprintf (stderr, "BAR1: ");
-  LOCATION_PRINT (stderr, *yylloc);
-  fprintf (stderr, "\n");
-#endif
 %}
 
 
@@ -128,7 +225,7 @@ blanks   [ \t\f]+
   "%expect"               return PERCENT_EXPECT;
   "%file-prefix"          return PERCENT_FILE_PREFIX;
   "%fixed"[-_]"output"[-_]"files"   return PERCENT_YACC;
-  "%glr"[-_]"parser"     return PERCENT_GLR_PARSER;
+  "%glr-parser"           return PERCENT_GLR_PARSER;
   "%left"                 return PERCENT_LEFT;
   "%locations"            return PERCENT_LOCATIONS;
   "%merge"               return PERCENT_MERGE;
@@ -137,10 +234,12 @@ blanks   [ \t\f]+
   "%nonassoc"             return PERCENT_NONASSOC;
   "%nterm"                return PERCENT_NTERM;
   "%output"               return PERCENT_OUTPUT;
-  "%prec"                 return PERCENT_PREC;
+  "%parse-param"          return PERCENT_PARSE_PARAM;
+  "%prec"                 rule_length--; return PERCENT_PREC;
   "%printer"              return PERCENT_PRINTER;
   "%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;
@@ -151,38 +250,58 @@ blanks   [ \t\f]+
   "%verbose"              return PERCENT_VERBOSE;
   "%yacc"                 return PERCENT_YACC;
 
+  {directive}             {
+    complain_at (*yylloc, _("invalid directive: %s"), quote (yytext));
+    YY_STEP;
+  }
+
+  ^"#line "{int}" \""[^\"]*"\"\n"   handle_syncline (yytext + strlen ("#line "), yylloc); YY_STEP;
+
   "="                     return EQUAL;
-  ":"                     return COLON;
-  "|"                     return PIPE;
+  ":"                     rule_length = 0; return COLON;
+  "|"                     rule_length = 0; return PIPE;
+  ","                     return COMMA;
   ";"                     return SEMICOLON;
 
-  {eols}      YY_LINES; YY_STEP;
-  {blanks}    YY_STEP;
+  [ \f\n\t\v]+  YY_STEP;
+
   {id}        {
     yylval->symbol = symbol_get (yytext, *yylloc);
+    rule_length++;
     return ID;
   }
 
-  {int}       yylval->integer = strtol (yytext, 0, 10); return 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;
+    return INT;
+  }
 
   /* Characters.  We don't check there is only one.  */
-  \'          YY_OBS_GROW; yy_push_state (SC_ESCAPED_CHARACTER);
+  "'"         YY_OBS_GROW; yy_push_state (SC_ESCAPED_CHARACTER);
 
   /* Strings. */
-  \"          YY_OBS_GROW; yy_push_state (SC_ESCAPED_STRING);
+  "\""        YY_OBS_GROW; yy_push_state (SC_ESCAPED_STRING);
 
   /* Comments. */
-  "/*"        yy_push_state (SC_COMMENT);
+  "/*"        BEGIN SC_YACC_COMMENT;
   "//".*      YY_STEP;
 
   /* Prologue. */
   "%{"        yy_push_state (SC_PROLOGUE);
 
   /* Code in between braces.  */
-  "{"         YY_OBS_GROW; ++braces_level; yy_push_state (SC_BRACED_CODE);
+  "{"         YY_OBS_GROW; braces_level = 0; yy_push_state (SC_BRACED_CODE);
 
   /* A type. */
-  "<"[^>]+">" {
+  "<"{tag}">" {
     obstack_grow (&string_obstack, yytext + 1, yyleng - 2);
     YY_OBS_FINISH;
     yylval->string = last_string;
@@ -197,57 +316,73 @@ blanks   [ \t\f]+
   }
 
   .           {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": invalid character: `%c'\n", *yytext);
+    complain_at (*yylloc, _("invalid character: %s"), quote (yytext));
     YY_STEP;
   }
 }
 
 
-  /*------------------------------------------------------------.
-  | Whatever the start condition (but those which correspond to |
-  | entity `swallowed' by Bison: SC_ESCAPED_STRING and          |
-  | SC_ESCAPED_CHARACTER), no M4 character must escape as is.   |
-  `------------------------------------------------------------*/
+  /*-------------------------------------------------------------------.
+  | 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.      |
+  `-------------------------------------------------------------------*/
 
-<SC_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>
 {
-  \[          if (YY_START != SC_COMMENT) obstack_sgrow (&string_obstack, "@<:@");
-  \]          if (YY_START != SC_COMMENT) obstack_sgrow (&string_obstack, "@:>@");
+  \[   obstack_sgrow (&string_obstack, "@<:@");
+  \]   obstack_sgrow (&string_obstack, "@:>@");
 }
 
 
+  /*---------------------------------------------------------------.
+  | Scanning a Yacc comment.  The initial `/ *' is already eaten.  |
+  `---------------------------------------------------------------*/
 
-  /*-----------------------------------------------------------.
-  | Scanning a C comment. The initial `/ *' is already eaten.  |
-  `-----------------------------------------------------------*/
-
-<SC_COMMENT>
+<SC_YACC_COMMENT>
 {
-  "*/" { /* End of the comment. */
-    if (yy_top_state () == INITIAL)
-      {
-       YY_STEP;
-      }
-    else
-      {
-       YY_OBS_GROW;
-      }
-    yy_pop_state ();
+  "*/" {
+    YY_STEP;
+    BEGIN INITIAL;
+  }
+
+  [^*]+|"*"  ;
+
+  <<EOF>> {
+    complain_at (*yylloc, _("unexpected end of file in a comment"));
+    BEGIN INITIAL;
   }
+}
+
+
+  /*------------------------------------------------------------.
+  | Scanning a C comment.  The initial `/ *' is already eaten.  |
+  `------------------------------------------------------------*/
 
-  [^\[\]*\n\r]+        if (yy_top_state () != INITIAL) YY_OBS_GROW;
-  {eols}       if (yy_top_state () != INITIAL) YY_OBS_GROW; YY_LINES;
-  .             /* Stray `*'. */if (yy_top_state () != INITIAL) YY_OBS_GROW;
+<SC_COMMENT>
+{
+  "*"{splice}"/"  YY_OBS_GROW; yy_pop_state ();
+  [^*\[\]]+|"*"   YY_OBS_GROW;
 
   <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a comment\n");
+    complain_at (*yylloc, _("unexpected end of file in a comment"));
     yy_pop_state ();
   }
 }
 
 
+  /*--------------------------------------------------------------.
+  | Scanning a line comment.  The initial `//' is already eaten.  |
+  `--------------------------------------------------------------*/
+
+<SC_LINE_COMMENT>
+{
+  "\n"                  YY_OBS_GROW; yy_pop_state ();
+  ([^\n\[\]]|{splice})+  YY_OBS_GROW;
+  <<EOF>>               yy_pop_state ();
+}
+
+
   /*----------------------------------------------------------------.
   | Scanning a C string, including its escapes.  The initial `"' is |
   | already eaten.                                                  |
@@ -255,22 +390,20 @@ blanks   [ \t\f]+
 
 <SC_ESCAPED_STRING>
 {
-  \" {
+  "\"" {
     assert (yy_top_state () == INITIAL);
     YY_OBS_GROW;
     YY_OBS_FINISH;
     yylval->string = last_string;
     yy_pop_state ();
+    rule_length++;
     return STRING;
   }
 
-  [^\"\n\r\\]+      YY_OBS_GROW;
-
-  {eols}    obstack_1grow (&string_obstack, '\n'); YY_LINES;
+  [^\"\\]+  YY_OBS_GROW;
 
   <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a string\n");
+    complain_at (*yylloc, _("unexpected end of file in a string"));
     assert (yy_top_state () == INITIAL);
     YY_OBS_FINISH;
     yylval->string = last_string;
@@ -286,7 +419,7 @@ blanks   [ \t\f]+
 
 <SC_ESCAPED_CHARACTER>
 {
-  \' {
+  "'" {
     YY_OBS_GROW;
     assert (yy_top_state () == INITIAL);
     {
@@ -297,17 +430,15 @@ blanks   [ \t\f]+
                                    (unsigned char) last_string[1], *yylloc);
       YY_OBS_FREE;
       yy_pop_state ();
+      rule_length++;
       return ID;
     }
   }
 
-  [^\'\n\r\\]      YY_OBS_GROW;
-
-  {eols}    obstack_1grow (&string_obstack, '\n'); YY_LINES;
+  [^\'\\]+  YY_OBS_GROW;
 
   <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a character\n");
+    complain_at (*yylloc, _("unexpected end of file in a character literal"));
     assert (yy_top_state () == INITIAL);
     YY_OBS_FINISH;
     yylval->string = last_string;
@@ -323,20 +454,30 @@ blanks   [ \t\f]+
 
 <SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>
 {
-  \\[0-7]{3}           {
-    long c = strtol (yytext + 1, 0, 8);
-    if (c > 255)
+  \\[0-7]{1,3} {
+    unsigned long c = strtoul (yytext + 1, 0, 8);
+    if (UCHAR_MAX < c)
       {
-       LOCATION_PRINT (stderr, *yylloc);
-       fprintf (stderr, ": invalid escape: %s\n", quote (yytext));
+       complain_at (*yylloc, _("invalid escape sequence: %s"),
+                    quote (yytext));
        YY_STEP;
       }
     else
       obstack_1grow (&string_obstack, c);
   }
 
-  \\x[0-9a-fA-F]{2}    {
-    obstack_1grow (&string_obstack, strtol (yytext + 2, 0, 16));
+  \\x[0-9a-fA-F]+ {
+    unsigned long c;
+    errno = 0;
+    c = strtoul (yytext + 2, 0, 16);
+    if (UCHAR_MAX < c || errno)
+      {
+       complain_at (*yylloc, _("invalid escape sequence: %s"),
+                    quote (yytext));
+       YY_STEP;
+      }
+    else
+      obstack_1grow (&string_obstack, c);
   }
 
   \\a  obstack_1grow (&string_obstack, '\a');
@@ -346,10 +487,21 @@ blanks   [ \t\f]+
   \\r  obstack_1grow (&string_obstack, '\r');
   \\t  obstack_1grow (&string_obstack, '\t');
   \\v  obstack_1grow (&string_obstack, '\v');
-  \\[\\""]   obstack_1grow (&string_obstack, yytext[1]);
+  \\[\"\'?\\]  obstack_1grow (&string_obstack, yytext[1]);
+  \\(u|U[0-9a-fA-F]{4})[0-9a-fA-F]{4} {
+    int c = convert_ucn_to_byte (yytext);
+    if (c < 0)
+      {
+       complain_at (*yylloc, _("invalid escape sequence: %s"),
+                    quote (yytext));
+       YY_STEP;
+      }
+    else
+      obstack_1grow (&string_obstack, c);
+  }
   \\(.|\n)     {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unrecognized escape: %s\n", quote (yytext));
+    complain_at (*yylloc, _("unrecognized escape sequence: %s"),
+                quote (yytext));
     YY_OBS_GROW;
   }
   /* FLex wants this rule, in case of a `\<<EOF>>'. */
@@ -364,22 +516,20 @@ blanks   [ \t\f]+
 
 <SC_CHARACTER>
 {
-  \' {
+  "'" {
     YY_OBS_GROW;
     assert (yy_top_state () != INITIAL);
     yy_pop_state ();
   }
 
-  [^\[\]\'\n\r\\]+     YY_OBS_GROW;
-  \\(.|\n)             YY_OBS_GROW;
-  /* FLex wants this rule, in case of a `\<<EOF>>'. */
+  [^'\[\]\\]+         YY_OBS_GROW;
+  \\{splice}[^\[\]]    YY_OBS_GROW;
+  {splice}            YY_OBS_GROW;
+  /* Needed for `\<<EOF>>', `\\<<newline>>[', and `\\<<newline>>]'.  */
   \\                   YY_OBS_GROW;
 
-  {eols}               YY_OBS_GROW; YY_LINES;
-
   <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a character\n");
+    complain_at (*yylloc, _("unexpected end of file in a character literal"));
     assert (yy_top_state () != INITIAL);
     yy_pop_state ();
   }
@@ -393,22 +543,20 @@ blanks   [ \t\f]+
 
 <SC_STRING>
 {
-  \" {
+  "\"" {
     assert (yy_top_state () != INITIAL);
     YY_OBS_GROW;
     yy_pop_state ();
   }
 
-  [^\[\]\"\n\r\\]+      YY_OBS_GROW;
-  \\(.|\n)              YY_OBS_GROW;
-  /* FLex wants this rule, in case of a `\<<EOF>>'. */
+  [^\"\[\]\\]+        YY_OBS_GROW;
+  \\{splice}[^\[\]]    YY_OBS_GROW;
+  {splice}            YY_OBS_GROW;
+  /* Needed for `\<<EOF>>', `\\<<newline>>[', and `\\<<newline>>]'.  */
   \\                   YY_OBS_GROW;
 
-  {eols}                YY_OBS_GROW; YY_LINES;
-
   <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a string\n");
+    complain_at (*yylloc, _("unexpected end of file in a string"));
     assert (yy_top_state () != INITIAL);
     yy_pop_state ();
   }
@@ -422,14 +570,14 @@ blanks   [ \t\f]+
 <SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
 {
   /* Characters.  We don't check there is only one.  */
-  \'          YY_OBS_GROW; yy_push_state (SC_CHARACTER);
+  "'"         YY_OBS_GROW; yy_push_state (SC_CHARACTER);
 
   /* Strings. */
-  \"          YY_OBS_GROW; yy_push_state (SC_STRING);
+  "\""        YY_OBS_GROW; yy_push_state (SC_STRING);
 
   /* Comments. */
-  "/*"        YY_OBS_GROW; yy_push_state (SC_COMMENT);
-  "//".*      YY_OBS_GROW;
+  "/"{splice}"*"  YY_OBS_GROW; yy_push_state (SC_COMMENT);
+  "/"{splice}"/"  YY_OBS_GROW; yy_push_state (SC_LINE_COMMENT);
 
   /* Not comments. */
   "/"         YY_OBS_GROW;
@@ -443,33 +591,32 @@ blanks   [ \t\f]+
 
 <SC_BRACED_CODE>
 {
+  "{"|"<"{splice}"%"  YY_OBS_GROW; braces_level++;
+  "%"{splice}">"      YY_OBS_GROW; braces_level--;
   "}" {
     YY_OBS_GROW;
-    if (--braces_level == 0)
+    braces_level--;
+    if (braces_level < 0)
       {
        yy_pop_state ();
        YY_OBS_FINISH;
        yylval->string = last_string;
+       rule_length++;
        return BRACED_CODE;
       }
   }
 
-  "{"                  YY_OBS_GROW; braces_level++;
-
-  "$"("<"[^>]+">")?(-?[0-9]+|"$") { handle_dollar (current_braced_code,
+  "$"("<"{tag}">")?(-?[0-9]+|"$") { handle_dollar (current_braced_code,
                                                   yytext, *yylloc); }
   "@"(-?[0-9]+|"$")               { handle_at (current_braced_code,
                                               yytext, *yylloc); }
 
-  [^$@\[\]/\'\"\{\}\n\r]+ YY_OBS_GROW;
-  {eols}       YY_OBS_GROW; YY_LINES;
-
-  /* A lose $, or /, or etc. */
-  .             YY_OBS_GROW;
+  /* `"<"{splice}"<"' tokenizes `<<%' correctly (as `<<' `%') rather
+     than incorrrectly (as `<' `<%').  */
+  [^\"$%\'/<@\[\]\{\}]+|[$%/<@]|"<"{splice}"<"  YY_OBS_GROW;
 
   <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a braced code\n");
+    complain_at (*yylloc, _("unexpected end of file in `{ ... }'"));
     yy_pop_state ();
     YY_OBS_FINISH;
     yylval->string = last_string;
@@ -492,13 +639,11 @@ blanks   [ \t\f]+
     return PROLOGUE;
   }
 
-  [^%\[\]/\'\"\n\r]+ YY_OBS_GROW;
+  [^%\[\]/\'\"]+     YY_OBS_GROW;
   "%"                YY_OBS_GROW;
-  {eols}            YY_OBS_GROW; YY_LINES;
 
   <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a prologue\n");
+    complain_at (*yylloc, _("unexpected end of file in `%%{ ... %%}'"));
     yy_pop_state ();
     YY_OBS_FINISH;
     yylval->string = last_string;
@@ -509,12 +654,12 @@ blanks   [ \t\f]+
 
   /*---------------------------------------------------------------.
   | Scanning the epilogue (everything after the second "%%", which |
-  | has already been eaten                                       |
+  | has already been eaten).                                       |
   `---------------------------------------------------------------*/
 
 <SC_EPILOGUE>
 {
-  ([^\[\]]|{eols})+  YY_OBS_GROW;
+  [^\[\]]+  YY_OBS_GROW;
 
   <<EOF>> {
     yy_pop_state ();
@@ -563,19 +708,15 @@ handle_action_dollar (char *text, location_t location)
       obstack_fgrow1 (&string_obstack,
                      "]b4_lhs_value([%s])[", type_name);
     }
-  else if (isdigit (*cp) || *cp == '-')
+  else
     {
-      /* 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 the
-        case of mid rule actions.  */
-      int rule_length = symbol_list_length (current_rule->next);
-      int n = strtol (cp, &cp, 10);
-
-      if (n > rule_length)
-       complain_at (location, _("invalid value: %s%d"), "$", n);
-      else
+      long num;
+      errno = 0;
+      num = strtol (cp, 0, 10);
+
+      if (INT_MIN <= num && num <= rule_length && ! errno)
        {
+         int n = num;
          if (!type_name && n > 0)
            type_name = symbol_list_n_type_name_get (current_rule, location,
                                                     n);
@@ -588,16 +729,14 @@ handle_action_dollar (char *text, location_t location)
                          "]b4_rhs_value([%d], [%d], [%s])[",
                          rule_length, n, type_name);
        }
-    }
-  else
-    {
-      complain_at (location, _("%s is invalid"), quote (text));
+      else
+       complain_at (location, _("integer out of range: %s"), quote (text));
     }
 }
 
 
 /*---------------------------------------------------------------.
-| TEXT is expexted tp be $$ in some code associated to a symbol: |
+| TEXT is expected to be $$ in some code associated to a symbol: |
 | destructor or printer.                                         |
 `---------------------------------------------------------------*/
 
@@ -608,7 +747,7 @@ handle_symbol_code_dollar (char *text, location_t location)
   if (*cp == '$')
     obstack_sgrow (&string_obstack, "]b4_dollar_dollar[");
   else
-    complain_at (location, _("%s is invalid"), quote (text));
+    complain_at (location, _("invalid value: %s"), quote (text));
 }
 
 
@@ -650,30 +789,26 @@ handle_action_at (char *text, location_t location)
     {
       obstack_sgrow (&string_obstack, "]b4_lhs_location[");
     }
-  else if (isdigit (*cp) || *cp == '-')
-    {
-      /* 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 the
-        case of mid rule actions.  */
-      int rule_length = symbol_list_length (current_rule->next);
-      int n = strtol (cp, &cp, 10);
-
-      if (n > rule_length)
-       complain_at (location, _("invalid value: %s%d"), "@", n);
-      else
-       obstack_fgrow2 (&string_obstack, "]b4_rhs_location([%d], [%d])[",
-                       rule_length, n);
-    }
   else
     {
-      complain_at (location, _("%s is invalid"), quote (text));
+      long num;
+      errno = 0;
+      num = strtol (cp, 0, 10);
+
+      if (INT_MIN <= num && num <= rule_length && ! errno)
+       {
+         int n = num;
+         obstack_fgrow2 (&string_obstack, "]b4_rhs_location([%d], [%d])[",
+                         rule_length, n);
+       }
+      else
+       complain_at (location, _("integer out of range: %s"), quote (text));
     }
 }
 
 
 /*---------------------------------------------------------------.
-| TEXT is expexted tp be @$ in some code associated to a symbol: |
+| TEXT is expected to be @$ in some code associated to a symbol: |
 | destructor or printer.                                         |
 `---------------------------------------------------------------*/
 
@@ -684,7 +819,7 @@ handle_symbol_code_at (char *text, location_t location)
   if (*cp == '$')
     obstack_sgrow (&string_obstack, "]b4_at_dollar[");
   else
-    complain_at (location, _("%s is invalid"), quote (text));
+    complain_at (location, _("invalid value: %s"), quote (text));
 }
 
 
@@ -711,6 +846,80 @@ handle_at (braced_code_t braced_code_kind,
 }
 
 
+/*------------------------------------------------------------------.
+| Convert universal character name UCN to a single-byte character,  |
+| and return that character.  Return -1 if UCN does not correspond  |
+| to a single-byte character.                                      |
+`------------------------------------------------------------------*/
+
+static int
+convert_ucn_to_byte (char const *ucn)
+{
+  unsigned long 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
+     non-ASCII hosts we support only the portable C character set.
+     These limitations should be removed once we add support for
+     multibyte characters.  */
+
+  if (UCHAR_MAX < code)
+    return -1;
+
+#if ! ('$' == 0x24 && '@' == 0x40 && '`' == 0x60 && '~' == 0x7e)
+  {
+    /* 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
+       which are unibyte characters on all the platforms that we know
+       about.  */
+    static signed char const table[] =
+      {
+       '\0',   -1,   -1,   -1,   -1,   -1,   -1, '\a',
+       '\b', '\t', '\n', '\v', '\f', '\r',   -1,   -1,
+         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        ' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'',
+        '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+        '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+        '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+        '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
+        'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
+        'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
+        'X',  'Y',  'Z',  '[', '\\',  ']',  '^',  '_',
+        '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+        'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+        'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+        'x',  'y',  'z',  '{',  '|',  '}',  '~'
+      };
+
+    code = code < sizeof table ? table[code] : -1;
+  }
+#endif
+
+  return code;
+}
+
+
+/*----------------------------------------------------------------.
+| Handle `#line INT "FILE"'.  ARGS has already skipped `#line '.  |
+`----------------------------------------------------------------*/
+
+static void
+handle_syncline (char *args, location_t *location)
+{
+  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;
+}
+
 /*-------------------------.
 | Initialize the scanner.  |
 `-------------------------*/