]> git.saurik.com Git - bison.git/blobdiff - src/scan-gram.l
XCALLOC -> CALLOC, XMALLOC -> MALLOC.
[bison.git] / src / scan-gram.l
index 5a1ce57b7949c9359342bbb2d26e0ec35edacf35..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 <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"
+
+#define YY_USER_INIT                                   \
+  do                                                   \
+    {                                                  \
+      scanner_cursor.file = current_file;              \
+      scanner_cursor.line = 1;                         \
+      scanner_cursor.column = 1;                       \
+    }                                                  \
+  while (0)
+
+/* Location of scanner cursor.  */
+boundary scanner_cursor;
 
 
-/* Each time we match a string, move the end cursor to its end. */
-#define YY_USER_INIT                           \
-do {                                           \
-  LOCATION_RESET (*yylloc);                    \
-   /* This is only to avoid GCC warnings. */   \
-  if (yycontrol) {;};                          \
-} while (0)
+static void adjust_location (location *, char const *, size_t);
+#define YY_USER_ACTION  adjust_location (loc, yytext, yyleng);
 
 
-#define YY_USER_ACTION  LOCATION_COLUMNS (*yylloc, yyleng)
-#define YY_LINES        LOCATION_LINES (*yylloc, yyleng); lineno += yyleng;
-#define YY_STEP         LOCATION_STEP (*yylloc)
+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 string_obstack;
-char *last_string;
+static struct obstack obstack_for_string;
 
 
-#define YY_OBS_GROW   \
-  obstack_grow (&string_obstack, yytext, yyleng)
+/* A string representing the most recently saved token.  */
+static char *last_string;
 
 
-#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;
 }
 
 }
 
+/* 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.
 
 
-static int braces_level = 0;
-static int percent_percent_count = 0;
+   Outside of well-formed rules, RULE_LENGTH has an undefined value.  */
+static int rule_length;
 
 
-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));
+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 void unexpected_end_of_file (boundary, char const *);
 
 %}
 
 %}
-%x SC_COMMENT
+%x SC_COMMENT SC_LINE_COMMENT SC_YACC_COMMENT
 %x SC_STRING SC_CHARACTER
 %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
 
-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)*
 
 %%
 %{
 
 %%
 %{
-  /* 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
+  /* Nesting level of the current code in braces.  */
+  int braces_level IF_LINT (= 0);
+
+  /* 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.  |
   `----------------------------*/
@@ -124,20 +173,26 @@ blanks   [ \t\f]+
   "%define"               return PERCENT_DEFINE;
   "%defines"              return PERCENT_DEFINES;
   "%destructor"           return PERCENT_DESTRUCTOR;
   "%define"               return PERCENT_DEFINE;
   "%defines"              return PERCENT_DEFINES;
   "%destructor"           return PERCENT_DESTRUCTOR;
+  "%dprec"               return PERCENT_DPREC;
   "%error"[-_]"verbose"   return PERCENT_ERROR_VERBOSE;
   "%expect"               return PERCENT_EXPECT;
   "%file-prefix"          return PERCENT_FILE_PREFIX;
   "%fixed"[-_]"output"[-_]"files"   return PERCENT_YACC;
   "%error"[-_]"verbose"   return PERCENT_ERROR_VERBOSE;
   "%expect"               return PERCENT_EXPECT;
   "%file-prefix"          return PERCENT_FILE_PREFIX;
   "%fixed"[-_]"output"[-_]"files"   return PERCENT_YACC;
+  "%glr-parser"           return PERCENT_GLR_PARSER;
   "%left"                 return PERCENT_LEFT;
   "%locations"            return PERCENT_LOCATIONS;
   "%left"                 return PERCENT_LEFT;
   "%locations"            return PERCENT_LOCATIONS;
+  "%merge"               return PERCENT_MERGE;
   "%name"[-_]"prefix"     return PERCENT_NAME_PREFIX;
   "%no"[-_]"lines"        return PERCENT_NO_LINES;
   "%nonassoc"             return PERCENT_NONASSOC;
   "%nterm"                return PERCENT_NTERM;
   "%output"               return PERCENT_OUTPUT;
   "%name"[-_]"prefix"     return PERCENT_NAME_PREFIX;
   "%no"[-_]"lines"        return PERCENT_NO_LINES;
   "%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;
   "%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;
   "%skeleton"             return PERCENT_SKELETON;
   "%start"                return PERCENT_START;
   "%term"                 return PERCENT_TOKEN;
@@ -148,100 +203,139 @@ blanks   [ \t\f]+
   "%verbose"              return PERCENT_VERBOSE;
   "%yacc"                 return PERCENT_YACC;
 
   "%verbose"              return PERCENT_VERBOSE;
   "%yacc"                 return PERCENT_YACC;
 
+  {directive} {
+    complain_at (*loc, _("invalid directive: %s"), quote (yytext));
+  }
+
   "="                     return EQUAL;
   "="                     return EQUAL;
-  ":"                     return COLON;
-  "|"                     return PIPE;
+  "|"                     rule_length = 0; return PIPE;
   ";"                     return SEMICOLON;
 
   ";"                     return SEMICOLON;
 
-  {eols}      YY_LINES; YY_STEP;
-  {blanks}    YY_STEP;
-  {id}        {
-    yylval->symbol = getsym (yytext, *yylloc);
-    return ID;
+  "," {
+    warn_at (*loc, _("stray `,' treated as white space"));
+  }
+
+  {id} {
+    val->symbol = symbol_get (yytext, *loc);
+    id_loc = *loc;
+    rule_length++;
+    BEGIN SC_AFTER_IDENTIFIER;
   }
 
   }
 
-  {int}       yylval->integer = strtol (yytext, 0, 10); return INT;
+  {int} {
+    unsigned long num;
+    set_errno (0);
+    num = strtoul (yytext, 0, 10);
+    if (INT_MAX < num || get_errno ())
+      {
+       complain_at (*loc, _("integer out of range: %s"), quote (yytext));
+       num = INT_MAX;
+      }
+    val->integer = num;
+    return INT;
+  }
 
   /* Characters.  We don't check there is only one.  */
 
   /* 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. */
-  "/*"        yy_push_state (SC_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; yy_push_state (SC_BRACED_CODE);
+  "{" {
+    STRING_GROW;
+    braces_level = 0;
+    code_start = loc->start;
+    BEGIN SC_BRACED_CODE;
+  }
 
   /* A type. */
 
   /* A type. */
-  "<"[^>]+">" {
-    obstack_grow (&string_obstack, yytext + 1, yyleng - 2);
-    YY_OBS_FINISH;
-    yylval->string = last_string;
+  "<"{tag}">" {
+    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;
   }
 
-  .           {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": invalid character: `%c'\n", *yytext);
-    YY_STEP;
+  . {
+    complain_at (*loc, _("invalid character: %s"), quote (yytext));
   }
 }
 
 
   }
 }
 
 
-  /*------------------------------------------------------------.
-  | 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.   |
-  `------------------------------------------------------------*/
+  /*-----------------------------------------------------------------.
+  | Scanning after an identifier, checking whether a colon is next.  |
+  `-----------------------------------------------------------------*/
 
 
-<SC_COMMENT,SC_STRING,SC_CHARACTER,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
+<SC_AFTER_IDENTIFIER>
 {
 {
-  \[          if (YY_START != SC_COMMENT) obstack_sgrow (&string_obstack, "@<:@");
-  \]          if (YY_START != SC_COMMENT) 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;
+  }
 }
 
 
 }
 
 
+  /*---------------------------------------------------------------.
+  | Scanning a Yacc comment.  The initial `/ *' is already eaten.  |
+  `---------------------------------------------------------------*/
+
+<SC_YACC_COMMENT>
+{
+  "*/"     BEGIN context_state;
+  .|\n    ;
+  <<EOF>>  unexpected_end_of_file (token_start, "*/");
+}
+
 
 
-  /*-----------------------------------------------------------.
-  | Scanning a C comment. The initial `/ *' is already eaten.  |
-  `-----------------------------------------------------------*/
+  /*------------------------------------------------------------.
+  | Scanning a C comment.  The initial `/ *' is already eaten.  |
+  `------------------------------------------------------------*/
 
 <SC_COMMENT>
 {
 
 <SC_COMMENT>
 {
-  "*/" { /* End of the comment. */
-    if (yy_top_state () == INITIAL)
-      {
-       YY_STEP;
-      }
-    else
-      {
-       YY_OBS_GROW;
-      }
-    yy_pop_state ();
-  }
+  "*"{splice}"/"  STRING_GROW; BEGIN context_state;
+  <<EOF>>        unexpected_end_of_file (token_start, "*/");
+}
 
 
-  [^\[\]*\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;
 
 
-  <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a comment\n");
-    yy_pop_state ();
-  }
+  /*--------------------------------------------------------------.
+  | Scanning a line comment.  The initial `//' is already eaten.  |
+  `--------------------------------------------------------------*/
+
+<SC_LINE_COMMENT>
+{
+  "\n"          STRING_GROW; BEGIN context_state;
+  {splice}      STRING_GROW;
+  <<EOF>>       BEGIN context_state;
 }
 
 
 }
 
 
@@ -252,28 +346,18 @@ blanks   [ \t\f]+
 
 <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++;
+    BEGIN INITIAL;
     return STRING;
   }
 
     return STRING;
   }
 
-  [^\"\n\r\\]+      YY_OBS_GROW;
-
-  {eols}    obstack_1grow (&string_obstack, '\n'); YY_LINES;
-
-  <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a string\n");
-    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, "\"");
 }
 
   /*---------------------------------------------------------------.
 }
 
   /*---------------------------------------------------------------.
@@ -283,33 +367,22 @@ blanks   [ \t\f]+
 
 <SC_ESCAPED_CHARACTER>
 {
 
 <SC_ESCAPED_CHARACTER>
 {
-  \' {
-    YY_OBS_GROW;
-    assert (yy_top_state () == INITIAL);
-    {
-      YY_OBS_FINISH;
-      yylval->symbol = getsym (last_string, *yylloc);
-      symbol_class_set (yylval->symbol, token_sym);
-      symbol_user_token_number_set (yylval->symbol, last_string[1]);
-      YY_OBS_FREE;
-      yy_pop_state ();
-      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;
   }
 
   }
 
-  [^\'\n\r\\]      YY_OBS_GROW;
-
-  {eols}    obstack_1grow (&string_obstack, '\n'); YY_LINES;
-
-  <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a character\n");
-    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, "'");
 }
 
 
 }
 
 
@@ -319,34 +392,45 @@ blanks   [ \t\f]+
 
 <SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>
 {
 
 <SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>
 {
-  \\[0-7]{3}           {
-    long c = strtol (yytext + 1, 0, 8);
-    if (c > 255)
-      {
-       LOCATION_PRINT (stderr, *yylloc);
-       fprintf (stderr, ": invalid escape: %s\n", yytext);
-       YY_STEP;
-      }
+  \\[0-7]{1,3} {
+    unsigned long c = strtoul (yytext + 1, 0, 8);
+    if (UCHAR_MAX < c)
+      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]{2}    {
-    obstack_1grow (&string_obstack, strtol (yytext + 2, 0, 16));
+  \\x[0-9abcdefABCDEF]+ {
+    unsigned long c;
+    set_errno (0);
+    c = strtoul (yytext + 2, 0, 16);
+    if (UCHAR_MAX < c || get_errno ())
+      complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
+    else
+      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]);
-  \\.  {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unrecognized escape: %s\n", yytext);
-    YY_OBS_GROW;
+  \\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)
+      complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
+    else
+      obstack_1grow (&obstack_for_string, c);
+  }
+  \\(.|\n)     {
+    complain_at (*loc, _("unrecognized escape sequence: %s"), quote (yytext));
+    STRING_GROW;
   }
 }
 
   }
 }
 
@@ -358,23 +442,9 @@ blanks   [ \t\f]+
 
 <SC_CHARACTER>
 {
 
 <SC_CHARACTER>
 {
-  \' {
-    YY_OBS_GROW;
-    assert (yy_top_state () != INITIAL);
-    yy_pop_state ();
-  }
-
-  [^\[\]\'\n\r\\]      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 character\n");
-    assert (yy_top_state () != INITIAL);
-    yy_pop_state ();
-  }
+  "'"                  STRING_GROW; BEGIN context_state;
+  \\{splice}[^$@\[\]]  STRING_GROW;
+  <<EOF>>              unexpected_end_of_file (token_start, "'");
 }
 
 
 }
 
 
@@ -385,23 +455,9 @@ blanks   [ \t\f]+
 
 <SC_STRING>
 {
 
 <SC_STRING>
 {
-  \" {
-    assert (yy_top_state () != INITIAL);
-    YY_OBS_GROW;
-    yy_pop_state ();
-  }
-
-  [^\[\]\"\n\r\\]+      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 string\n");
-    assert (yy_top_state () != INITIAL);
-    yy_pop_state ();
-  }
+  "\""                 STRING_GROW; BEGIN context_state;
+  \\{splice}[^$@\[\]]  STRING_GROW;
+  <<EOF>>              unexpected_end_of_file (token_start, "\"");
 }
 
 
 }
 
 
@@ -411,15 +467,29 @@ blanks   [ \t\f]+
 
 <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. */
-  "/*"        YY_OBS_GROW; yy_push_state (SC_COMMENT);
-  "//".*      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;
+  }
 }
 
 
 }
 
 
@@ -430,39 +500,32 @@ blanks   [ \t\f]+
 
 <SC_BRACED_CODE>
 {
 
 <SC_BRACED_CODE>
 {
+  "{"|"<"{splice}"%"  STRING_GROW; braces_level++;
+  "%"{splice}">"      STRING_GROW; braces_level--;
   "}" {
   "}" {
-    YY_OBS_GROW;
-    if (--braces_level == 0)
+    STRING_GROW;
+    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++;
+       BEGIN INITIAL;
        return BRACED_CODE;
       }
   }
 
        return BRACED_CODE;
       }
   }
 
-  "{"                  YY_OBS_GROW; braces_level++;
+  /* Tokenize `<<%' correctly (as `<<' `%') rather than incorrrectly
+     (as `<' `<%').  */
+  "<"{splice}"<"  STRING_GROW;
 
 
-  "$"("<"[^>]+">")?(-?[0-9]+|"$") { handle_dollar (current_braced_code,
-                                                  yytext, *yylloc); }
+  "$"("<"{tag}">")?(-?[0-9]+|"$") { handle_dollar (current_braced_code,
+                                                  yytext, *loc); }
   "@"(-?[0-9]+|"$")               { handle_at (current_braced_code,
   "@"(-?[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;
-
-  <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a braced code\n");
-    yy_pop_state ();
-    YY_OBS_FINISH;
-    yylval->string = last_string;
-    return BRACED_CODE;
-  }
+                                              yytext, *loc); }
 
 
+  <<EOF>>  unexpected_end_of_file (code_start, "}");
 }
 
 
 }
 
 
@@ -473,62 +536,148 @@ blanks   [ \t\f]+
 <SC_PROLOGUE>
 {
   "%}" {
 <SC_PROLOGUE>
 {
   "%}" {
-    yy_pop_state ();
-    YY_OBS_FINISH;
-    yylval->string = last_string;
-    return PROLOGUE;
-  }
-
-  [^%\[\]/\'\"\n\r]+     YY_OBS_GROW;
-  "%"+[^%\}\n\r]+ YY_OBS_GROW;
-  {eols}         YY_OBS_GROW; YY_LINES;
-
-  <<EOF>> {
-    LOCATION_PRINT (stderr, *yylloc);
-    fprintf (stderr, ": unexpected end of file in a prologue\n");
-    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;
   }
 
+  <<EOF>>  unexpected_end_of_file (code_start, "%}");
 }
 
 
   /*---------------------------------------------------------------.
   | Scanning the epilogue (everything after the second "%%", which |
 }
 
 
   /*---------------------------------------------------------------.
   | Scanning the epilogue (everything after the second "%%", which |
-  | has already been eaten                                       |
+  | has already been eaten).                                       |
   `---------------------------------------------------------------*/
 
 <SC_EPILOGUE>
 {
   `---------------------------------------------------------------*/
 
 <SC_EPILOGUE>
 {
-  ([^\[\]]|{eols})+  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;
+}
+
+
 /*------------------------------------------------------------------.
 /*------------------------------------------------------------------.
-| CP is pointing to a wannabee semantic value (i.e., a `$').        |
+| TEXT is pointing to a wannabee semantic value (i.e., a `$').      |
 |                                                                   |
 | Possible inputs: $[<TYPENAME>]($|integer)                         |
 |                                                                   |
 |                                                                   |
 | Possible inputs: $[<TYPENAME>]($|integer)                         |
 |                                                                   |
-| Output to the STRING_OBSTACK a reference to this semantic value.  |
+| Output to OBSTACK_FOR_STRING a reference to this semantic value.  |
 `------------------------------------------------------------------*/
 
 static inline void
 `------------------------------------------------------------------*/
 
 static inline void
-handle_action_dollar (char *cp, location_t location)
+handle_action_dollar (char *text, location loc)
 {
   const char *type_name = NULL;
 {
   const char *type_name = NULL;
-
-  ++cp;
+  char *cp = text + 1;
 
   /* Get the type name if explicit. */
   if (*cp == '<')
 
   /* Get the type name if explicit. */
   if (*cp == '<')
@@ -543,68 +692,54 @@ handle_action_dollar (char *cp, 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);
     }
                      "]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;
+      set_errno (0);
+      num = strtol (cp, 0, 10);
+
+      if (INT_MIN <= num && num <= rule_length && ! get_errno ())
        {
        {
+         int n = num;
          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,
+         obstack_fgrow3 (&obstack_for_string,
                          "]b4_rhs_value([%d], [%d], [%s])[",
                          rule_length, n, type_name);
        }
                          "]b4_rhs_value([%d], [%d], [%s])[",
                          rule_length, n, type_name);
        }
-    }
-  else
-    {
-      char buf[] = "$c";
-      buf[1] = *cp;
-      complain_at (location, _("%s is invalid"), quote (buf));
+      else
+       complain_at (loc, _("integer out of range: %s"), quote (text));
     }
 }
 
 
     }
 }
 
 
-/*---------------------------------------.
-| CP is pointing to $$ in a destructor.  |
-`---------------------------------------*/
+/*---------------------------------------------------------------.
+| TEXT is expected to be $$ in some code associated to a symbol: |
+| destructor or printer.                                         |
+`---------------------------------------------------------------*/
 
 static inline void
 
 static inline void
-handle_destructor_dollar (char *cp, location_t location)
+handle_symbol_code_dollar (char *text, location loc)
 {
 {
-  ++cp;
+  char *cp = text + 1;
   if (*cp == '$')
   if (*cp == '$')
-    {
-      obstack_sgrow (&string_obstack, "]b4_dollar_dollar[");
-    }
+    obstack_sgrow (&obstack_for_string, "]b4_dollar_dollar[");
   else
   else
-    {
-      char buf[] = "$c";
-      buf[1] = *cp;
-      complain_at (location, _("%s is invalid"), quote (buf));
-    }
+    complain_at (loc, _("invalid value: %s"), quote (text));
 }
 
 
 }
 
 
@@ -614,17 +749,17 @@ handle_destructor_dollar (char *cp, 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:
       break;
 
     case destructor_braced_code:
-      handle_destructor_dollar (text, location);
+    case printer_braced_code:
+      handle_symbol_code_dollar (text, loc);
       break;
     }
 }
       break;
     }
 }
@@ -632,61 +767,50 @@ 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;
   locations_flag = 1;
-  ++text;
 
 
-  if (*text == '$')
+  if (*cp == '$')
     {
     {
-      obstack_sgrow (&string_obstack, "]b4_lhs_location[");
-    }
-  else if (isdigit (*text) || *text == '-')
-    {
-      /* 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 (text, &text, 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);
+      obstack_sgrow (&obstack_for_string, "]b4_lhs_location[");
     }
   else
     {
     }
   else
     {
-      char buf[] = "@c";
-      buf[1] = *text;
-      complain_at (location, _("%s is invalid"), quote (buf));
+      long num;
+      set_errno (0);
+      num = strtol (cp, 0, 10);
+
+      if (INT_MIN <= num && num <= rule_length && ! get_errno ())
+       {
+         int n = num;
+         obstack_fgrow2 (&obstack_for_string, "]b4_rhs_location([%d], [%d])[",
+                         rule_length, n);
+       }
+      else
+       complain_at (loc, _("integer out of range: %s"), quote (text));
     }
 }
 
 
     }
 }
 
 
-/*--------------------------------------------.
-| TEXT is expexted tp be @$ in a destructor.  |
-`--------------------------------------------*/
+/*---------------------------------------------------------------.
+| TEXT is expected to be @$ in some code associated to a symbol: |
+| destructor or printer.                                         |
+`---------------------------------------------------------------*/
 
 static inline void
 
 static inline void
-handle_destructor_at (char *text, location_t location)
+handle_symbol_code_at (char *text, location loc)
 {
 {
-  ++text;
-  if (*text == '$')
-    {
-      obstack_sgrow (&string_obstack, "]b4_at_dollar[");
-    }
+  char *cp = text + 1;
+  if (*cp == '$')
+    obstack_sgrow (&obstack_for_string, "]b4_at_dollar[");
   else
   else
-    {
-      char buf[] = "$c";
-      buf[1] = *text;
-      complain_at (location, _("%s is invalid"), quote (buf));
-    }
+    complain_at (loc, _("invalid value: %s"), quote (text));
 }
 
 
 }
 
 
@@ -696,22 +820,120 @@ handle_destructor_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:
       break;
 
     case destructor_braced_code:
-      handle_destructor_at (text, location);
+    case printer_braced_code:
+      handle_symbol_code_at (text, loc);
       break;
     }
 }
 
 
       break;
     }
 }
 
 
+/*------------------------------------------------------------------.
+| 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 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[] =
+      {
+       '\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)
+{
+  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.  |
 `-------------------------*/
@@ -719,7 +941,7 @@ handle_at (braced_code_t braced_code_kind,
 void
 scanner_initialize (void)
 {
 void
 scanner_initialize (void)
 {
-  obstack_init (&string_obstack);
+  obstack_init (&obstack_for_string);
 }
 
 
 }
 
 
@@ -730,5 +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);
 }
 }