]> git.saurik.com Git - bison.git/blobdiff - src/scan-gram.l
* Makefile.maint (gzip_rsyncable, GZIP_ENV): Compute within the
[bison.git] / src / scan-gram.l
index 2e7823175eb393e38f969a6c9b64c46e89812308..cf704c7724e65c624392025547c9550116d7b805 100644 (file)
@@ -1,6 +1,6 @@
 /* Bison Grammar Scanner                             -*- C -*-
 
 /* Bison Grammar Scanner                             -*- C -*-
 
-   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
    This file is part of Bison, the GNU Compiler Compiler.
 
 
    This file is part of Bison, the GNU Compiler Compiler.
 
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
 
    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
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301  USA
 */
 
 %option debug nodefault nounput noyywrap never-interactive
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
 */
 
 %option debug nodefault nounput noyywrap never-interactive
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
+/* 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
+
 #include "system.h"
 
 #include <mbswidth.h>
 #include "system.h"
 
 #include <mbswidth.h>
-#include <get-errno.h>
 #include <quote.h>
 
 #include "complain.h"
 #include "files.h"
 #include "getargs.h"
 #include "gram.h"
 #include <quote.h>
 
 #include "complain.h"
 #include "files.h"
 #include "getargs.h"
 #include "gram.h"
+#include "quotearg.h"
 #include "reader.h"
 #include "uniqstr.h"
 
 #include "reader.h"
 #include "uniqstr.h"
 
     }                                                  \
   while (0)
 
     }                                                  \
   while (0)
 
+/* Pacify "gcc -Wmissing-prototypes" when flex 2.5.31 is used.  */
+int gram_get_lineno (void);
+FILE *gram_get_in (void);
+FILE *gram_get_out (void);
+int gram_get_leng (void);
+char *gram_get_text (void);
+void gram_set_lineno (int);
+void gram_set_in (FILE *);
+void gram_set_out (FILE *);
+int gram_get_debug (void);
+void gram_set_debug (int);
+int gram_lex_destroy (void);
+
 /* Location of scanner cursor.  */
 boundary scanner_cursor;
 
 /* Location of scanner cursor.  */
 boundary scanner_cursor;
 
@@ -69,8 +87,11 @@ static size_t no_cr_read (FILE *, char *, size_t);
 static struct obstack obstack_for_string;
 
 /* A string representing the most recently saved token.  */
 static struct obstack obstack_for_string;
 
 /* A string representing the most recently saved token.  */
-static char *last_string;
+char *last_string;
 
 
+/* The location of the most recently saved token, if it was a
+   BRACED_CODE token; otherwise, this has an unspecified value.  */
+location last_braced_code_loc;
 
 #define STRING_GROW   \
   obstack_grow (&obstack_for_string, yytext, yyleng)
 
 #define STRING_GROW   \
   obstack_grow (&obstack_for_string, yytext, yyleng)
@@ -98,11 +119,27 @@ scanner_last_string_free (void)
    Outside of well-formed rules, RULE_LENGTH has an undefined value.  */
 static int rule_length;
 
    Outside of well-formed rules, RULE_LENGTH has an undefined value.  */
 static int rule_length;
 
+static void rule_length_overflow (location) __attribute__ ((__noreturn__));
+
+/* Increment the rule length by one, checking for overflow.  */
+static inline void
+increment_rule_length (location loc)
+{
+  rule_length++;
+
+  /* Don't allow rule_length == INT_MAX, since that might cause
+     confusion with strtol if INT_MAX == LONG_MAX.  */
+  if (rule_length == INT_MAX)
+    rule_length_overflow (loc);
+}
+
 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_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_eof (boundary, char const *);
 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_COMMENT SC_LINE_COMMENT SC_YACC_COMMENT
@@ -156,10 +193,9 @@ splice      (\\[ \f\t\v]*\n)*
 
 <INITIAL,SC_AFTER_IDENTIFIER,SC_PRE_CODE>
 {
 
 <INITIAL,SC_AFTER_IDENTIFIER,SC_PRE_CODE>
 {
-  [ \f\n\t\v]  ;
+  /* Comments and white space.  */
   ","         warn_at (*loc, _("stray `,' treated as white space"));
   ","         warn_at (*loc, _("stray `,' treated as white space"));
-
-  /* Comments. */
+  [ \f\n\t\v]  |
   "//".*       ;
   "/*" {
     token_start = loc->start;
   "//".*       ;
   "/*" {
     token_start = loc->start;
@@ -170,7 +206,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" {
   /* #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);
   }
 }
 
   }
 }
 
@@ -182,20 +218,24 @@ splice     (\\[ \f\t\v]*\n)*
 {
   "%binary"               return PERCENT_NONASSOC;
   "%debug"                return PERCENT_DEBUG;
 {
   "%binary"               return PERCENT_NONASSOC;
   "%debug"                return PERCENT_DEBUG;
+  "%default"[-_]"prec"    return PERCENT_DEFAULT_PREC;
   "%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;
   "%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;
+  "%expect"[-_]"rr"      return PERCENT_EXPECT_RR;
   "%file-prefix"          return PERCENT_FILE_PREFIX;
   "%fixed"[-_]"output"[-_]"files"   return PERCENT_YACC;
   "%file-prefix"          return PERCENT_FILE_PREFIX;
   "%fixed"[-_]"output"[-_]"files"   return PERCENT_YACC;
+  "%initial-action"       token_type = PERCENT_INITIAL_ACTION; BEGIN SC_PRE_CODE;
   "%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;
   "%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"[-_]"default"[-_]"prec" return PERCENT_NO_DEFAULT_PREC;
   "%no"[-_]"lines"        return PERCENT_NO_LINES;
   "%nonassoc"             return PERCENT_NONASSOC;
   "%nondeterministic-parser"   return PERCENT_NONDETERMINISTIC_PARSER;
   "%no"[-_]"lines"        return PERCENT_NO_LINES;
   "%nonassoc"             return PERCENT_NONASSOC;
   "%nondeterministic-parser"   return PERCENT_NONDETERMINISTIC_PARSER;
@@ -205,6 +245,7 @@ splice       (\\[ \f\t\v]*\n)*
   "%prec"                 rule_length--; return PERCENT_PREC;
   "%printer"              token_type = PERCENT_PRINTER; BEGIN SC_PRE_CODE;
   "%pure"[-_]"parser"     return PERCENT_PURE_PARSER;
   "%prec"                 rule_length--; return PERCENT_PREC;
   "%printer"              token_type = PERCENT_PRINTER; BEGIN SC_PRE_CODE;
   "%pure"[-_]"parser"     return PERCENT_PURE_PARSER;
+  "%require"              return PERCENT_REQUIRE;
   "%right"                return PERCENT_RIGHT;
   "%skeleton"             return PERCENT_SKELETON;
   "%start"                return PERCENT_START;
   "%right"                return PERCENT_RIGHT;
   "%skeleton"             return PERCENT_SKELETON;
   "%start"                return PERCENT_START;
@@ -227,20 +268,16 @@ splice     (\\[ \f\t\v]*\n)*
   {id} {
     val->symbol = symbol_get (yytext, *loc);
     id_loc = *loc;
   {id} {
     val->symbol = symbol_get (yytext, *loc);
     id_loc = *loc;
-    rule_length++;
+    increment_rule_length (*loc);
     BEGIN SC_AFTER_IDENTIFIER;
   }
 
   {int} {
     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;
   }
 
     return INT;
   }
 
@@ -248,13 +285,15 @@ splice     (\\[ \f\t\v]*\n)*
   "'"        STRING_GROW; token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
 
   /* Strings. */
   "'"        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;
 
   /* Code in between braces.  */
   "{" {
 
   /* Prologue. */
   "%{"        code_start = loc->start; BEGIN SC_PROLOGUE;
 
   /* Code in between braces.  */
   "{" {
+    if (current_rule && current_rule->action)
+      grammar_midrule_action ();
     STRING_GROW;
     token_type = BRACED_CODE;
     braces_level = 0;
     STRING_GROW;
     token_type = BRACED_CODE;
     braces_level = 0;
@@ -351,31 +390,29 @@ 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>
 {
   "\"" {
 
 <SC_ESCAPED_STRING>
 {
   "\"" {
-    STRING_GROW;
     STRING_FINISH;
     loc->start = token_start;
     val->chars = last_string;
     STRING_FINISH;
     loc->start = token_start;
     val->chars = last_string;
-    rule_length++;
+    increment_rule_length (*loc);
     BEGIN INITIAL;
     return STRING;
   }
     BEGIN INITIAL;
     return STRING;
   }
-
-  .|\n     STRING_GROW;
-  <<EOF>>   unexpected_eof (token_start, "\""); BEGIN INITIAL;
+  \n           unexpected_newline (token_start, "\""); BEGIN INITIAL;
+  <<EOF>>      unexpected_eof (token_start, "\"");     BEGIN INITIAL;
 }
 
 }
 
-  /*---------------------------------------------------------------.
-  | 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>
 {
 
 <SC_ESCAPED_CHARACTER>
 {
@@ -384,18 +421,24 @@ splice     (\\[ \f\t\v]*\n)*
     STRING_GROW;
     STRING_FINISH;
     loc->start = token_start;
     STRING_GROW;
     STRING_FINISH;
     loc->start = token_start;
-    val->symbol = symbol_get (last_string, *loc);
-    symbol_class_set (val->symbol, token_sym, *loc);
+    val->symbol = symbol_get (quotearg_style (escape_quoting_style,
+                                             last_string),
+                             *loc);
+    symbol_class_set (val->symbol, token_sym, *loc, false);
     last_string_1 = last_string[1];
     symbol_user_token_number_set (val->symbol, last_string_1, *loc);
     STRING_FREE;
     last_string_1 = last_string[1];
     symbol_user_token_number_set (val->symbol, last_string_1, *loc);
     STRING_FREE;
-    rule_length++;
+    increment_rule_length (*loc);
     BEGIN INITIAL;
     return ID;
   }
     BEGIN INITIAL;
     return ID;
   }
+  \n           unexpected_newline (token_start, "'");  BEGIN INITIAL;
+  <<EOF>>      unexpected_eof (token_start, "'");      BEGIN INITIAL;
+}
 
 
-  .|\n     STRING_GROW;
-  <<EOF>>   unexpected_eof (token_start, "'"); BEGIN INITIAL;
+<SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING>
+{
+  \0       complain_at (*loc, _("invalid null character"));
 }
 
 
 }
 
 
@@ -406,19 +449,22 @@ splice     (\\[ \f\t\v]*\n)*
 <SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>
 {
   \\[0-7]{1,3} {
 <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));
     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]+ {
     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));
       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);
   }
     else
       obstack_1grow (&obstack_for_string, c);
   }
@@ -438,6 +484,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));
     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);
   }
     else
       obstack_1grow (&obstack_for_string, c);
   }
@@ -447,33 +495,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>
 {
 
 <SC_CHARACTER>
 {
-  "'"                  STRING_GROW; BEGIN context_state;
-  \\{splice}[^$@\[\]]  STRING_GROW;
-  <<EOF>>              unexpected_eof (token_start, "'"); BEGIN context_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>
 {
 <SC_STRING>
 {
-  "\""                 STRING_GROW; BEGIN context_state;
-  \\{splice}[^$@\[\]]  STRING_GROW;
-  <<EOF>> {
-    unexpected_eof (token_start, "\"");
-    BEGIN context_state;
-  }
+  "\""         STRING_GROW; BEGIN context_state;
+  \n           unexpected_newline (token_start, "\""); BEGIN context_state;
+  <<EOF>>      unexpected_eof (token_start, "\""); BEGIN context_state;
 }
 
 
 }
 
 
@@ -530,7 +572,7 @@ splice       (\\[ \f\t\v]*\n)*
       }
     else
       {
       }
     else
       {
-       complain_at (*loc, _("missing `{' in `%s'"),
+       complain_at (*loc, _("missing `{' in %s"),
                     token_name (token_type));
        obstack_sgrow (&obstack_for_string, "{}");
        STRING_FINISH;
                     token_name (token_type));
        obstack_sgrow (&obstack_for_string, "{}");
        STRING_FINISH;
@@ -578,9 +620,10 @@ splice      (\\[ \f\t\v]*\n)*
     if (outer_brace)
       {
        STRING_FINISH;
     if (outer_brace)
       {
        STRING_FINISH;
-       rule_length++;
        loc->start = code_start;
        val->chars = last_string;
        loc->start = code_start;
        val->chars = last_string;
+       increment_rule_length (*loc);
+       last_braced_code_loc = *loc;
        BEGIN INITIAL;
        return token_type;
       }
        BEGIN INITIAL;
        return token_type;
       }
@@ -593,6 +636,15 @@ splice      (\\[ \f\t\v]*\n)*
   "$"("<"{tag}">")?(-?[0-9]+|"$")  handle_dollar (token_type, yytext, *loc);
   "@"(-?[0-9]+|"$")               handle_at (token_type, yytext, *loc);
 
   "$"("<"{tag}">")?(-?[0-9]+|"$")  handle_dollar (token_type, yytext, *loc);
   "@"(-?[0-9]+|"$")               handle_at (token_type, yytext, *loc);
 
+  "$"  {
+    warn_at (*loc, _("stray `$'"));
+    obstack_sgrow (&obstack_for_string, "$][");
+  }
+  "@"  {
+    warn_at (*loc, _("stray `@'"));
+    obstack_sgrow (&obstack_for_string, "@@");
+  }
+
   <<EOF>>  unexpected_eof (code_start, "}"); BEGIN INITIAL;
 }
 
   <<EOF>>  unexpected_eof (code_start, "}"); BEGIN INITIAL;
 }
 
@@ -632,10 +684,9 @@ splice      (\\[ \f\t\v]*\n)*
 }
 
 
 }
 
 
-  /*----------------------------------------------------------------.
-  | By default, grow the string obstack with the input, escaping M4 |
-  | quoting characters.                                                    |
-  `----------------------------------------------------------------*/
+  /*-----------------------------------------.
+  | Escape M4 quoting characters in C code.  |
+  `-----------------------------------------*/
 
 <SC_COMMENT,SC_LINE_COMMENT,SC_STRING,SC_CHARACTER,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
 {
 
 <SC_COMMENT,SC_LINE_COMMENT,SC_STRING,SC_CHARACTER,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
 {
@@ -643,17 +694,46 @@ splice     (\\[ \f\t\v]*\n)*
   \@   obstack_sgrow (&obstack_for_string, "@@");
   \[   obstack_sgrow (&obstack_for_string, "@{");
   \]   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;
+
 %%
 
 %%
 
-/* Keeps track of the maximum number of semantic values to the left of 
-   a handle (those referenced by $0, $-1, etc.) are required by the 
+/* Keeps track of the maximum number of semantic values to the left of
+   a handle (those referenced by $0, $-1, etc.) are required by the
    semantic actions of this grammar. */
 int max_left_semantic_context = 0;
 
    semantic actions of this grammar. */
 int max_left_semantic_context = 0;
 
+/* If BUF is null, add BUFSIZE (which in this case must be less than
+   INT_MAX) to COLUMN; otherwise, add mbsnwidth (BUF, BUFSIZE, 0) to
+   COLUMN.  If an overflow occurs, or might occur but is undetectable,
+   return INT_MAX.  Assume COLUMN is nonnegative.  */
+
+static inline int
+add_column_width (int column, char const *buf, size_t bufsize)
+{
+  size_t width;
+  unsigned int remaining_columns = INT_MAX - column;
+
+  if (buf)
+    {
+      if (INT_MAX / 2 <= bufsize)
+       return INT_MAX;
+      width = mbsnwidth (buf, bufsize, 0);
+    }
+  else
+    width = bufsize;
+
+  return width <= remaining_columns ? column + width : INT_MAX;
+}
+
 /* Set *LOC and adjust scanner cursor to account for token TOKEN of
    size SIZE.  */
 
 /* Set *LOC and adjust scanner cursor to account for token TOKEN of
    size SIZE.  */
 
@@ -672,22 +752,30 @@ adjust_location (location *loc, char const *token, size_t size)
     switch (*p)
       {
       case '\n':
     switch (*p)
       {
       case '\n':
-       line++;
+       line += line < INT_MAX;
        column = 1;
        p0 = p + 1;
        break;
 
       case '\t':
        column = 1;
        p0 = p + 1;
        break;
 
       case '\t':
-       column += mbsnwidth (p0, p - p0, 0);
-       column += 8 - ((column - 1) & 7);
+       column = add_column_width (column, p0, p - p0);
+       column = add_column_width (column, NULL, 8 - ((column - 1) & 7));
        p0 = p + 1;
        break;
        p0 = p + 1;
        break;
+
+      default:
+       break;
       }
 
   scanner_cursor.line = line;
       }
 
   scanner_cursor.line = line;
-  scanner_cursor.column = column + mbsnwidth (p0, p - p0, 0);
+  scanner_cursor.column = column = add_column_width (column, p0, p - p0);
 
   loc->end = scanner_cursor;
 
   loc->end = scanner_cursor;
+
+  if (line == INT_MAX && loc->start.line != INT_MAX)
+    warn_at (*loc, _("line number overflow"));
+  if (column == INT_MAX && loc->start.column != INT_MAX)
+    warn_at (*loc, _("column number overflow"));
 }
 
 
 }
 
 
@@ -776,19 +864,18 @@ handle_action_dollar (char *text, location loc)
        type_name = "";
       obstack_fgrow1 (&obstack_for_string,
                      "]b4_lhs_value([%s])[", type_name);
        type_name = "";
       obstack_fgrow1 (&obstack_for_string,
                      "]b4_lhs_value([%s])[", type_name);
+      current_rule->used = true;
     }
   else
     {
     }
   else
     {
-      long num;
-      set_errno (0);
-      num = strtol (cp, 0, 10);
+      long int num = strtol (cp, NULL, 10);
 
 
-      if (INT_MIN <= num && num <= rule_length && ! get_errno ())
+      if (1 - INT_MAX + rule_length <= num && num <= rule_length)
        {
          int n = num;
        {
          int n = num;
-         if (1-n > max_left_semantic_context)
-           max_left_semantic_context = 1-n;
-         if (!type_name && n > 0)
+         if (max_left_semantic_context < 1 - n)
+           max_left_semantic_context = 1 - n;
+         if (!type_name && 0 < n)
            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"),
            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"),
@@ -796,8 +883,9 @@ handle_action_dollar (char *text, location loc)
          if (!type_name)
            type_name = "";
          obstack_fgrow3 (&obstack_for_string,
          if (!type_name)
            type_name = "";
          obstack_fgrow3 (&obstack_for_string,
-                         "]b4_rhs_value([%d], [%d], [%s])[",
+                         "]b4_rhs_value(%d, %d, [%s])[",
                          rule_length, n, type_name);
                          rule_length, n, type_name);
+         symbol_list_n_used_set (current_rule, n, true);
        }
       else
        complain_at (loc, _("integer out of range: %s"), quote (text));
        }
       else
        complain_at (loc, _("integer out of range: %s"), quote (text));
@@ -807,10 +895,10 @@ handle_action_dollar (char *text, location loc)
 }
 
 
 }
 
 
-/*-----------------------------------------------------------------.
-| Dispatch onto handle_action_dollar, or handle_destructor_dollar, |
-| depending upon TOKEN_TYPE.                                       |
-`-----------------------------------------------------------------*/
+/*----------------------------------------------------------------.
+| Map `$?' onto the proper M4 symbol, depending on its TOKEN_TYPE |
+| (are we in an action?).                                         |
+`----------------------------------------------------------------*/
 
 static void
 handle_dollar (int token_type, char *text, location loc)
 
 static void
 handle_dollar (int token_type, char *text, location loc)
@@ -823,6 +911,7 @@ handle_dollar (int token_type, char *text, location loc)
       break;
 
     case PERCENT_DESTRUCTOR:
       break;
 
     case PERCENT_DESTRUCTOR:
+    case PERCENT_INITIAL_ACTION:
     case PERCENT_PRINTER:
       if (text[1] == '$')
        {
     case PERCENT_PRINTER:
       if (text[1] == '$')
        {
@@ -857,14 +946,12 @@ handle_action_at (char *text, location loc)
     obstack_sgrow (&obstack_for_string, "]b4_lhs_location[");
   else
     {
     obstack_sgrow (&obstack_for_string, "]b4_lhs_location[");
   else
     {
-      long num;
-      set_errno (0);
-      num = strtol (cp, 0, 10);
+      long int num = strtol (cp, NULL, 10);
 
 
-      if (INT_MIN <= num && num <= rule_length && ! get_errno ())
+      if (1 - INT_MAX + rule_length <= num && num <= rule_length)
        {
          int n = num;
        {
          int n = num;
-         obstack_fgrow2 (&obstack_for_string, "]b4_rhs_location([%d], [%d])[",
+         obstack_fgrow2 (&obstack_for_string, "]b4_rhs_location(%d, %d)[",
                          rule_length, n);
        }
       else
                          rule_length, n);
        }
       else
@@ -875,10 +962,10 @@ handle_action_at (char *text, location loc)
 }
 
 
 }
 
 
-/*-------------------------------------------------------------------.
-| Dispatch onto handle_action_at, or handle_destructor_at, depending |
-| upon CODE_KIND.                                                    |
-`-------------------------------------------------------------------*/
+/*----------------------------------------------------------------.
+| Map `@?' onto the proper M4 symbol, depending on its TOKEN_TYPE |
+| (are we in an action?).                                         |
+`----------------------------------------------------------------*/
 
 static void
 handle_at (int token_type, char *text, location loc)
 
 static void
 handle_at (int token_type, char *text, location loc)
@@ -889,6 +976,7 @@ handle_at (int token_type, char *text, location loc)
       handle_action_at (text, loc);
       return;
 
       handle_action_at (text, loc);
       return;
 
+    case PERCENT_INITIAL_ACTION:
     case PERCENT_DESTRUCTOR:
     case PERCENT_PRINTER:
       if (text[1] == '$')
     case PERCENT_DESTRUCTOR:
     case PERCENT_PRINTER:
       if (text[1] == '$')
@@ -906,6 +994,26 @@ handle_at (int token_type, char *text, location loc)
 }
 
 
 }
 
 
+/*------------------------------------------------------.
+| Scan NUMBER for a base-BASE integer at location LOC.  |
+`------------------------------------------------------*/
+
+static unsigned long int
+scan_integer (char const *number, int base, location loc)
+{
+  verify (INT_MAX < ULONG_MAX);
+  unsigned long int num = strtoul (number, NULL, base);
+
+  if (INT_MAX < num)
+    {
+      complain_at (loc, _("integer out of range: %s"), quote (number));
+      num = INT_MAX;
+    }
+
+  return num;
+}
+
+
 /*------------------------------------------------------------------.
 | Convert universal character name UCN to a single-byte character,  |
 | and return that character.  Return -1 if UCN does not correspond  |
 /*------------------------------------------------------------------.
 | Convert universal character name UCN to a single-byte character,  |
 | and return that character.  Return -1 if UCN does not correspond  |
@@ -915,7 +1023,8 @@ handle_at (int token_type, char *text, location loc)
 static int
 convert_ucn_to_byte (char const *ucn)
 {
 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
 
   /* FIXME: Currently we assume Unicode-compatible unibyte characters
      on ASCII hosts (i.e., Latin-1 on hosts with 8-bit bytes).  On
@@ -967,18 +1076,50 @@ convert_ucn_to_byte (char const *ucn)
 `----------------------------------------------------------------*/
 
 static void
 `----------------------------------------------------------------*/
 
 static void
-handle_syncline (char *args)
+handle_syncline (char *args, location loc)
 {
 {
-  int lineno = strtol (args, &args, 10);
-  const char *file = NULL;
-  file = strchr (args, '"') + 1;
-  *strchr (file, '"') = 0;
-  scanner_cursor.file = current_file = xstrdup (file);
+  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;
+    }
+  scanner_cursor.file = current_file = uniqstr_new (file);
   scanner_cursor.line = lineno;
   scanner_cursor.column = 1;
 }
 
 
   scanner_cursor.line = lineno;
   scanner_cursor.column = 1;
 }
 
 
+/*---------------------------------.
+| Report a rule that is too long.  |
+`---------------------------------*/
+
+static void
+rule_length_overflow (location loc)
+{
+  fatal_at (loc, _("rule is too long"));
+}
+
+
+/*----------------------------------------------------------------.
+| 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.  |
 /*------------------------------------------------------------------------.
 | 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.  |
@@ -987,10 +1128,18 @@ handle_syncline (char *args)
 static void
 unexpected_eof (boundary start, char const *token_end)
 {
 static void
 unexpected_eof (boundary start, char const *token_end)
 {
-  location loc;
-  loc.start = start;
-  loc.end = scanner_cursor;
-  complain_at (loc, _("missing `%s' at end of file"), 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);
 }
 
 
 }