]> git.saurik.com Git - bison.git/blobdiff - src/scan-gram.l
(<INITIAL>{int}): Use set_errno and get_errno instead of errno.
[bison.git] / src / scan-gram.l
index 95aab9853709e118bd0b34910432925eda6d7bbc..41aefd620440633787aba050e6f15c91d12a5439 100644 (file)
    02111-1307  USA
 */
 
    02111-1307  USA
 */
 
-%option debug nodefault noyywrap nounput never-interactive stack
+%option debug nodefault noyywrap never-interactive
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
 #include "system.h"
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
 #include "system.h"
-#include "mbswidth.h"
+
+#include <mbswidth.h>
+#include <get-errno.h>
+#include <quote.h>
+
 #include "complain.h"
 #include "complain.h"
-#include "quote.h"
+#include "files.h"
 #include "getargs.h"
 #include "gram.h"
 #include "reader.h"
 #include "getargs.h"
 #include "gram.h"
 #include "reader.h"
+#include "uniqstr.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)
-
-#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;
-       }
-    }
+#define YY_USER_INIT                                   \
+  do                                                   \
+    {                                                  \
+      scanner_cursor.file = current_file;              \
+      scanner_cursor.line = 1;                         \
+      scanner_cursor.column = 1;                       \
+    }                                                  \
+  while (0)
 
 
-  return s;
-}
+/* 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);
 
 
-/* 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);
-}
-
+static size_t no_cr_read (FILE *, char *, size_t);
+#define YY_INPUT(buf, result, size) ((result) = no_cr_read (yyin, buf, size))
 
 
 
 
-/* STRING_OBSTACK -- Used to store all the characters that we need to
+/* OBSTACK_FOR_STRING -- Used to store all the characters that we need to
    keep (to construct ID, STRINGS etc.).  Use the following macros to
    use it.
 
    keep (to construct ID, STRINGS etc.).  Use the following macros to
    use it.
 
-   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;
 
 
-static struct obstack string_obstack;
-char *last_string;
+/* A string representing the most recently saved token.  */
+static char *last_string;
 
 
-#define YY_OBS_GROW   \
-  obstack_grow (&string_obstack, yytext, yyleng)
 
 
-#define YY_OBS_FINISH                                  \
+#define STRING_GROW   \
+  obstack_grow (&obstack_for_string, yytext, yyleng)
+
+#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,15 +96,16 @@ 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_dollar (braced_code code_kind, char *cp, location loc);
+static void handle_at (braced_code code_kind, char *cp, location loc);
+static void handle_syncline (char *args);
 static int convert_ucn_to_byte (char const *hex_text);
 static int convert_ucn_to_byte (char const *hex_text);
+static void unexpected_end_of_file (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_BRACED_CODE SC_PROLOGUE SC_EPILOGUE
 
 %x SC_ESCAPED_STRING SC_ESCAPED_CHARACTER
 %x SC_BRACED_CODE SC_PROLOGUE SC_EPILOGUE
 
@@ -201,14 +125,44 @@ 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);
+
+  /* Location of most recent identifier, when applicable.  */
+  location id_loc IF_LINT (= *loc);
+
+  /* Where containing code started, when applicable.  */
+  boundary code_start IF_LINT (= loc->start);
+
+  /* Where containing comment or string or character literal started,
+     when applicable.  */
+  boundary token_start IF_LINT (= loc->start);
 %}
 
 
 %}
 
 
+  /*-----------------------.
+  | Scanning white space.  |
+  `-----------------------*/
+
+<INITIAL,SC_AFTER_IDENTIFIER>
+{
+  [ \f\n\t\v]  ;
+
+  /* Comments. */
+  "/*"         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.  |
   `----------------------------*/
@@ -249,86 +203,104 @@ splice    (\\[ \f\t\v]*\n)*
   "%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));
   }
 
   "="                     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;
+  "," {
+    warn_at (*loc, _("stray `,' treated as white space"));
+  }
 
 
-  {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} {
     unsigned long num;
   }
 
   {int} {
     unsigned long num;
-    errno = 0;
+    set_errno (0);
     num = strtoul (yytext, 0, 10);
     num = strtoul (yytext, 0, 10);
-    if (INT_MAX < num || errno)
+    if (INT_MAX < num || get_errno ())
       {
       {
-       complain_at (*yylloc, _("integer out of range: %s"), quote (yytext));
+       complain_at (*loc, _("integer out of range: %s"), quote (yytext));
        num = INT_MAX;
       }
        num = INT_MAX;
       }
-    yylval->integer = num;
+    val->integer = num;
     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;
+  "\""       STRING_GROW; 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;
+    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);
+      {
+       code_start = loc->start;
+       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));
   }
 }
 
 
   }
 }
 
 
-  /*-------------------------------------------------------------------.
-  | 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;
+  }
 }
 
 
 }
 
 
@@ -338,17 +310,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_end_of_file (token_start, "*/");
 }
 
 
 }
 
 
@@ -358,13 +322,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_end_of_file (token_start, "*/");
 }
 
 
 }
 
 
@@ -374,9 +333,9 @@ 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;
 }
 
 
 }
 
 
@@ -388,25 +347,17 @@ splice     (\\[ \f\t\v]*\n)*
 <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_GROW;
+    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     STRING_GROW;
+  <<EOF>>   unexpected_end_of_file (token_start, "\"");
 }
 
   /*---------------------------------------------------------------.
 }
 
   /*---------------------------------------------------------------.
@@ -417,31 +368,21 @@ splice     (\\[ \f\t\v]*\n)*
 <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;
-    }
+    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);
+    STRING_FREE;
+    rule_length++;
+    BEGIN INITIAL;
+    return ID;
   }
 
   }
 
-  [^\'\\]+  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;
-  }
+  .|\n     STRING_GROW;
+  <<EOF>>   unexpected_end_of_file (token_start, "'");
 }
 
 
 }
 
 
@@ -454,55 +395,43 @@ splice     (\\[ \f\t\v]*\n)*
   \\[0-7]{1,3} {
     unsigned long c = strtoul (yytext + 1, 0, 8);
     if (UCHAR_MAX < c)
   \\[0-7]{1,3} {
     unsigned long c = strtoul (yytext + 1, 0, 8);
     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
     else
-      obstack_1grow (&string_obstack, c);
+      obstack_1grow (&obstack_for_string, c);
   }
 
   }
 
-  \\x[0-9a-fA-F]+ {
+  \\x[0-9abcdefABCDEF]+ {
     unsigned long c;
     unsigned long c;
-    errno = 0;
+    set_errno (0);
     c = strtoul (yytext + 2, 0, 16);
     c = strtoul (yytext + 2, 0, 16);
-    if (UCHAR_MAX < c || errno)
-      {
-       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
     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
     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;
 }
 
 
 }
 
 
@@ -513,23 +442,9 @@ splice      (\\[ \f\t\v]*\n)*
 
 <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;
+  \\{splice}[^$@\[\]]  STRING_GROW;
+  <<EOF>>              unexpected_end_of_file (token_start, "'");
 }
 
 
 }
 
 
@@ -540,23 +455,9 @@ splice      (\\[ \f\t\v]*\n)*
 
 <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;
+  \\{splice}[^$@\[\]]  STRING_GROW;
+  <<EOF>>              unexpected_end_of_file (token_start, "\"");
 }
 
 
 }
 
 
@@ -566,18 +467,29 @@ 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);
-
-  /* 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);
-
-  /* Not comments. */
-  "/"         YY_OBS_GROW;
+  "'" {
+    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;
+  }
 }
 
 
 }
 
 
@@ -588,38 +500,32 @@ 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;
+    STRING_GROW;
     braces_level--;
     if (braces_level < 0)
       {
     braces_level--;
     if (braces_level < 0)
       {
-       yy_pop_state ();
-       YY_OBS_FINISH;
-       yylval->string = last_string;
+       STRING_FINISH;
+       loc->start = code_start;
+       val->chars = last_string;
        rule_length++;
        rule_length++;
+       BEGIN INITIAL;
        return BRACED_CODE;
       }
   }
 
        return BRACED_CODE;
       }
   }
 
+  /* Tokenize `<<%' correctly (as `<<' `%') rather than incorrrectly
+     (as `<' `<%').  */
+  "<"{splice}"<"  STRING_GROW;
+
   "$"("<"{tag}">")?(-?[0-9]+|"$") { handle_dollar (current_braced_code,
   "$"("<"{tag}">")?(-?[0-9]+|"$") { handle_dollar (current_braced_code,
-                                                  yytext, *yylloc); }
+                                                  yytext, *loc); }
   "@"(-?[0-9]+|"$")               { handle_at (current_braced_code,
   "@"(-?[0-9]+|"$")               { handle_at (current_braced_code,
-                                              yytext, *yylloc); }
-
-  /* `"<"{splice}"<"' tokenizes `<<%' correctly (as `<<' `%') rather
-     than incorrrectly (as `<' `<%').  */
-  [^\"$%\'/<@\[\]\{\}]+|[$%/<@]|"<"{splice}"<"  YY_OBS_GROW;
-
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in `{ ... }'"));
-    yy_pop_state ();
-    YY_OBS_FINISH;
-    yylval->string = last_string;
-    return BRACED_CODE;
-  }
+                                              yytext, *loc); }
 
 
+  <<EOF>>  unexpected_end_of_file (code_start, "}");
 }
 
 
 }
 
 
@@ -630,22 +536,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_end_of_file (code_start, "%}");
 }
 
 
 }
 
 
@@ -656,29 +554,127 @@ 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;
   }
 }
 
 
+  /*----------------------------------------------------------------.
+  | By default, grow the string obstack with the input, escaping M4 |
+  | quoting characters.                                                    |
+  `----------------------------------------------------------------*/
+
+<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, "@}");
+  .|\n  STRING_GROW;
+}
+
+
 %%
 
 %%
 
+/* 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 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;
+}
+
+
 /*------------------------------------------------------------------.
 | 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
 `------------------------------------------------------------------*/
 
 static inline void
-handle_action_dollar (char *text, location_t location)
+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;
@@ -696,38 +692,37 @@ handle_action_dollar (char *text, location_t location)
   if (*cp == '$')
     {
       if (!type_name)
   if (*cp == '$')
     {
       if (!type_name)
-       type_name = symbol_list_n_type_name_get (current_rule, location, 0);
+       type_name = symbol_list_n_type_name_get (current_rule, loc, 0);
       if (!type_name && typed)
       if (!type_name && typed)
-       complain_at (location, _("$$ of `%s' has no declared type"),
+       complain_at (loc, _("$$ of `%s' has no declared type"),
                     current_rule->sym->tag);
       if (!type_name)
        type_name = "";
                     current_rule->sym->tag);
       if (!type_name)
        type_name = "";
-      obstack_fgrow1 (&string_obstack,
+      obstack_fgrow1 (&obstack_for_string,
                      "]b4_lhs_value([%s])[", type_name);
     }
   else
     {
       long num;
                      "]b4_lhs_value([%s])[", type_name);
     }
   else
     {
       long num;
-      errno = 0;
+      set_errno (0);
       num = strtol (cp, 0, 10);
 
       num = strtol (cp, 0, 10);
 
-      if (INT_MIN <= num && num <= rule_length && ! errno)
+      if (INT_MIN <= num && num <= rule_length && ! get_errno ())
        {
          int n = num;
          if (!type_name && n > 0)
        {
          int n = num;
          if (!type_name && n > 0)
-           type_name = symbol_list_n_type_name_get (current_rule, location,
-                                                    n);
+           type_name = symbol_list_n_type_name_get (current_rule, loc, n);
          if (!type_name && typed)
          if (!type_name && typed)
-           complain_at (location, _("$%d of `%s' has no declared type"),
-                     n, current_rule->sym->tag);
+           complain_at (loc, _("$%d of `%s' has no declared type"),
+                        n, current_rule->sym->tag);
          if (!type_name)
            type_name = "";
          if (!type_name)
            type_name = "";
-         obstack_fgrow3 (&string_obstack,
+         obstack_fgrow3 (&obstack_for_string,
                          "]b4_rhs_value([%d], [%d], [%s])[",
                          rule_length, n, type_name);
        }
       else
                          "]b4_rhs_value([%d], [%d], [%s])[",
                          rule_length, n, type_name);
        }
       else
-       complain_at (location, _("integer out of range: %s"), quote (text));
+       complain_at (loc, _("integer out of range: %s"), quote (text));
     }
 }
 
     }
 }
 
@@ -738,13 +733,13 @@ handle_action_dollar (char *text, location_t location)
 `---------------------------------------------------------------*/
 
 static inline void
 `---------------------------------------------------------------*/
 
 static inline void
-handle_symbol_code_dollar (char *text, location_t location)
+handle_symbol_code_dollar (char *text, location loc)
 {
   char *cp = text + 1;
   if (*cp == '$')
 {
   char *cp = text + 1;
   if (*cp == '$')
-    obstack_sgrow (&string_obstack, "]b4_dollar_dollar[");
+    obstack_sgrow (&obstack_for_string, "]b4_dollar_dollar[");
   else
   else
-    complain_at (location, _("invalid value: %s"), quote (text));
+    complain_at (loc, _("invalid value: %s"), quote (text));
 }
 
 
 }
 
 
@@ -754,18 +749,17 @@ handle_symbol_code_dollar (char *text, location_t location)
 `-----------------------------------------------------------------*/
 
 static void
 `-----------------------------------------------------------------*/
 
 static void
-handle_dollar (braced_code_t braced_code_kind,
-              char *text, location_t location)
+handle_dollar (braced_code braced_code_kind, char *text, location loc)
 {
   switch (braced_code_kind)
     {
     case action_braced_code:
 {
   switch (braced_code_kind)
     {
     case action_braced_code:
-      handle_action_dollar (text, location);
+      handle_action_dollar (text, loc);
       break;
 
     case destructor_braced_code:
     case printer_braced_code:
       break;
 
     case destructor_braced_code:
     case printer_braced_code:
-      handle_symbol_code_dollar (text, location);
+      handle_symbol_code_dollar (text, loc);
       break;
     }
 }
       break;
     }
 }
@@ -773,33 +767,33 @@ handle_dollar (braced_code_t braced_code_kind,
 
 /*------------------------------------------------------.
 | 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
 `------------------------------------------------------*/
 
 static inline void
-handle_action_at (char *text, location_t location)
+handle_action_at (char *text, location loc)
 {
   char *cp = text + 1;
   locations_flag = 1;
 
   if (*cp == '$')
     {
 {
   char *cp = text + 1;
   locations_flag = 1;
 
   if (*cp == '$')
     {
-      obstack_sgrow (&string_obstack, "]b4_lhs_location[");
+      obstack_sgrow (&obstack_for_string, "]b4_lhs_location[");
     }
   else
     {
       long num;
     }
   else
     {
       long num;
-      errno = 0;
+      set_errno (0);
       num = strtol (cp, 0, 10);
 
       num = strtol (cp, 0, 10);
 
-      if (INT_MIN <= num && num <= rule_length && ! errno)
+      if (INT_MIN <= num && num <= rule_length && ! get_errno ())
        {
          int n = num;
        {
          int n = num;
-         obstack_fgrow2 (&string_obstack, "]b4_rhs_location([%d], [%d])[",
+         obstack_fgrow2 (&obstack_for_string, "]b4_rhs_location([%d], [%d])[",
                          rule_length, n);
        }
       else
                          rule_length, n);
        }
       else
-       complain_at (location, _("integer out of range: %s"), quote (text));
+       complain_at (loc, _("integer out of range: %s"), quote (text));
     }
 }
 
     }
 }
 
@@ -810,13 +804,13 @@ handle_action_at (char *text, location_t location)
 `---------------------------------------------------------------*/
 
 static inline void
 `---------------------------------------------------------------*/
 
 static inline void
-handle_symbol_code_at (char *text, location_t location)
+handle_symbol_code_at (char *text, location loc)
 {
   char *cp = text + 1;
   if (*cp == '$')
 {
   char *cp = text + 1;
   if (*cp == '$')
-    obstack_sgrow (&string_obstack, "]b4_at_dollar[");
+    obstack_sgrow (&obstack_for_string, "]b4_at_dollar[");
   else
   else
-    complain_at (location, _("invalid value: %s"), quote (text));
+    complain_at (loc, _("invalid value: %s"), quote (text));
 }
 
 
 }
 
 
@@ -826,18 +820,17 @@ handle_symbol_code_at (char *text, location_t location)
 `-------------------------------------------------------------------*/
 
 static void
 `-------------------------------------------------------------------*/
 
 static void
-handle_at (braced_code_t braced_code_kind,
-          char *text, location_t location)
+handle_at (braced_code braced_code_kind, char *text, location loc)
 {
   switch (braced_code_kind)
     {
     case action_braced_code:
 {
   switch (braced_code_kind)
     {
     case action_braced_code:
-      handle_action_at (text, location);
+      handle_action_at (text, loc);
       break;
 
     case destructor_braced_code:
     case printer_braced_code:
       break;
 
     case destructor_braced_code:
     case printer_braced_code:
-      handle_symbol_code_at (text, location);
+      handle_symbol_code_at (text, loc);
       break;
     }
 }
       break;
     }
 }
@@ -868,7 +861,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[] =
@@ -899,6 +892,48 @@ convert_ucn_to_byte (char const *ucn)
 }
 
 
 }
 
 
+/*----------------------------------------------------------------.
+| Handle `#line INT "FILE"'.  ARGS has already skipped `#line '.  |
+`----------------------------------------------------------------*/
+
+static void
+handle_syncline (char *args)
+{
+  int lineno = strtol (args, &args, 10);
+  const char *file = NULL;
+  file = strchr (args, '"') + 1;
+  *strchr (file, '"') = 0;
+  scanner_cursor.file = current_file = xstrdup (file);
+  scanner_cursor.line = lineno;
+  scanner_cursor.column = 1;
+}
+
+
+/*------------------------------------------------------------------------.
+| 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)
+{
+  size_t i = strlen (token_end);
+
+  location loc;
+  loc.start = start;
+  loc.end = scanner_cursor;
+  complain_at (loc, _("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]);
+}
+
+
 /*-------------------------.
 | Initialize the scanner.  |
 `-------------------------*/
 /*-------------------------.
 | Initialize the scanner.  |
 `-------------------------*/
@@ -906,7 +941,7 @@ convert_ucn_to_byte (char const *ucn)
 void
 scanner_initialize (void)
 {
 void
 scanner_initialize (void)
 {
-  obstack_init (&string_obstack);
+  obstack_init (&obstack_for_string);
 }
 
 
 }
 
 
@@ -917,7 +952,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);
 }