X-Git-Url: https://git.saurik.com/bison.git/blobdiff_plain/29c017256a980c07f898d8a4e5f6a630a5bf70b7..d45708d4184fe4f1f6e5abd0e0bfaa18eff02f61:/src/scan-gram.l

diff --git a/src/scan-gram.l b/src/scan-gram.l
index e8620646..61716810 100644
--- a/src/scan-gram.l
+++ b/src/scan-gram.l
@@ -1,11 +1,12 @@
 /* Bison Grammar Scanner                             -*- C -*-
-   Copyright (C) 2002 Free Software Foundation, Inc.
+
+   Copyright (C) 2002-2007, 2009-2010 Free Software Foundation, Inc.
 
    This file is part of Bison, the GNU Compiler Compiler.
 
-   This program is free software; you can redistribute it and/or modify
+   This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -14,182 +15,84 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307  USA
-*/
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-%option debug nodefault noyywrap nounput never-interactive stack
+%option debug nodefault noinput nounput noyywrap never-interactive
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
-#include "system.h"
-#include "mbswidth.h"
+/* Work around a bug in flex 2.5.31.  See Debian bug 333231
+   <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>.  */
+#undef gram_wrap
+#define gram_wrap() 1
+
+#define FLEX_PREFIX(Id) gram_ ## Id
+#include "flex-scanner.h"
+
 #include "complain.h"
-#include "quote.h"
-#include "getargs.h"
+#include "files.h"
 #include "gram.h"
+#include "quotearg.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;
-	}
-    }
-
-  return s;
-}
+#include <ctype.h>
+#include <mbswidth.h>
+#include <quote.h>
 
+#include "scan-gram.h"
 
-/* Extend *LOC to account for token TOKEN of size SIZE.  */
+#define YY_DECL GRAM_LEX_DECL
 
-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;
-      }
+#define YY_USER_INIT					\
+   code_start = scanner_cursor = loc->start;		\
 
-  loc->last_line = line;
-  loc->last_column = column + mbsnwidth (p0, p - p0, 0);
-}
+/* Location of scanner cursor.  */
+static boundary scanner_cursor;
 
+#define YY_USER_ACTION  location_compute (loc, &scanner_cursor, yytext, yyleng);
 
+static size_t no_cr_read (FILE *, char *, size_t);
+#define YY_INPUT(buf, result, size) ((result) = no_cr_read (yyin, buf, size))
 
-/* STRING_OBSTACK -- Used to store all the characters that we need to
-   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.  */
-
-static struct obstack string_obstack;
-char *last_string;
-
-#define YY_OBS_GROW   \
-  obstack_grow (&string_obstack, yytext, yyleng)
-
-#define YY_OBS_FINISH					\
-  do {							\
-    obstack_1grow (&string_obstack, '\0');		\
-    last_string = obstack_finish (&string_obstack);	\
-  } while (0)
-
-#define YY_OBS_FREE						\
-  do {								\
-    obstack_free (&string_obstack, last_string);		\
-  } while (0)
+/* A string representing the most recently saved token.  */
+static char *last_string;
 
 void
-scanner_last_string_free (void)
+gram_scanner_last_string_free (void)
 {
-  YY_OBS_FREE;
+  STRING_FREE;
 }
 
-
-static int braces_level = 0;
-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
-   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 *, location);
+static unsigned long int scan_integer (char const *p, int base, location loc);
 static int convert_ucn_to_byte (char const *hex_text);
+static void unexpected_eof (boundary, char const *);
+static void unexpected_newline (boundary, char const *);
 
 %}
-%x SC_COMMENT SC_LINE_COMMENT SC_YACC_COMMENT
-%x SC_STRING SC_CHARACTER
+ /* A C-like comment in directives/rules. */
+%x SC_YACC_COMMENT
+ /* Strings and characters in directives/rules. */
 %x SC_ESCAPED_STRING SC_ESCAPED_CHARACTER
-%x SC_BRACED_CODE SC_PROLOGUE SC_EPILOGUE
+ /* A identifier was just read in directives/rules.  Special state
+    to capture the sequence `identifier :'. */
+%x SC_AFTER_IDENTIFIER
+
+ /* Three types of user code:
+    - prologue (code between `%{' `%}' in the first section, before %%);
+    - actions, printers, union, etc, (between braced in the middle section);
+    - epilogue (everything after the second %%). */
+%x SC_PROLOGUE SC_BRACED_CODE SC_EPILOGUE
+ /* C and C++ comments in code. */
+%x SC_COMMENT SC_LINE_COMMENT
+ /* Strings and characters in code. */
+%x SC_STRING SC_CHARACTER
 
 letter	  [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
 id	  {letter}({letter}|[0-9])*
 directive %{letter}({letter}|[0-9]|-)*
-int 	  [0-9]+
+int	  [0-9]+
 
 /* POSIX says that a tag must be both an id and a C union member, but
    historically almost any character is allowed in a tag.  We disallow
@@ -202,132 +105,190 @@ splice	 (\\[ \f\t\v]*\n)*
 
 %%
 %{
-  /* At each yylex invocation, mark the current position as the
-     start of the next token.  */
-  YY_STEP;
+  /* 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 (= empty_location);
+
+  /* Where containing code started, when applicable.  Its initial
+     value is relevant only when yylex is invoked in the SC_EPILOGUE
+     start condition.  */
+  boundary code_start = scanner_cursor;
+
+  /* Where containing comment or string or character literal started,
+     when applicable.  */
+  boundary token_start IF_LINT (= scanner_cursor);
 %}
 
 
+  /*-----------------------.
+  | Scanning white space.  |
+  `-----------------------*/
+
+<INITIAL,SC_AFTER_IDENTIFIER>
+{
+  /* Comments and white space.  */
+  ","	       warn_at (*loc, _("stray `,' treated as white space"));
+  [ \f\n\t\v]  |
+  "//".*       ;
+  "/*" {
+    token_start = loc->start;
+    context_state = YY_START;
+    BEGIN SC_YACC_COMMENT;
+  }
+
+  /* #line directives are not documented, and may be withdrawn or
+     modified in future versions of Bison.  */
+  ^"#line "{int}" \"".*"\"\n" {
+    handle_syncline (yytext + sizeof "#line " - 1, *loc);
+  }
+}
+
+
   /*----------------------------.
   | Scanning Bison directives.  |
   `----------------------------*/
 <INITIAL>
 {
-  "%binary"               return PERCENT_NONASSOC;
-  "%debug"                return PERCENT_DEBUG;
-  "%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;
+  "%binary"               	    return PERCENT_NONASSOC;
+  "%code"                           return PERCENT_CODE;
+  "%debug"                	    return PERCENT_DEBUG;
+  "%default"[-_]"prec"    	    return PERCENT_DEFAULT_PREC;
+  "%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;
+  "%expect"[-_]"rr"	  	    return PERCENT_EXPECT_RR;
+  "%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;
-  "%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;
-  "%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;
-  "%token"                return PERCENT_TOKEN;
-  "%token"[-_]"table"     return PERCENT_TOKEN_TABLE;
-  "%type"                 return PERCENT_TYPE;
-  "%union"                return PERCENT_UNION;
-  "%verbose"              return PERCENT_VERBOSE;
-  "%yacc"                 return PERCENT_YACC;
-
-  {directive}             {
-    complain_at (*yylloc, _("invalid directive: %s"), quote (yytext));
-    YY_STEP;
+  "%initial-action"       	    return PERCENT_INITIAL_ACTION;
+  "%glr-parser"           	    return PERCENT_GLR_PARSER;
+  "%language"                 	    return PERCENT_LANGUAGE;
+  "%left"                 	    return PERCENT_LEFT;
+  "%lex-param"		  	    return PERCENT_LEX_PARAM;
+  "%locations"            	    return PERCENT_LOCATIONS;
+  "%merge"		  	    return PERCENT_MERGE;
+  "%name"[-_]"prefix"     	    return PERCENT_NAME_PREFIX;
+  "%no"[-_]"default"[-_]"prec"	    return PERCENT_NO_DEFAULT_PREC;
+  "%no"[-_]"lines"        	    return PERCENT_NO_LINES;
+  "%nonassoc"             	    return PERCENT_NONASSOC;
+  "%nondeterministic-parser"   	    return PERCENT_NONDETERMINISTIC_PARSER;
+  "%nterm"                	    return PERCENT_NTERM;
+  "%output"               	    return PERCENT_OUTPUT;
+  "%parse-param"	  	    return PERCENT_PARSE_PARAM;
+  "%prec"                 	    return PERCENT_PREC;
+  "%printer"              	    return PERCENT_PRINTER;
+  "%pure"[-_]"parser"     	    return PERCENT_PURE_PARSER;
+  "%require"              	    return PERCENT_REQUIRE;
+  "%right"                	    return PERCENT_RIGHT;
+  "%skeleton"             	    return PERCENT_SKELETON;
+  "%start"                	    return PERCENT_START;
+  "%term"                 	    return PERCENT_TOKEN;
+  "%token"                	    return PERCENT_TOKEN;
+  "%token"[-_]"table"     	    return PERCENT_TOKEN_TABLE;
+  "%type"                 	    return PERCENT_TYPE;
+  "%union"		  	    return PERCENT_UNION;
+  "%verbose"              	    return PERCENT_VERBOSE;
+  "%yacc"                 	    return PERCENT_YACC;
+
+  {directive} {
+    complain_at (*loc, _("invalid directive: %s"), quote (yytext));
   }
 
   "="                     return EQUAL;
-  ":"                     rule_length = 0; return COLON;
-  "|"                     rule_length = 0; return PIPE;
-  ","                     return COMMA;
+  "|"                     return PIPE;
   ";"                     return SEMICOLON;
+  "<*>"                   return TYPE_TAG_ANY;
+  "<>"                    return TYPE_TAG_NONE;
 
-  [ \f\n\t\v]+  YY_STEP;
-
-  {id}        {
-    yylval->symbol = symbol_get (yytext, *yylloc);
-    rule_length++;
-    return ID;
+  {id} {
+    val->uniqstr = uniqstr_new (yytext);
+    id_loc = *loc;
+    BEGIN SC_AFTER_IDENTIFIER;
   }
 
   {int} {
-    unsigned long num;
-    errno = 0;
-    num = strtoul (yytext, 0, 10);
-    if (INT_MAX < num || errno)
-      {
-	complain_at (*yylloc, _("invalid value: %s"), quote (yytext));
-	num = INT_MAX;
-      }
-    yylval->integer = num;
+    val->integer = scan_integer (yytext, 10, *loc);
+    return INT;
+  }
+  0[xX][0-9abcdefABCDEF]+ {
+    val->integer = scan_integer (yytext, 16, *loc);
     return INT;
   }
 
   /* Characters.  We don't check there is only one.  */
-  "'"         YY_OBS_GROW; yy_push_state (SC_ESCAPED_CHARACTER);
+  "'"	      STRING_GROW; token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
 
   /* Strings. */
-  "\""        YY_OBS_GROW; yy_push_state (SC_ESCAPED_STRING);
-
-  /* Comments. */
-  "/*"        BEGIN SC_YACC_COMMENT;
-  "//".*      YY_STEP;
+  "\""	      token_start = loc->start; BEGIN SC_ESCAPED_STRING;
 
   /* Prologue. */
-  "%{"        yy_push_state (SC_PROLOGUE);
+  "%{"        code_start = loc->start; BEGIN SC_PROLOGUE;
 
   /* 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. */
   "<"{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;
   }
 
-
-  "%%"   {
+  "%%" {
+    static int percent_percent_count;
     if (++percent_percent_count == 2)
-      yy_push_state (SC_EPILOGUE);
+      BEGIN SC_EPILOGUE;
     return PERCENT_PERCENT;
   }
 
-  .           {
-    complain_at (*yylloc, _("invalid character: %s"), quote (yytext));
-    YY_STEP;
+  . {
+    complain_at (*loc, _("invalid character: %s"), quote (yytext));
+  }
+
+  <<EOF>> {
+    loc->start = loc->end = scanner_cursor;
+    yyterminate ();
   }
 }
 
 
-  /*-------------------------------------------------------------------.
-  | Whatever the start condition (but those which correspond to        |
-  | entities `swallowed' by Bison: SC_YACC_COMMENT, SC_ESCAPED_STRING, |
-  | and SC_ESCAPED_CHARACTER), no M4 character must escape as is.      |
-  `-------------------------------------------------------------------*/
+  /*-----------------------------------------------------------------.
+  | Scanning after an identifier, checking whether a colon is next.  |
+  `-----------------------------------------------------------------*/
 
-<SC_COMMENT,SC_LINE_COMMENT,SC_STRING,SC_CHARACTER,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
+<SC_AFTER_IDENTIFIER>
 {
-  \[	obstack_sgrow (&string_obstack, "@<:@");
-  \]	obstack_sgrow (&string_obstack, "@:>@");
+  ":" {
+    *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;
+  }
 }
 
 
@@ -337,17 +298,9 @@ splice	 (\\[ \f\t\v]*\n)*
 
 <SC_YACC_COMMENT>
 {
-  "*/" {
-    YY_STEP;
-    BEGIN INITIAL;
-  }
-
-  [^*]+|"*"  ;
-
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a comment"));
-    BEGIN INITIAL;
-  }
+  "*/"     BEGIN context_state;
+  .|\n	   ;
+  <<EOF>>  unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
@@ -357,13 +310,8 @@ splice	 (\\[ \f\t\v]*\n)*
 
 <SC_COMMENT>
 {
-  "*"{splice}"/"  YY_OBS_GROW; yy_pop_state ();
-  [^*\[\]]+|"*"   YY_OBS_GROW;
-
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a comment"));
-    yy_pop_state ();
-  }
+  "*"{splice}"/"  STRING_GROW; BEGIN context_state;
+  <<EOF>>	  unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
@@ -373,76 +321,75 @@ splice	 (\\[ \f\t\v]*\n)*
 
 <SC_LINE_COMMENT>
 {
-  "\n"			 YY_OBS_GROW; yy_pop_state ();
-  ([^\n\[\]]|{splice})+  YY_OBS_GROW;
-  <<EOF>>		 yy_pop_state ();
+  "\n"		 STRING_GROW; BEGIN context_state;
+  {splice}	 STRING_GROW;
+  <<EOF>>	 BEGIN context_state;
 }
 
 
-  /*----------------------------------------------------------------.
-  | Scanning a C string, including its escapes.  The initial `"' is |
-  | already eaten.                                                  |
-  `----------------------------------------------------------------*/
+  /*------------------------------------------------.
+  | Scanning a Bison string, including its escapes. |
+  | The initial quote is already eaten.             |
+  `------------------------------------------------*/
 
 <SC_ESCAPED_STRING>
 {
-  "\"" {
-    assert (yy_top_state () == INITIAL);
-    YY_OBS_GROW;
-    YY_OBS_FINISH;
-    yylval->string = last_string;
-    yy_pop_state ();
-    rule_length++;
+  "\""|"\n" {
+    if (yytext[0] == '\n')
+      unexpected_newline (token_start, "\"");
+    STRING_FINISH;
+    loc->start = token_start;
+    val->chars = last_string;
+    BEGIN INITIAL;
     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 ();
+    unexpected_eof (token_start, "\"");
+    STRING_FINISH;
+    loc->start = token_start;
+    val->chars = last_string;
+    BEGIN INITIAL;
     return STRING;
   }
 }
 
-  /*---------------------------------------------------------------.
-  | Scanning a C character, decoding its escapes.  The initial "'" |
-  | is already eaten.                                              |
-  `---------------------------------------------------------------*/
+  /*----------------------------------------------------------.
+  | Scanning a Bison character literal, decoding its escapes. |
+  | The initial quote is already eaten.			      |
+  `----------------------------------------------------------*/
 
 <SC_ESCAPED_CHARACTER>
 {
-  "'" {
-    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;
-    }
+  "'"|"\n" {
+    if (yytext[0] == '\n')
+      unexpected_newline (token_start, "'");
+    STRING_GROW;
+    STRING_FINISH;
+    loc->start = token_start;
+    val->character = last_string[1];
+    STRING_FREE;
+    BEGIN INITIAL;
+    return CHAR;
   }
-
-  [^\'\\]+  YY_OBS_GROW;
-
   <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a character"));
-    assert (yy_top_state () == INITIAL);
-    YY_OBS_FINISH;
-    yylval->string = last_string;
-    yy_pop_state ();
-    return CHARACTER;
+    unexpected_eof (token_start, "'");
+    STRING_FINISH;
+    loc->start = token_start;
+    if (strlen(last_string) > 1)
+      val->character = last_string[1];
+    else
+      val->character = last_string[0];
+    STRING_FREE;
+    BEGIN INITIAL;
+    return CHAR;
   }
 }
 
+<SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING>
+{
+  \0	    complain_at (*loc, _("invalid null character"));
+}
+
 
   /*----------------------------.
   | Decode escaped characters.  |
@@ -451,107 +398,75 @@ splice	 (\\[ \f\t\v]*\n)*
 <SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>
 {
   \\[0-7]{1,3} {
-    unsigned long c = strtoul (yytext + 1, 0, 8);
-    if (UCHAR_MAX < c)
-      {
-	complain_at (*yylloc, _("invalid escape: %s"), quote (yytext));
-	YY_STEP;
-      }
+    unsigned long int c = strtoul (yytext + 1, NULL, 8);
+    if (!c || UCHAR_MAX < c)
+      complain_at (*loc, _("invalid number after \\-escape: %s"),
+                   yytext+1);
     else
-      obstack_1grow (&string_obstack, c);
+      obstack_1grow (&obstack_for_string, c);
   }
 
-  \\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: %s"), quote (yytext));
-	YY_STEP;
-      }
+  \\x[0-9abcdefABCDEF]+ {
+    verify (UCHAR_MAX < ULONG_MAX);
+    unsigned long int c = strtoul (yytext + 2, NULL, 16);
+    if (!c || UCHAR_MAX < c)
+      complain_at (*loc, _("invalid number after \\-escape: %s"),
+                   yytext+1);
     else
-      obstack_1grow (&string_obstack, 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} {
+      obstack_1grow (&obstack_for_string, c);
+  }
+
+  \\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 (*yylloc, _("invalid escape: %s"), quote (yytext));
-	YY_STEP;
-      }
+    if (c <= 0)
+      complain_at (*loc, _("invalid number after \\-escape: %s"),
+                   yytext+1);
     else
-      obstack_1grow (&string_obstack, c);
+      obstack_1grow (&obstack_for_string, c);
   }
   \\(.|\n)	{
-    complain_at (*yylloc, _("unrecognized escape: %s"), quote (yytext));
-    YY_OBS_GROW;
+    char const *p = yytext + 1;
+    /* Quote only if escaping won't make the character visible.  */
+    if (isspace ((unsigned char) *p) && isprint ((unsigned char) *p))
+      p = quote (p);
+    else
+      p = quotearg_style_mem (escape_quoting_style, p, 1);
+    complain_at (*loc, _("invalid character after \\-escape: %s"), p);
   }
-  /* FLex wants this rule, in case of a `\<<EOF>>'. */
-  \\                   YY_OBS_GROW;
 }
 
+  /*--------------------------------------------.
+  | Scanning user-code characters and strings.  |
+  `--------------------------------------------*/
 
-  /*----------------------------------------------------------.
-  | Scanning a C character without decoding its escapes.  The |
-  | initial "'" is already eaten.                             |
-  `----------------------------------------------------------*/
+<SC_CHARACTER,SC_STRING>
+{
+  {splice}|\\{splice}[^\n\[\]]	STRING_GROW;
+}
 
 <SC_CHARACTER>
 {
-  "'" {
-    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"));
-    assert (yy_top_state () != INITIAL);
-    yy_pop_state ();
-  }
+  "'"		STRING_GROW; BEGIN context_state;
+  \n		unexpected_newline (token_start, "'"); BEGIN context_state;
+  <<EOF>>	unexpected_eof (token_start, "'"); BEGIN context_state;
 }
 
-
-  /*----------------------------------------------------------------.
-  | Scanning a C string, without decoding its escapes.  The initial |
-  | `"' is already eaten.                                           |
-  `----------------------------------------------------------------*/
-
 <SC_STRING>
 {
-  "\"" {
-    assert (yy_top_state () != INITIAL);
-    YY_OBS_GROW;
-    yy_pop_state ();
-  }
-
-  [^\"\[\]\\]+	       YY_OBS_GROW;
-  \\{splice}[^\[\]]    YY_OBS_GROW;
-  {splice}	       YY_OBS_GROW;
-  /* Needed for `\<<EOF>>', `\\<<newline>>[', and `\\<<newline>>]'.  */
-  \\                   YY_OBS_GROW;
-
-  <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a string"));
-    assert (yy_top_state () != INITIAL);
-    yy_pop_state ();
-  }
+  "\""		STRING_GROW; BEGIN context_state;
+  \n		unexpected_newline (token_start, "\""); BEGIN context_state;
+  <<EOF>>	unexpected_eof (token_start, "\""); BEGIN context_state;
 }
 
 
@@ -561,60 +476,68 @@ splice	 (\\[ \f\t\v]*\n)*
 
 <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;
+  }
 }
 
 
-  /*---------------------------------------------------------------.
-  | Scanning some code in braces (%union and actions). The initial |
-  | "{" is already eaten.                                          |
-  `---------------------------------------------------------------*/
+
+  /*-----------------------------------------------------------.
+  | Scanning some code in braces (actions). The initial "{" is |
+  | already eaten.                                             |
+  `-----------------------------------------------------------*/
 
 <SC_BRACED_CODE>
 {
+  "{"|"<"{splice}"%"  STRING_GROW; braces_level++;
+  "%"{splice}">"      STRING_GROW; braces_level--;
   "}" {
-    YY_OBS_GROW;
-    if (--braces_level == 0)
+    obstack_1grow (&obstack_for_string, '}');
+
+    --braces_level;
+    if (braces_level < 0)
       {
-	yy_pop_state ();
-	YY_OBS_FINISH;
-	yylval->string = last_string;
-	rule_length++;
+	STRING_FINISH;
+	loc->start = code_start;
+	val->code = last_string;
+	BEGIN INITIAL;
 	return BRACED_CODE;
       }
   }
 
-  "{"  	        YY_OBS_GROW; braces_level++;
-
-  "$"("<"{tag}">")?(-?[0-9]+|"$") { handle_dollar (current_braced_code,
-						   yytext, *yylloc); }
-  "@"(-?[0-9]+|"$")               { handle_at (current_braced_code,
-					       yytext, *yylloc); }
-
-  [^$@\[\]/\'\"\{\}]+	YY_OBS_GROW;
-
-  /* A stray $, or /, or etc. */
-  .             YY_OBS_GROW;
+  /* Tokenize `<<%' correctly (as `<<' `%') rather than incorrrectly
+     (as `<' `<%').  */
+  "<"{splice}"<"  STRING_GROW;
 
   <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a braced code"));
-    yy_pop_state ();
-    YY_OBS_FINISH;
-    yylval->string = last_string;
+    unexpected_eof (code_start, "}");
+    STRING_FINISH;
+    loc->start = code_start;
+    val->code = last_string;
+    BEGIN INITIAL;
     return BRACED_CODE;
   }
-
 }
 
 
@@ -625,20 +548,19 @@ splice	 (\\[ \f\t\v]*\n)*
 <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;
   }
 
-  [^%\[\]/\'\"]+     YY_OBS_GROW;
-  "%"                YY_OBS_GROW;
-
   <<EOF>> {
-    complain_at (*yylloc, _("unexpected end of file in a prologue"));
-    yy_pop_state ();
-    YY_OBS_FINISH;
-    yylval->string = last_string;
+    unexpected_eof (code_start, "%}");
+    STRING_FINISH;
+    loc->start = code_start;
+    val->chars = last_string;
+    BEGIN INITIAL;
     return PROLOGUE;
   }
 }
@@ -651,190 +573,90 @@ splice	 (\\[ \f\t\v]*\n)*
 
 <SC_EPILOGUE>
 {
-  [^\[\]]+  YY_OBS_GROW;
-
   <<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;
   }
 }
 
 
+  /*-----------------------------------------------------.
+  | By default, grow the string obstack with the input.  |
+  `-----------------------------------------------------*/
+
+<SC_COMMENT,SC_LINE_COMMENT,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE,SC_STRING,SC_CHARACTER,SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>.	|
+<SC_COMMENT,SC_LINE_COMMENT,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>\n	STRING_GROW;
+
 %%
 
-/*------------------------------------------------------------------.
-| 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.  |
-`------------------------------------------------------------------*/
+/* 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 inline void
-handle_action_dollar (char *text, location_t location)
+static size_t
+no_cr_read (FILE *fp, char *buf, size_t size)
 {
-  const char *type_name = NULL;
-  char *cp = text + 1;
-
-  /* Get the type name if explicit. */
-  if (*cp == '<')
+  size_t bytes_read = fread (buf, 1, size, fp);
+  if (bytes_read)
     {
-      type_name = ++cp;
-      while (*cp != '>')
-	++cp;
-      *cp = '\0';
-      ++cp;
-    }
+      char *w = memchr (buf, '\r', bytes_read);
+      if (w)
+	{
+	  char const *r = ++w;
+	  char const *lim = buf + bytes_read;
 
-  if (*cp == '$')
-    {
-      if (!type_name)
-	type_name = symbol_list_n_type_name_get (current_rule, location, 0);
-      if (!type_name && typed)
-	complain_at (location, _("$$ of `%s' has no declared type"),
-		     current_rule->sym->tag);
-      if (!type_name)
-	type_name = "";
-      obstack_fgrow1 (&string_obstack,
-		      "]b4_lhs_value([%s])[", type_name);
-    }
-  else
-    {
-      long num;
-      errno = 0;
-      num = strtol (cp, 0, 10);
+	  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++;
 
-      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);
-	  if (!type_name && typed)
-	    complain_at (location, _("$%d of `%s' has no declared type"),
-		      n, current_rule->sym->tag);
-	  if (!type_name)
-	    type_name = "";
-	  obstack_fgrow3 (&string_obstack,
-			  "]b4_rhs_value([%d], [%d], [%s])[",
-			  rule_length, n, type_name);
+	      /* Copy until the next '\r'.  */
+	      do
+		{
+		  if (r == lim)
+		    return w - buf;
+		}
+	      while ((*w++ = *r++) != '\r');
+	    }
+
+	  return w - buf;
 	}
-      else
-	complain_at (location, _("invalid value: %s"), quote (text));
     }
-}
-
-
-/*---------------------------------------------------------------.
-| TEXT is expected to be $$ in some code associated to a symbol: |
-| destructor or printer.                                         |
-`---------------------------------------------------------------*/
 
-static inline void
-handle_symbol_code_dollar (char *text, location_t location)
-{
-  char *cp = text + 1;
-  if (*cp == '$')
-    obstack_sgrow (&string_obstack, "]b4_dollar_dollar[");
-  else
-    complain_at (location, _("invalid value: %s"), quote (text));
+  return bytes_read;
 }
 
 
-/*-----------------------------------------------------------------.
-| Dispatch onto handle_action_dollar, or handle_destructor_dollar, |
-| depending upon CODE_KIND.                                        |
-`-----------------------------------------------------------------*/
-
-static void
-handle_dollar (braced_code_t braced_code_kind,
-	       char *text, location_t location)
-{
-  switch (braced_code_kind)
-    {
-    case action_braced_code:
-      handle_action_dollar (text, location);
-      break;
-
-    case destructor_braced_code:
-    case printer_braced_code:
-      handle_symbol_code_dollar (text, location);
-      break;
-    }
-}
-
 
 /*------------------------------------------------------.
-| TEXT is a location token (i.e., a `@...').  Output to |
-| STRING_OBSTACK a reference to this location.          |
+| Scan NUMBER for a base-BASE integer at location LOC.  |
 `------------------------------------------------------*/
 
-static inline void
-handle_action_at (char *text, location_t location)
+static unsigned long int
+scan_integer (char const *number, int base, location loc)
 {
-  char *cp = text + 1;
-  locations_flag = 1;
+  verify (INT_MAX < ULONG_MAX);
+  unsigned long int num = strtoul (number, NULL, base);
 
-  if (*cp == '$')
+  if (INT_MAX < num)
     {
-      obstack_sgrow (&string_obstack, "]b4_lhs_location[");
+      complain_at (loc, _("integer out of range: %s"), quote (number));
+      num = INT_MAX;
     }
-  else
-    {
-      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, _("invalid value: %s"), quote (text));
-    }
-}
-
-
-/*---------------------------------------------------------------.
-| TEXT is expected to be @$ in some code associated to a symbol: |
-| destructor or printer.                                         |
-`---------------------------------------------------------------*/
-
-static inline void
-handle_symbol_code_at (char *text, location_t location)
-{
-  char *cp = text + 1;
-  if (*cp == '$')
-    obstack_sgrow (&string_obstack, "]b4_at_dollar[");
-  else
-    complain_at (location, _("invalid value: %s"), quote (text));
-}
-
-
-/*-------------------------------------------------------------------.
-| Dispatch onto handle_action_at, or handle_destructor_at, depending |
-| upon CODE_KIND.                                                    |
-`-------------------------------------------------------------------*/
-
-static void
-handle_at (braced_code_t braced_code_kind,
-	   char *text, location_t location)
-{
-  switch (braced_code_kind)
-    {
-    case action_braced_code:
-      handle_action_at (text, location);
-      break;
-
-    case destructor_braced_code:
-    case printer_braced_code:
-      handle_symbol_code_at (text, location);
-      break;
-    }
+  return num;
 }
 
 
@@ -847,7 +669,8 @@ handle_at (braced_code_t braced_code_kind,
 static int
 convert_ucn_to_byte (char const *ucn)
 {
-  unsigned long code = strtoul (ucn + 2, 0, 16);
+  verify (UCHAR_MAX <= INT_MAX);
+  unsigned long int code = strtoul (ucn + 2, NULL, 16);
 
   /* FIXME: Currently we assume Unicode-compatible unibyte characters
      on ASCII hosts (i.e., Latin-1 on hosts with 8-bit bytes).  On
@@ -863,7 +686,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 '$', '@',
-       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[] =
@@ -894,14 +717,74 @@ convert_ucn_to_byte (char const *ucn)
 }
 
 
+/*----------------------------------------------------------------.
+| Handle `#line INT "FILE"'.  ARGS has already skipped `#line '.  |
+`----------------------------------------------------------------*/
+
+static void
+handle_syncline (char *args, location loc)
+{
+  char *after_num;
+  unsigned long int lineno = strtoul (args, &after_num, 10);
+  char *file = strchr (after_num, '"') + 1;
+  *strchr (file, '"') = '\0';
+  if (INT_MAX <= lineno)
+    {
+      warn_at (loc, _("line number overflow"));
+      lineno = INT_MAX;
+    }
+  current_file = uniqstr_new (file);
+  boundary_set (&scanner_cursor, current_file, lineno, 1);
+}
+
+
+/*----------------------------------------------------------------.
+| For a token or comment starting at START, report message MSGID, |
+| which should say that an end marker was found before		  |
+| the expected TOKEN_END.					  |
+`----------------------------------------------------------------*/
+
+static void
+unexpected_end (boundary start, char const *msgid, char const *token_end)
+{
+  location loc;
+  loc.start = start;
+  loc.end = scanner_cursor;
+  complain_at (loc, _(msgid), token_end);
+}
+
+
+/*------------------------------------------------------------------------.
+| Report an unexpected EOF in a token or comment starting at START.       |
+| An end of file was encountered and the expected TOKEN_END was missing.  |
+`------------------------------------------------------------------------*/
+
+static void
+unexpected_eof (boundary start, char const *token_end)
+{
+  unexpected_end (start, N_("missing `%s' at end of file"), token_end);
+}
+
+
+/*----------------------------------------.
+| Likewise, but for unexpected newlines.  |
+`----------------------------------------*/
+
+static void
+unexpected_newline (boundary start, char const *token_end)
+{
+  unexpected_end (start, N_("missing `%s' at end of line"), token_end);
+}
+
+
 /*-------------------------.
 | Initialize the scanner.  |
 `-------------------------*/
 
 void
-scanner_initialize (void)
+gram_scanner_initialize (void)
 {
-  obstack_init (&string_obstack);
+  obstack_init (&obstack_for_string);
 }
 
 
@@ -910,9 +793,9 @@ scanner_initialize (void)
 `-----------------------------------------------*/
 
 void
-scanner_free (void)
+gram_scanner_free (void)
 {
-  obstack_free (&string_obstack, 0);
+  obstack_free (&obstack_for_string, 0);
   /* Reclaim Flex's buffers.  */
-  yy_delete_buffer (YY_CURRENT_BUFFER);
+  yylex_destroy ();
 }