]> git.saurik.com Git - bison.git/blobdiff - src/scan-gram.l
glr.c: scope reduction
[bison.git] / src / scan-gram.l
index 61545cbba7760ca76ff66144e5d29dd356d08174..5e78cb9b7ade7994980e04af7fd687fc34e05844 100644 (file)
@@ -1,12 +1,12 @@
 /* Bison Grammar Scanner                             -*- C -*-
 
-   Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+   Copyright (C) 2002-2012 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,
    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., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301  USA
-*/
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-%option debug nodefault nounput noyywrap never-interactive
+%option debug nodefault noinput nounput noyywrap never-interactive
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
-#include <config.h>
-#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
 
 #include "complain.h"
 #include "files.h"
-#include "getargs.h"    /* yacc_flag */
 #include "gram.h"
 #include "quotearg.h"
 #include "reader.h"
 #include "uniqstr.h"
 
+#include <c-ctype.h>
 #include <mbswidth.h>
 #include <quote.h>
 
    code_start = scanner_cursor = loc->start;           \
 
 /* Location of scanner cursor.  */
-boundary 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))
 
+#define ROLLBACK_CURRENT_TOKEN                                  \
+  do {                                                          \
+    scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);    \
+    yyless (0);                                                 \
+  } while (0)
+
 /* A string representing the most recently saved token.  */
-char *last_string;
+static char *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
 gram_scanner_last_string_free (void)
@@ -70,10 +76,6 @@ gram_scanner_last_string_free (void)
   STRING_FREE;
 }
 
-/* The location of the most recently saved token, if it was a
-   BRACED_CODE token; otherwise, this has an unspecified value.  */
-location gram_last_braced_code_loc;
-
 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);
@@ -86,11 +88,11 @@ static void unexpected_newline (boundary, char const *);
  /* Strings and characters in directives/rules. */
 %x SC_ESCAPED_STRING SC_ESCAPED_CHARACTER
  /* A identifier was just read in directives/rules.  Special state
-    to capture the sequence `identifier :'. */
+    to capture the sequence 'identifier :'. */
 %x SC_AFTER_IDENTIFIER
 
  /* Three types of user code:
-    - prologue (code between `%{' `%}' in the first section, before %%);
+    - 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
@@ -98,10 +100,12 @@ static void unexpected_newline (boundary, char const *);
 %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_]
-id       {letter}({letter}|[0-9])*
-directive %{letter}({letter}|[0-9]|-)*
+letter    [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
+notletter [^.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]{-}[%\{]
+id       {letter}({letter}|[-0-9])*
 int      [0-9]+
 
 /* POSIX says that a tag must be both an id and a C union member, but
@@ -116,13 +120,13 @@ splice     (\\[ \f\t\v]*\n)*
 %%
 %{
   /* Nesting level of the current code in braces.  */
-  int braces_level IF_LINT (= 0);
+  int braces_level PACIFY_CC (= 0);
 
   /* Parent context state, when applicable.  */
-  int context_state IF_LINT (= 0);
+  int context_state PACIFY_CC (= 0);
 
   /* Location of most recent identifier, when applicable.  */
-  location id_loc IF_LINT (= empty_location);
+  location id_loc PACIFY_CC (= empty_location);
 
   /* Where containing code started, when applicable.  Its initial
      value is relevant only when yylex is invoked in the SC_EPILOGUE
@@ -131,7 +135,7 @@ splice       (\\[ \f\t\v]*\n)*
 
   /* Where containing comment or string or character literal started,
      when applicable.  */
-  boundary token_start IF_LINT (= scanner_cursor);
+  boundary token_start PACIFY_CC (= scanner_cursor);
 %}
 
 
@@ -139,12 +143,12 @@ splice     (\\[ \f\t\v]*\n)*
   | Scanning white space.  |
   `-----------------------*/
 
-<INITIAL,SC_AFTER_IDENTIFIER>
+<INITIAL,SC_AFTER_IDENTIFIER,SC_BRACKETED_ID,SC_RETURN_BRACKETED_ID>
 {
   /* Comments and white space.  */
-  ","         warn_at (*loc, _("stray `,' treated as white space"));
+  ","          warn_at (*loc, _("stray ',' treated as white space"));
   [ \f\n\t\v]  |
-  "//".*       ;
+  "//".*       continue;
   "/*" {
     token_start = loc->start;
     context_state = YY_START;
@@ -153,7 +157,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 "{int}(" \"".*"\"")?"\n" {
     handle_syncline (yytext + sizeof "#line " - 1, *loc);
   }
 }
@@ -162,60 +166,72 @@ 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;
-  "%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;
+  "%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;
-  "%initial-action"                return PERCENT_INITIAL_ACTION;
-  "%glr-parser"                    return PERCENT_GLR_PARSER;
-  "%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} {
+  "%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;
+
+  "%"{id}|"%"{notletter}([[:graph:]])+ {
     complain_at (*loc, _("invalid directive: %s"), quote (yytext));
   }
 
   "="                     return EQUAL;
   "|"                     return PIPE;
   ";"                     return SEMICOLON;
+  "<*>"                   return TYPE_TAG_ANY;
+  "<>"                    return TYPE_TAG_NONE;
 
   {id} {
     val->uniqstr = uniqstr_new (yytext);
     id_loc = *loc;
+    bracketed_id_str = NULL;
     BEGIN SC_AFTER_IDENTIFIER;
   }
 
@@ -228,8 +244,14 @@ splice      (\\[ \f\t\v]*\n)*
     return INT;
   }
 
-  /* Characters.  We don't check there is only one.  */
-  "'"        STRING_GROW; token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
+  /* 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.  */
+  "'"        token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
 
   /* Strings. */
   "\""       token_start = loc->start; BEGIN SC_ESCAPED_STRING;
@@ -239,8 +261,6 @@ splice       (\\[ \f\t\v]*\n)*
 
   /* Code in between braces.  */
   "{" {
-    if (current_rule && current_rule->action)
-      grammar_midrule_action ();
     STRING_GROW;
     braces_level = 0;
     code_start = loc->start;
@@ -263,8 +283,17 @@ splice      (\\[ \f\t\v]*\n)*
     return PERCENT_PERCENT;
   }
 
-  . {
-    complain_at (*loc, _("invalid character: %s"), quote (yytext));
+  "[" {
+    bracketed_id_str = NULL;
+    bracketed_id_start = loc->start;
+    bracketed_id_context_state = YY_START;
+    BEGIN SC_BRACKETED_ID;
+  }
+
+  [^\[%A-Za-z0-9_<>{}\"\'*;|=/, \f\n\t\v]+|. {
+    complain_at (*loc, "%s: %s",
+                 ngettext ("invalid character", "invalid characters", yyleng),
+                 quote_mem (yytext, yyleng));
   }
 
   <<EOF>> {
@@ -280,40 +309,113 @@ 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;
+      }
+  }
   ":" {
+    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"));
+  }
+
+  [^\].A-Za-z0-9_/ \f\n\t\v]+|. {
+    complain_at (*loc, "%s: %s",
+                 ngettext ("invalid character in bracketed name",
+                           "invalid characters in bracketed name", yyleng),
+                 quote_mem (yytext, yyleng));
+  }
+
+  <<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.  |
+  | Scanning a Yacc comment.  The initial '/ *' is already eaten.  |
   `---------------------------------------------------------------*/
 
 <SC_YACC_COMMENT>
 {
   "*/"     BEGIN context_state;
-  .|\n    ;
+  .|\n    continue;
   <<EOF>>  unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
   /*------------------------------------------------------------.
-  | Scanning a C comment.  The initial `/ *' is already eaten.  |
+  | Scanning a C comment.  The initial '/ *' is already eaten.  |
   `------------------------------------------------------------*/
 
 <SC_COMMENT>
@@ -324,7 +426,7 @@ splice       (\\[ \f\t\v]*\n)*
 
 
   /*--------------------------------------------------------------.
-  | Scanning a line comment.  The initial `//' is already eaten.  |
+  | Scanning a line comment.  The initial '//' is already eaten.  |
   `--------------------------------------------------------------*/
 
 <SC_LINE_COMMENT>
@@ -342,15 +444,23 @@ splice     (\\[ \f\t\v]*\n)*
 
 <SC_ESCAPED_STRING>
 {
-  "\"" {
+  "\""|"\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;
     BEGIN INITIAL;
     return STRING;
   }
-  \n           unexpected_newline (token_start, "\""); BEGIN INITIAL;
-  <<EOF>>      unexpected_eof (token_start, "\"");     BEGIN INITIAL;
 }
 
   /*----------------------------------------------------------.
@@ -360,17 +470,47 @@ splice     (\\[ \f\t\v]*\n)*
 
 <SC_ESCAPED_CHARACTER>
 {
-  "'" {
-    STRING_GROW;
+  "'"|"\n" {
     STRING_FINISH;
     loc->start = token_start;
-    val->character = last_string[1];
+    val->character = last_string[0];
+    {
+      /* FIXME: Eventually, make these errors.  */
+      if (last_string[0] == '\0')
+        {
+          warn_at (*loc, _("empty character literal"));
+          /* '\0' seems dangerous even if we are about to complain.  */
+          val->character = '\'';
+        }
+      else if (last_string[1] != '\0')
+        warn_at (*loc, _("extra characters in character literal"));
+    }
+    if (yytext[0] == '\n')
+      unexpected_newline (token_start, "'");
+    STRING_FREE;
+    BEGIN INITIAL;
+    return CHAR;
+  }
+  <<EOF>> {
+    STRING_FINISH;
+    loc->start = token_start;
+    val->character = last_string[0];
+    {
+      /* FIXME: Eventually, make these errors.  */
+      if (last_string[0] == '\0')
+        {
+          warn_at (*loc, _("empty character literal"));
+          /* '\0' seems dangerous even if we are about to complain.  */
+          val->character = '\'';
+        }
+      else if (last_string[1] != '\0')
+        warn_at (*loc, _("extra characters in character literal"));
+    }
+    unexpected_eof (token_start, "'");
     STRING_FREE;
     BEGIN INITIAL;
     return CHAR;
   }
-  \n           unexpected_newline (token_start, "'");  BEGIN INITIAL;
-  <<EOF>>      unexpected_eof (token_start, "'");      BEGIN INITIAL;
 }
 
 <SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING>
@@ -387,10 +527,9 @@ splice      (\\[ \f\t\v]*\n)*
 {
   \\[0-7]{1,3} {
     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));
+    if (!c || UCHAR_MAX < c)
+      complain_at (*loc, _("invalid number after \\-escape: %s"),
+                   yytext+1);
     else
       obstack_1grow (&obstack_for_string, c);
   }
@@ -398,10 +537,9 @@ splice      (\\[ \f\t\v]*\n)*
   \\x[0-9abcdefABCDEF]+ {
     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));
+    if (!c || UCHAR_MAX < c)
+      complain_at (*loc, _("invalid number after \\-escape: %s"),
+                   yytext+1);
     else
       obstack_1grow (&obstack_for_string, c);
   }
@@ -419,16 +557,20 @@ splice     (\\[ \f\t\v]*\n)*
 
   \\(u|U[0-9abcdefABCDEF]{4})[0-9abcdefABCDEF]{4} {
     int c = convert_ucn_to_byte (yytext);
-    if (c < 0)
-      complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
-    else if (! c)
-      complain_at (*loc, _("invalid null character: %s"), quote (yytext));
+    if (c <= 0)
+      complain_at (*loc, _("invalid number after \\-escape: %s"),
+                   yytext+1);
     else
       obstack_1grow (&obstack_for_string, c);
   }
   \\(.|\n)     {
-    complain_at (*loc, _("unrecognized escape sequence: %s"), quote (yytext));
-    STRING_GROW;
+    char const *p = yytext + 1;
+    /* Quote only if escaping won't make the character visible.  */
+    if (c_isspace ((unsigned char) *p) && c_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);
   }
 }
 
@@ -506,18 +648,24 @@ splice     (\\[ \f\t\v]*\n)*
       {
        STRING_FINISH;
        loc->start = code_start;
-       val->chars = last_string;
-       gram_last_braced_code_loc = *loc;
+       val->code = last_string;
        BEGIN INITIAL;
        return BRACED_CODE;
       }
   }
 
-  /* Tokenize `<<%' correctly (as `<<' `%') rather than incorrrectly
-     (as `<' `<%').  */
+  /* Tokenize '<<%' correctly (as '<<' '%') rather than incorrrectly
+     (as '<' '<%').  */
   "<"{splice}"<"  STRING_GROW;
 
-  <<EOF>>  unexpected_eof (code_start, "}"); BEGIN INITIAL;
+  <<EOF>> {
+    unexpected_eof (code_start, "}");
+    STRING_FINISH;
+    loc->start = code_start;
+    val->code = last_string;
+    BEGIN INITIAL;
+    return BRACED_CODE;
+  }
 }
 
 
@@ -535,7 +683,14 @@ splice      (\\[ \f\t\v]*\n)*
     return PROLOGUE;
   }
 
-  <<EOF>>  unexpected_eof (code_start, "%}"); BEGIN INITIAL;
+  <<EOF>> {
+    unexpected_eof (code_start, "%}");
+    STRING_FINISH;
+    loc->start = code_start;
+    val->chars = last_string;
+    BEGIN INITIAL;
+    return PROLOGUE;
+  }
 }
 
 
@@ -690,23 +845,27 @@ convert_ucn_to_byte (char const *ucn)
 }
 
 
-/*----------------------------------------------------------------.
-| Handle `#line INT "FILE"'.  ARGS has already skipped `#line '.  |
-`----------------------------------------------------------------*/
+/*---------------------------------------------------------------------.
+| Handle '#line INT( "FILE")?\n'.  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';
+  char *file;
+  unsigned long int lineno = strtoul (args, &file, 10);
   if (INT_MAX <= lineno)
     {
       warn_at (loc, _("line number overflow"));
       lineno = INT_MAX;
     }
-  current_file = uniqstr_new (file);
+
+  file = mbschr (file, '"');
+  if (file)
+    {
+      *mbschr (file + 1, '"') = '\0';
+      current_file = uniqstr_new (file + 1);
+    }
   boundary_set (&scanner_cursor, current_file, lineno, 1);
 }
 
@@ -723,6 +882,10 @@ unexpected_end (boundary start, char const *msgid, char const *token_end)
   location loc;
   loc.start = start;
   loc.end = scanner_cursor;
+  token_end = quote (token_end);
+  // Instead of '\'', display "'".
+  if (!strcmp (token_end, "'\\''"))
+    token_end = "\"'\"";
   complain_at (loc, _(msgid), token_end);
 }
 
@@ -735,7 +898,7 @@ unexpected_end (boundary start, char const *msgid, char const *token_end)
 static void
 unexpected_eof (boundary start, char const *token_end)
 {
-  unexpected_end (start, N_("missing `%s' at end of file"), token_end);
+  unexpected_end (start, N_("missing %s at end of file"), token_end);
 }
 
 
@@ -746,7 +909,7 @@ unexpected_eof (boundary start, char const *token_end)
 static void
 unexpected_newline (boundary start, char const *token_end)
 {
-  unexpected_end (start, N_("missing `%s' at end of line"), token_end);
+  unexpected_end (start, N_("missing %s at end of line"), token_end);
 }
 
 
@@ -770,5 +933,5 @@ gram_scanner_free (void)
 {
   obstack_free (&obstack_for_string, 0);
   /* Reclaim Flex's buffers.  */
-  yy_delete_buffer (YY_CURRENT_BUFFER);
+  yylex_destroy ();
 }