X-Git-Url: https://git.saurik.com/bison.git/blobdiff_plain/0433ba88f97e23a896ffba2e4b33bac1d09ff345..3208e3f4d9aff080a546685560536336f0852bcb:/src/scan-gram.l

diff --git a/src/scan-gram.l b/src/scan-gram.l
index 424ae2e9..5c0418c6 100644
--- a/src/scan-gram.l
+++ b/src/scan-gram.l
@@ -1,12 +1,13 @@
 /* Bison Grammar Scanner                             -*- C -*-
 
-   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   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,
@@ -15,110 +16,112 @@
    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 never-interactive
+%option debug nodefault nounput noyywrap never-interactive
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
-#include "system.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 <src/flex-scanner.h>
+
+#include <src/complain.h>
+#include <src/files.h>
+#include <src/gram.h>
+#include <quotearg.h>
+#include <src/reader.h>
+#include <src/uniqstr.h>
 
 #include <mbswidth.h>
-#include <get-errno.h>
 #include <quote.h>
 
-#include "complain.h"
-#include "files.h"
-#include "getargs.h"
-#include "gram.h"
-#include "reader.h"
-#include "uniqstr.h"
+#include <src/scan-gram.h>
+
+#define YY_DECL GRAM_LEX_DECL
 
 #define YY_USER_INIT					\
-  do							\
-    {							\
-      scanner_cursor.file = current_file;		\
-      scanner_cursor.line = 1;				\
-      scanner_cursor.column = 1;			\
-    }							\
-  while (0)
+   code_start = scanner_cursor = loc->start;		\
 
 /* Location of scanner cursor.  */
-boundary scanner_cursor;
+static boundary scanner_cursor;
 
-static void adjust_location (location *, char const *, size_t);
-#define YY_USER_ACTION  adjust_location (loc, yytext, yyleng);
+#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))
 
+#define RETURN_PERCENT_FLAG(Value)              \
+  do {                                          \
+    val->uniqstr = uniqstr_new (Value);         \
+    return PERCENT_FLAG;                        \
+  } while (0)
 
-/* 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.
-
-   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;
+#define ROLLBACK_CURRENT_TOKEN                                  \
+  do {                                                          \
+    scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);	\
+    yyless (0);                                                 \
+  } while (0)
 
 /* A string representing the most recently saved token.  */
 static char *last_string;
 
-
-#define STRING_GROW   \
-  obstack_grow (&obstack_for_string, yytext, yyleng)
-
-#define STRING_FINISH					\
-  do {							\
-    obstack_1grow (&obstack_for_string, '\0');		\
-    last_string = obstack_finish (&obstack_for_string);	\
-  } while (0)
-
-#define STRING_FREE \
-  obstack_free (&obstack_for_string, last_string)
+/* Bracketed identifier. */
+static uniqstr bracketed_id_str = 0;
+static location bracketed_id_loc;
+static boundary bracketed_id_start;
+static int bracketed_id_context_state = 0;
 
 void
-scanner_last_string_free (void)
+gram_scanner_last_string_free (void)
 {
   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.
-
-   Outside of well-formed rules, RULE_LENGTH has an undefined value.  */
-static int rule_length;
-
-static void handle_dollar (int token_type, char *cp, location loc);
-static void handle_at (int token_type, char *cp, location loc);
-static void handle_syncline (char *args);
+static 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_end_of_file (boundary, char const *);
+static void unexpected_eof (boundary, char const *);
+static void unexpected_newline (boundary, char const *);
 
 %}
-%x SC_COMMENT SC_LINE_COMMENT SC_YACC_COMMENT
-%x SC_STRING SC_CHARACTER
-%x SC_AFTER_IDENTIFIER
+ /* 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_PRE_CODE 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
+ /* A complex tag, with nested angles brackets. */
+%x SC_TAG
+
+ /* 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
+ /* Bracketed identifiers support. */
+%x SC_BRACKETED_ID SC_RETURN_BRACKETED_ID
 
-letter	  [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
+letter	  [-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
 id	  {letter}({letter}|[0-9])*
-directive %{letter}({letter}|[0-9]|-)*
+directive %{id}
 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>]+
+   NUL, as this simplifies our implementation.  We disallow angle
+   bracket to match them in nested pairs: several languages use them
+   for generics/template types.  */
+tag	 [^\0<>]+
 
 /* Zero or more instances of backslash-newline.  Following GCC, allow
    white space between the backslash and the newline.  */
@@ -126,25 +129,24 @@ splice	 (\\[ \f\t\v]*\n)*
 
 %%
 %{
-  /* Nesting level of the current code in braces.  */
-  int braces_level IF_LINT (= 0);
+  /* Nesting level.  Either for nested braces, or nested angle brackets
+     (but not mixed).  */
+  int nesting IF_LINT (= 0);
 
   /* Parent context state, when applicable.  */
   int context_state IF_LINT (= 0);
 
-  /* Token type to return, when applicable.  */
-  int token_type IF_LINT (= 0);
-
   /* Location of most recent identifier, when applicable.  */
-  location id_loc IF_LINT (= *loc);
+  location id_loc IF_LINT (= empty_location);
 
-  /* Where containing code started, when applicable.
-     Once the second %% seen, we are looking for the epilogue.  */
-  boundary code_start = loc->end;
+  /* 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 (= loc->start);
+  boundary token_start IF_LINT (= scanner_cursor);
 %}
 
 
@@ -152,12 +154,11 @@ splice	 (\\[ \f\t\v]*\n)*
   | Scanning white space.  |
   `-----------------------*/
 
-<INITIAL,SC_AFTER_IDENTIFIER,SC_PRE_CODE>
+<INITIAL,SC_AFTER_IDENTIFIER,SC_BRACKETED_ID,SC_RETURN_BRACKETED_ID>
 {
-  [ \f\n\t\v]  ;
+  /* Comments and white space.  */
   ","	       warn_at (*loc, _("stray `,' treated as white space"));
-
-  /* Comments. */
+  [ \f\n\t\v]  |
   "//".*       ;
   "/*" {
     token_start = loc->start;
@@ -168,7 +169,7 @@ splice	 (\\[ \f\t\v]*\n)*
   /* #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);
+    handle_syncline (yytext + sizeof "#line " - 1, *loc);
   }
 }
 
@@ -176,76 +177,94 @@ splice	 (\\[ \f\t\v]*\n)*
   /*----------------------------.
   | Scanning Bison directives.  |
   `----------------------------*/
+
+  /* For directives that are also command line options, the regex must be
+	"%..."
+     after "[-_]"s are removed, and the directive must match the --long
+     option name, with a single string argument.  Otherwise, add exceptions
+     to ../build-aux/cross-options.pl.  */
+
 <INITIAL>
 {
-  "%binary"               return PERCENT_NONASSOC;
-  "%debug"                return PERCENT_DEBUG;
-  "%define"               return PERCENT_DEFINE;
-  "%defines"              return PERCENT_DEFINES;
-  "%destructor"		  token_type = PERCENT_DESTRUCTOR; BEGIN SC_PRE_CODE;
-  "%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_FLAG("parse.trace");
+  "%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;
-  "%lex-param"		  token_type = PERCENT_LEX_PARAM; BEGIN SC_PRE_CODE;
-  "%locations"            return PERCENT_LOCATIONS;
-  "%merge"		  return PERCENT_MERGE;
-  "%name"[-_]"prefix"     return PERCENT_NAME_PREFIX;
-  "%no"[-_]"lines"        return PERCENT_NO_LINES;
-  "%nonassoc"             return PERCENT_NONASSOC;
-  "%nterm"                return PERCENT_NTERM;
-  "%output"               return PERCENT_OUTPUT;
-  "%parse-param"	  token_type = PERCENT_PARSE_PARAM; BEGIN SC_PRE_CODE;
-  "%prec"                 rule_length--; return PERCENT_PREC;
-  "%printer"              token_type = PERCENT_PRINTER; BEGIN SC_PRE_CODE;
-  "%pure"[-_]"parser"     return PERCENT_PURE_PARSER;
-  "%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"		  token_type = PERCENT_UNION; BEGIN SC_PRE_CODE;
-  "%verbose"              return PERCENT_VERBOSE;
-  "%yacc"                 return PERCENT_YACC;
+  "%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_FLAG("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;
+  "%precedence"                     return PERCENT_PRECEDENCE;
+  "%printer"                        return PERCENT_PRINTER;
+  "%pure"[-_]"parser"               RETURN_PERCENT_FLAG("api.pure");
+  "%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 PIPE;
+  "|"                     return PIPE;
   ";"                     return SEMICOLON;
 
   {id} {
-    val->symbol = symbol_get (yytext, *loc);
+    val->uniqstr = uniqstr_new (yytext);
     id_loc = *loc;
-    rule_length++;
+    bracketed_id_str = NULL;
     BEGIN SC_AFTER_IDENTIFIER;
   }
 
   {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;
+    val->integer = scan_integer (yytext, 10, *loc);
     return INT;
   }
+  0[xX][0-9abcdefABCDEF]+ {
+    val->integer = scan_integer (yytext, 16, *loc);
+    return INT;
+  }
+
+  /* Identifiers may not start with a digit.  Yet, don't silently
+     accept "1FOO" as "1 FOO".  */
+  {int}{id} {
+    complain_at (*loc, _("invalid identifier: %s"), quote (yytext));
+  }
 
-  /* Characters.  We don't check there is only one.  */
+  /* Characters.  */
   "'"	      STRING_GROW; token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
 
   /* Strings. */
-  "\""	      STRING_GROW; token_start = loc->start; BEGIN SC_ESCAPED_STRING;
+  "\""	      token_start = loc->start; BEGIN SC_ESCAPED_STRING;
 
   /* Prologue. */
   "%{"        code_start = loc->start; BEGIN SC_PROLOGUE;
@@ -253,34 +272,60 @@ splice	 (\\[ \f\t\v]*\n)*
   /* Code in between braces.  */
   "{" {
     STRING_GROW;
-    token_type = BRACED_CODE;
-    braces_level = 0;
+    nesting = 0;
     code_start = loc->start;
     BEGIN SC_BRACED_CODE;
   }
 
   /* A type. */
+  "<*>"       return TAG_ANY;
+  "<>"        return TAG_NONE;
   "<"{tag}">" {
     obstack_grow (&obstack_for_string, yytext + 1, yyleng - 2);
     STRING_FINISH;
     val->uniqstr = uniqstr_new (last_string);
     STRING_FREE;
-    return TYPE;
+    return TAG;
+  }
+  "<"         {
+    nesting = 0;
+    token_start = loc->start;
+    BEGIN SC_TAG;
   }
 
   "%%" {
     static int percent_percent_count;
     if (++percent_percent_count == 2)
-      {
-	code_start = loc->start;
-	BEGIN SC_EPILOGUE;
-      }
+      BEGIN SC_EPILOGUE;
     return PERCENT_PERCENT;
   }
 
+  "[" {
+    bracketed_id_str = NULL;
+    bracketed_id_start = loc->start;
+    bracketed_id_context_state = YY_START;
+    BEGIN SC_BRACKETED_ID;
+  }
+
   . {
     complain_at (*loc, _("invalid character: %s"), quote (yytext));
   }
+
+  <<EOF>> {
+    loc->start = loc->end = scanner_cursor;
+    yyterminate ();
+  }
+}
+
+
+  /*--------------------------------------------------------------.
+  | Supporting \0 complexifies our implementation for no expected |
+  | added value.                                                  |
+  `--------------------------------------------------------------*/
+
+<SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING,SC_TAG>
+{
+  \0	    complain_at (*loc, _("invalid null character"));
 }
 
 
@@ -290,26 +335,94 @@ splice	 (\\[ \f\t\v]*\n)*
 
 <SC_AFTER_IDENTIFIER>
 {
+  "[" {
+    if (bracketed_id_str)
+      {
+	ROLLBACK_CURRENT_TOKEN;
+	BEGIN SC_RETURN_BRACKETED_ID;
+	*loc = id_loc;
+	return ID;
+      }
+    else
+      {
+	bracketed_id_start = loc->start;
+	bracketed_id_context_state = YY_START;
+	BEGIN SC_BRACKETED_ID;
+      }
+  }
   ":" {
-    rule_length = 0;
+    BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
     *loc = id_loc;
-    BEGIN INITIAL;
     return ID_COLON;
   }
   . {
-    scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);
-    yyless (0);
+    ROLLBACK_CURRENT_TOKEN;
+    BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
     *loc = id_loc;
-    BEGIN INITIAL;
     return ID;
   }
   <<EOF>> {
+    BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
     *loc = id_loc;
-    BEGIN INITIAL;
     return ID;
   }
 }
 
+  /*--------------------------------.
+  | Scanning bracketed identifiers. |
+  `--------------------------------*/
+
+<SC_BRACKETED_ID>
+{
+  {id} {
+    if (bracketed_id_str)
+      {
+	complain_at (*loc, _("unexpected identifier in bracketed name: %s"),
+		     quote (yytext));
+      }
+    else
+      {
+	bracketed_id_str = uniqstr_new (yytext);
+	bracketed_id_loc = *loc;
+      }
+  }
+  "]" {
+    BEGIN bracketed_id_context_state;
+    if (bracketed_id_str)
+      {
+	if (INITIAL == bracketed_id_context_state)
+	  {
+	    val->uniqstr = bracketed_id_str;
+	    bracketed_id_str = 0;
+	    *loc = bracketed_id_loc;
+	    return BRACKETED_ID;
+	  }
+      }
+    else
+      complain_at (*loc, _("an identifier expected"));
+  }
+  . {
+    complain_at (*loc, _("invalid character in bracketed name: %s"),
+		 quote (yytext));
+  }
+  <<EOF>> {
+    BEGIN bracketed_id_context_state;
+    unexpected_eof (bracketed_id_start, "]");
+  }
+}
+
+<SC_RETURN_BRACKETED_ID>
+{
+  . {
+    ROLLBACK_CURRENT_TOKEN;
+    val->uniqstr = bracketed_id_str;
+    bracketed_id_str = 0;
+    *loc = bracketed_id_loc;
+    BEGIN INITIAL;
+    return BRACKETED_ID;
+  }
+}
+
 
   /*---------------------------------------------------------------.
   | Scanning a Yacc comment.  The initial `/ *' is already eaten.  |
@@ -319,7 +432,7 @@ splice	 (\\[ \f\t\v]*\n)*
 {
   "*/"     BEGIN context_state;
   .|\n	   ;
-  <<EOF>>  unexpected_end_of_file (token_start, "*/");
+  <<EOF>>  unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
@@ -330,7 +443,7 @@ splice	 (\\[ \f\t\v]*\n)*
 <SC_COMMENT>
 {
   "*"{splice}"/"  STRING_GROW; BEGIN context_state;
-  <<EOF>>	  unexpected_end_of_file (token_start, "*/");
+  <<EOF>>	  unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
@@ -346,53 +459,114 @@ splice	 (\\[ \f\t\v]*\n)*
 }
 
 
-  /*----------------------------------------------------------------.
-  | 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>
 {
-  "\"" {
-    STRING_GROW;
+  "\""|"\n" {
+    if (yytext[0] == '\n')
+      unexpected_newline (token_start, "\"");
+    STRING_FINISH;
+    loc->start = token_start;
+    val->chars = last_string;
+    BEGIN INITIAL;
+    return STRING;
+  }
+  <<EOF>> {
+    unexpected_eof (token_start, "\"");
     STRING_FINISH;
     loc->start = token_start;
     val->chars = last_string;
-    rule_length++;
     BEGIN INITIAL;
     return STRING;
   }
-
-  .|\n	    STRING_GROW;
-  <<EOF>>   unexpected_end_of_file (token_start, "\"");
 }
 
-  /*---------------------------------------------------------------.
-  | 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>
 {
-  "'" {
-    unsigned char last_string_1;
+  "'"|"\n" {
     STRING_GROW;
     STRING_FINISH;
     loc->start = token_start;
-    val->symbol = symbol_get (last_string, *loc);
-    symbol_class_set (val->symbol, token_sym, *loc);
-    last_string_1 = last_string[1];
-    symbol_user_token_number_set (val->symbol, last_string_1, *loc);
+    val->character = last_string[1];
+    {
+      /* FIXME: Eventually, make these errors.  */
+      size_t length = strlen (last_string);
+      if (strlen (last_string) < 3)
+        warn_at (*loc, _("empty character literal"));
+      else if (strlen (last_string) > 3)
+        warn_at (*loc, _("extra characters in character literal"));
+    }
+    if (yytext[0] == '\n')
+      unexpected_newline (token_start, "'");
     STRING_FREE;
-    rule_length++;
     BEGIN INITIAL;
-    return ID;
+    return CHAR;
+  }
+  <<EOF>> {
+    STRING_FINISH;
+    loc->start = token_start;
+    {
+      size_t length = strlen (last_string);
+      /* FIXME: Eventually, make these errors.  */
+      if (length < 2)
+        warn_at (*loc, _("empty character literal"));
+      else if (length > 2)
+        warn_at (*loc, _("extra characters in character literal"));
+      if (length > 1)
+        val->character = last_string[1];
+      else
+        val->character = last_string[0];
+    }
+    unexpected_eof (token_start, "'");
+    STRING_FREE;
+    BEGIN INITIAL;
+    return CHAR;
   }
-
-  .|\n	    STRING_GROW;
-  <<EOF>>   unexpected_end_of_file (token_start, "'");
 }
 
+  /*-----------------------------------------------------------.
+  | Scanning a Bison nested tag.  The initial angle bracket is |
+  | already eaten.                                             |
+  `-----------------------------------------------------------*/
+
+<SC_TAG>
+{
+  ">" {
+    --nesting;
+    if (nesting < 0)
+      {
+        STRING_FINISH;
+        loc->start = token_start;
+        val->uniqstr = uniqstr_new (last_string);
+        STRING_FREE;
+        BEGIN INITIAL;
+        return TAG;
+      }
+    STRING_GROW;
+  }
+
+  [^<>]+ STRING_GROW;
+  "<"+   STRING_GROW; nesting += yyleng;
+
+  <<EOF>> {
+    unexpected_eof (token_start, ">");
+    STRING_FINISH;
+    loc->start = token_start;
+    val->uniqstr = uniqstr_new (last_string);
+    STRING_FREE;
+    BEGIN INITIAL;
+    return TAG;
+  }
+}
 
   /*----------------------------.
   | Decode escaped characters.  |
@@ -401,19 +575,22 @@ splice	 (\\[ \f\t\v]*\n)*
 <SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>
 {
   \\[0-7]{1,3} {
-    unsigned long c = strtoul (yytext + 1, 0, 8);
+    unsigned long int c = strtoul (yytext + 1, NULL, 8);
     if (UCHAR_MAX < c)
       complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
+    else if (! c)
+      complain_at (*loc, _("invalid null character: %s"), quote (yytext));
     else
       obstack_1grow (&obstack_for_string, c);
   }
 
   \\x[0-9abcdefABCDEF]+ {
-    unsigned long c;
-    set_errno (0);
-    c = strtoul (yytext + 2, 0, 16);
-    if (UCHAR_MAX < c || get_errno ())
+    verify (UCHAR_MAX < ULONG_MAX);
+    unsigned long int c = strtoul (yytext + 2, NULL, 16);
+    if (UCHAR_MAX < c)
       complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
+    else if (! c)
+      complain_at (*loc, _("invalid null character: %s"), quote (yytext));
     else
       obstack_1grow (&obstack_for_string, c);
   }
@@ -433,6 +610,8 @@ splice	 (\\[ \f\t\v]*\n)*
     int c = convert_ucn_to_byte (yytext);
     if (c < 0)
       complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
+    else if (! c)
+      complain_at (*loc, _("invalid null character: %s"), quote (yytext));
     else
       obstack_1grow (&obstack_for_string, c);
   }
@@ -442,30 +621,27 @@ splice	 (\\[ \f\t\v]*\n)*
   }
 }
 
+  /*--------------------------------------------.
+  | 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>
 {
-  "'"			STRING_GROW; BEGIN context_state;
-  \\{splice}[^$@\[\]]	STRING_GROW;
-  <<EOF>>		unexpected_end_of_file (token_start, "'");
+  "'"		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>
 {
-  "\""			STRING_GROW; BEGIN context_state;
-  \\{splice}[^$@\[\]]	STRING_GROW;
-  <<EOF>>		unexpected_end_of_file (token_start, "\"");
+  "\""		STRING_GROW; BEGIN context_state;
+  \n		unexpected_newline (token_start, "\""); BEGIN context_state;
+  <<EOF>>	unexpected_eof (token_start, "\""); BEGIN context_state;
 }
 
 
@@ -501,78 +677,27 @@ splice	 (\\[ \f\t\v]*\n)*
 }
 
 
-  /*---------------------------------------------------------------.
-  | Scanning after %union etc., possibly followed by white space.  |
-  | For %union only, allow arbitrary C code to appear before the   |
-  | following brace, as an extension to POSIX.			   |
-  `---------------------------------------------------------------*/
 
-<SC_PRE_CODE>
-{
-  . {
-    bool valid = yytext[0] == '{' || token_type == PERCENT_UNION;
-    scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);
-    yyless (0);
-
-    if (valid)
-      {
-	braces_level = -1;
-	code_start = loc->start;
-	BEGIN SC_BRACED_CODE;
-      }
-    else
-      {
-	complain_at (*loc, _("missing `{' in `%s'"),
-		     token_name (token_type));
-	obstack_sgrow (&obstack_for_string, "{}");
-	STRING_FINISH;
-	val->chars = last_string;
-	BEGIN INITIAL;
-	return token_type;
-      }
-  }
-}
-
-
-  /*---------------------------------------------------------------.
-  | 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--;
+  "{"|"<"{splice}"%"  STRING_GROW; nesting++;
+  "%"{splice}">"      STRING_GROW; nesting--;
   "}" {
-    bool outer_brace = --braces_level < 0;
-
-    /* As an undocumented Bison extension, append `;' before the last
-       brace in braced code, so that the user code can omit trailing
-       `;'.  But do not append `;' if emulating Yacc, since Yacc does
-       not append one.
-
-       FIXME: Bison should warn if a semicolon seems to be necessary
-       here, and should omit the semicolon if it seems unnecessary
-       (e.g., after ';', '{', or '}', each followed by comments or
-       white space).  Such a warning shouldn't depend on --yacc; it
-       should depend on a new --pedantic option, which would cause
-       Bison to warn if it detects an extension to POSIX.  --pedantic
-       should also diagnose other Bison extensions like %yacc.
-       Perhaps there should also be a GCC-style --pedantic-errors
-       option, so that such warnings are diagnosed as errors.  */
-    if (outer_brace && token_type == BRACED_CODE && ! yacc_flag)
-      obstack_1grow (&obstack_for_string, ';');
-
     obstack_1grow (&obstack_for_string, '}');
 
-    if (outer_brace)
+    --nesting;
+    if (nesting < 0)
       {
 	STRING_FINISH;
-	rule_length++;
 	loc->start = code_start;
-	val->chars = last_string;
+	val->code = last_string;
 	BEGIN INITIAL;
-	return token_type;
+	return BRACED_CODE;
       }
   }
 
@@ -580,10 +705,14 @@ splice	 (\\[ \f\t\v]*\n)*
      (as `<' `<%').  */
   "<"{splice}"<"  STRING_GROW;
 
-  "$"("<"{tag}">")?(-?[0-9]+|"$")  handle_dollar (token_type, yytext, *loc);
-  "@"(-?[0-9]+|"$")		   handle_at (token_type, yytext, *loc);
-
-  <<EOF>>  unexpected_end_of_file (code_start, "}");
+  <<EOF>> {
+    unexpected_eof (code_start, "}");
+    STRING_FINISH;
+    loc->start = code_start;
+    val->code = last_string;
+    BEGIN INITIAL;
+    return BRACED_CODE;
+  }
 }
 
 
@@ -601,7 +730,14 @@ splice	 (\\[ \f\t\v]*\n)*
     return PROLOGUE;
   }
 
-  <<EOF>>  unexpected_end_of_file (code_start, "%}");
+  <<EOF>> {
+    unexpected_eof (code_start, "%}");
+    STRING_FINISH;
+    loc->start = code_start;
+    val->chars = last_string;
+    BEGIN INITIAL;
+    return PROLOGUE;
+  }
 }
 
 
@@ -622,60 +758,15 @@ splice	 (\\[ \f\t\v]*\n)*
 }
 
 
-  /*----------------------------------------------------------------.
-  | 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;
-}
+  /*-----------------------------------------------------.
+  | 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;
 
 %%
 
-/* 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.  */
@@ -723,169 +814,24 @@ no_cr_read (FILE *fp, char *buf, size_t size)
 }
 
 
-/*------------------------------------------------------------------.
-| TEXT is pointing to a wannabee semantic value (i.e., a `$').      |
-|                                                                   |
-| Possible inputs: $[<TYPENAME>]($|integer)                         |
-|                                                                   |
-| Output to OBSTACK_FOR_STRING a reference to this semantic value.  |
-`------------------------------------------------------------------*/
-
-static inline bool
-handle_action_dollar (char *text, location loc)
-{
-  const char *type_name = NULL;
-  char *cp = text + 1;
-
-  if (! current_rule)
-    return false;
-
-  /* Get the type name if explicit. */
-  if (*cp == '<')
-    {
-      type_name = ++cp;
-      while (*cp != '>')
-	++cp;
-      *cp = '\0';
-      ++cp;
-    }
-
-  if (*cp == '$')
-    {
-      if (!type_name)
-	type_name = symbol_list_n_type_name_get (current_rule, loc, 0);
-      if (!type_name && typed)
-	complain_at (loc, _("$$ of `%s' has no declared type"),
-		     current_rule->sym->tag);
-      if (!type_name)
-	type_name = "";
-      obstack_fgrow1 (&obstack_for_string,
-		      "]b4_lhs_value([%s])[", type_name);
-    }
-  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)
-	    type_name = symbol_list_n_type_name_get (current_rule, loc, n);
-	  if (!type_name && typed)
-	    complain_at (loc, _("$%d of `%s' has no declared type"),
-			 n, current_rule->sym->tag);
-	  if (!type_name)
-	    type_name = "";
-	  obstack_fgrow3 (&obstack_for_string,
-			  "]b4_rhs_value([%d], [%d], [%s])[",
-			  rule_length, n, type_name);
-	}
-      else
-	complain_at (loc, _("integer out of range: %s"), quote (text));
-    }
-
-  return true;
-}
-
-
-/*-----------------------------------------------------------------.
-| Dispatch onto handle_action_dollar, or handle_destructor_dollar, |
-| depending upon TOKEN_TYPE.                                       |
-`-----------------------------------------------------------------*/
-
-static void
-handle_dollar (int token_type, char *text, location loc)
-{
-  switch (token_type)
-    {
-    case BRACED_CODE:
-      if (handle_action_dollar (text, loc))
-	return;
-      break;
-
-    case PERCENT_DESTRUCTOR:
-    case PERCENT_PRINTER:
-      if (text[1] == '$')
-	{
-	  obstack_sgrow (&obstack_for_string, "]b4_dollar_dollar[");
-	  return;
-	}
-      break;
-
-    default:
-      break;
-    }
-
-  complain_at (loc, _("invalid value: %s"), quote (text));
-}
-
 
 /*------------------------------------------------------.
-| TEXT is a location token (i.e., a `@...').  Output to |
-| OBSTACK_FOR_STRING a reference to this location.      |
+| Scan NUMBER for a base-BASE integer at location LOC.  |
 `------------------------------------------------------*/
 
-static inline bool
-handle_action_at (char *text, location loc)
+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 (! current_rule)
-    return false;
-
-  if (*cp == '$')
-    obstack_sgrow (&obstack_for_string, "]b4_lhs_location[");
-  else
+  if (INT_MAX < num)
     {
-      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));
+      complain_at (loc, _("integer out of range: %s"), quote (number));
+      num = INT_MAX;
     }
 
-  return true;
-}
-
-
-/*-------------------------------------------------------------------.
-| Dispatch onto handle_action_at, or handle_destructor_at, depending |
-| upon CODE_KIND.                                                    |
-`-------------------------------------------------------------------*/
-
-static void
-handle_at (int token_type, char *text, location loc)
-{
-  switch (token_type)
-    {
-    case BRACED_CODE:
-      handle_action_at (text, loc);
-      return;
-
-    case PERCENT_DESTRUCTOR:
-    case PERCENT_PRINTER:
-      if (text[1] == '$')
-	{
-	  obstack_sgrow (&obstack_for_string, "]b4_at_dollar[");
-	  return;
-	}
-      break;
-
-    default:
-      break;
-    }
-
-  complain_at (loc, _("invalid value: %s"), quote (text));
+  return num;
 }
 
 
@@ -898,7 +844,8 @@ handle_at (int token_type, char *text, location loc)
 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
@@ -950,40 +897,58 @@ convert_ucn_to_byte (char const *ucn)
 `----------------------------------------------------------------*/
 
 static void
-handle_syncline (char *args)
+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)
 {
-  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;
+  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.  |
-| After reporting the problem, pretend that TOKEN_END was found.          |
 `------------------------------------------------------------------------*/
 
 static void
-unexpected_end_of_file (boundary start, char const *token_end)
+unexpected_eof (boundary start, char const *token_end)
 {
-  size_t i = strlen (token_end);
+  unexpected_end (start, N_("missing `%s' at end of file"), 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;
+/*----------------------------------------.
+| Likewise, but for unexpected newlines.  |
+`----------------------------------------*/
 
-  while (i != 0)
-    unput (token_end[--i]);
+static void
+unexpected_newline (boundary start, char const *token_end)
+{
+  unexpected_end (start, N_("missing `%s' at end of line"), token_end);
 }
 
 
@@ -992,7 +957,7 @@ unexpected_end_of_file (boundary start, char const *token_end)
 `-------------------------*/
 
 void
-scanner_initialize (void)
+gram_scanner_initialize (void)
 {
   obstack_init (&obstack_for_string);
 }
@@ -1003,9 +968,9 @@ scanner_initialize (void)
 `-----------------------------------------------*/
 
 void
-scanner_free (void)
+gram_scanner_free (void)
 {
   obstack_free (&obstack_for_string, 0);
   /* Reclaim Flex's buffers.  */
-  yy_delete_buffer (YY_CURRENT_BUFFER);
+  yylex_destroy ();
 }