]> git.saurik.com Git - bison.git/blobdiff - src/scan-gram.l
doc: fix environment issues.
[bison.git] / src / scan-gram.l
index a6bd5e2f1967a7857e2e832ce58fd1a328affe89..e2e08f9ab9dbf62253ee67e27d4c59c0d5840c52 100644 (file)
@@ -1,7 +1,6 @@
 /* Bison Grammar Scanner                             -*- C -*-
 
 /* Bison Grammar Scanner                             -*- C -*-
 
-   Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
-   Free Software Foundation, Inc.
+   Copyright (C) 2002-2012 Free Software Foundation, Inc.
 
    This file is part of Bison, the GNU Compiler Compiler.
 
 
    This file is part of Bison, the GNU Compiler Compiler.
 
@@ -18,7 +17,7 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
    You should have received a copy of the GNU General Public License
    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"
 
 %{
 %option prefix="gram_" outfile="lex.yy.c"
 
 %{
 #include <src/reader.h>
 #include <src/uniqstr.h>
 
 #include <src/reader.h>
 #include <src/uniqstr.h>
 
+#include <ctype.h>
 #include <mbswidth.h>
 #include <quote.h>
 #include <mbswidth.h>
 #include <quote.h>
+#include <streq.h>
 
 #include <src/scan-gram.h>
 
 #define YY_DECL GRAM_LEX_DECL
 
 
 #include <src/scan-gram.h>
 
 #define YY_DECL GRAM_LEX_DECL
 
-#define YY_USER_INIT                                   \
-   code_start = scanner_cursor = loc->start;           \
+#define YY_USER_INIT                                    \
+   code_start = scanner_cursor = loc->start;            \
 
 /* Location of scanner cursor.  */
 static boundary scanner_cursor;
 
 /* Location of scanner cursor.  */
 static boundary scanner_cursor;
@@ -55,15 +56,21 @@ static boundary scanner_cursor;
 static size_t no_cr_read (FILE *, char *, size_t);
 #define YY_INPUT(buf, result, size) ((result) = no_cr_read (yyin, buf, size))
 
 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)              \
+#define RETURN_PERCENT_PARAM(Value)                     \
+  RETURN_VALUE(PERCENT_PARAM, param, param_ ## Value)
+
+#define RETURN_PERCENT_FLAG(Value)                              \
+  RETURN_VALUE(PERCENT_FLAG, uniqstr, uniqstr_new (Value))
+
+#define RETURN_VALUE(Token, Field, Value)       \
   do {                                          \
   do {                                          \
-    val->uniqstr = uniqstr_new (Value);         \
-    return PERCENT_FLAG;                        \
+    val->Field = Value;                         \
+    return Token;                               \
   } while (0)
 
 #define ROLLBACK_CURRENT_TOKEN                                  \
   do {                                                          \
   } while (0)
 
 #define ROLLBACK_CURRENT_TOKEN                                  \
   do {                                                          \
-    scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);    \
+    scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);     \
     yyless (0);                                                 \
   } while (0)
 
     yyless (0);                                                 \
   } while (0)
 
@@ -94,16 +101,17 @@ 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
  /* 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
  /* A complex tag, with nested angles brackets. */
 %x SC_TAG
 
 %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 %%);
+ /* Four types of user code:
+    - prologue (code between '%{' '%}' in the first section, before %%);
     - actions, printers, union, etc, (between braced in the middle section);
     - actions, printers, union, etc, (between braced in the middle section);
-    - epilogue (everything after the second %%). */
-%x SC_PROLOGUE SC_BRACED_CODE SC_EPILOGUE
+    - epilogue (everything after the second %%).
+    - predicate (code between '%?{' and '{' in middle section); */
+%x SC_PROLOGUE SC_BRACED_CODE SC_EPILOGUE SC_PREDICATE
  /* C and C++ comments in code. */
 %x SC_COMMENT SC_LINE_COMMENT
  /* Strings and characters in code. */
  /* C and C++ comments in code. */
 %x SC_COMMENT SC_LINE_COMMENT
  /* Strings and characters in code. */
@@ -111,33 +119,33 @@ static void unexpected_newline (boundary, char const *);
  /* Bracketed identifiers support. */
 %x SC_BRACKETED_ID SC_RETURN_BRACKETED_ID
 
  /* Bracketed identifiers support. */
 %x SC_BRACKETED_ID SC_RETURN_BRACKETED_ID
 
-letter   [-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
-id       {letter}({letter}|[0-9])*
+letter    [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
+id        {letter}({letter}|[-0-9])*
 directive %{id}
 directive %{id}
-int      [0-9]+
+int       [0-9]+
 
 /* POSIX says that a tag must be both an id and a C union member, but
    historically almost any character is allowed in a tag.  We disallow
    NUL, as this simplifies our implementation.  We disallow angle
    bracket to match them in nested pairs: several languages use them
    for generics/template types.  */
 
 /* 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, 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<>]+
+tag      [^\0<>]+
 
 /* Zero or more instances of backslash-newline.  Following GCC, allow
    white space between the backslash and the newline.  */
 
 /* Zero or more instances of backslash-newline.  Following GCC, allow
    white space between the backslash and the newline.  */
-splice  (\\[ \f\t\v]*\n)*
+splice   (\\[ \f\t\v]*\n)*
 
 %%
 %{
   /* Nesting level.  Either for nested braces, or nested angle brackets
      (but not mixed).  */
 
 %%
 %{
   /* Nesting level.  Either for nested braces, or nested angle brackets
      (but not mixed).  */
-  int nesting IF_LINT (= 0);
+  int nesting PACIFY_CC (= 0);
 
   /* Parent context state, when applicable.  */
 
   /* 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 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
 
   /* Where containing code started, when applicable.  Its initial
      value is relevant only when yylex is invoked in the SC_EPILOGUE
@@ -146,7 +154,7 @@ splice       (\\[ \f\t\v]*\n)*
 
   /* Where containing comment or string or character literal started,
      when applicable.  */
 
   /* 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);
 %}
 
 
 %}
 
 
@@ -157,7 +165,7 @@ splice       (\\[ \f\t\v]*\n)*
 <INITIAL,SC_AFTER_IDENTIFIER,SC_BRACKETED_ID,SC_RETURN_BRACKETED_ID>
 {
   /* Comments and white space.  */
 <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]  |
   "//".*       ;
   "/*" {
   [ \f\n\t\v]  |
   "//".*       ;
   "/*" {
@@ -179,7 +187,7 @@ splice       (\\[ \f\t\v]*\n)*
   `----------------------------*/
 
   /* For directives that are also command line options, the regex must be
   `----------------------------*/
 
   /* 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.  */
      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.  */
@@ -203,7 +211,7 @@ splice       (\\[ \f\t\v]*\n)*
   "%glr-parser"                     return PERCENT_GLR_PARSER;
   "%language"                       return PERCENT_LANGUAGE;
   "%left"                           return PERCENT_LEFT;
   "%glr-parser"                     return PERCENT_GLR_PARSER;
   "%language"                       return PERCENT_LANGUAGE;
   "%left"                           return PERCENT_LEFT;
-  "%lex-param"                      return PERCENT_LEX_PARAM;
+  "%lex-param"                      RETURN_PERCENT_PARAM(lex);
   "%locations"                      RETURN_PERCENT_FLAG("locations");
   "%merge"                          return PERCENT_MERGE;
   "%name"[-_]"prefix"               return PERCENT_NAME_PREFIX;
   "%locations"                      RETURN_PERCENT_FLAG("locations");
   "%merge"                          return PERCENT_MERGE;
   "%name"[-_]"prefix"               return PERCENT_NAME_PREFIX;
@@ -213,7 +221,8 @@ splice       (\\[ \f\t\v]*\n)*
   "%nondeterministic-parser"        return PERCENT_NONDETERMINISTIC_PARSER;
   "%nterm"                          return PERCENT_NTERM;
   "%output"                         return PERCENT_OUTPUT;
   "%nondeterministic-parser"        return PERCENT_NONDETERMINISTIC_PARSER;
   "%nterm"                          return PERCENT_NTERM;
   "%output"                         return PERCENT_OUTPUT;
-  "%parse-param"                    return PERCENT_PARSE_PARAM;
+  "%param"                          RETURN_PERCENT_PARAM(both);
+  "%parse-param"                    RETURN_PERCENT_PARAM(parse);
   "%prec"                           return PERCENT_PREC;
   "%precedence"                     return PERCENT_PRECEDENCE;
   "%printer"                        return PERCENT_PRINTER;
   "%prec"                           return PERCENT_PREC;
   "%precedence"                     return PERCENT_PRECEDENCE;
   "%printer"                        return PERCENT_PRINTER;
@@ -261,10 +270,10 @@ splice     (\\[ \f\t\v]*\n)*
   }
 
   /* Characters.  */
   }
 
   /* Characters.  */
-  "'"        STRING_GROW; token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
+  "'"         token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
 
   /* Strings. */
 
   /* Strings. */
-  "\""       token_start = loc->start; BEGIN SC_ESCAPED_STRING;
+  "\""        token_start = loc->start; BEGIN SC_ESCAPED_STRING;
 
   /* Prologue. */
   "%{"        code_start = loc->start; BEGIN SC_PROLOGUE;
 
   /* Prologue. */
   "%{"        code_start = loc->start; BEGIN SC_PROLOGUE;
@@ -277,6 +286,13 @@ splice      (\\[ \f\t\v]*\n)*
     BEGIN SC_BRACED_CODE;
   }
 
     BEGIN SC_BRACED_CODE;
   }
 
+  /* Semantic predicate. */
+  "%?"[ \f\n\t\v]*"{" {
+    nesting = 0;
+    code_start = loc->start;
+    BEGIN SC_PREDICATE;
+  }
+
   /* A type. */
   "<*>"       return TAG_ANY;
   "<>"        return TAG_NONE;
   /* A type. */
   "<*>"       return TAG_ANY;
   "<>"        return TAG_NONE;
@@ -325,7 +341,7 @@ splice       (\\[ \f\t\v]*\n)*
 
 <SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING,SC_TAG>
 {
 
 <SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING,SC_TAG>
 {
-  \0       complain_at (*loc, _("invalid null character"));
+  \0        complain_at (*loc, _("invalid null character"));
 }
 
 
 }
 
 
@@ -338,16 +354,16 @@ splice     (\\[ \f\t\v]*\n)*
   "[" {
     if (bracketed_id_str)
       {
   "[" {
     if (bracketed_id_str)
       {
-       ROLLBACK_CURRENT_TOKEN;
-       BEGIN SC_RETURN_BRACKETED_ID;
-       *loc = id_loc;
-       return ID;
+        ROLLBACK_CURRENT_TOKEN;
+        BEGIN SC_RETURN_BRACKETED_ID;
+        *loc = id_loc;
+        return ID;
       }
     else
       {
       }
     else
       {
-       bracketed_id_start = loc->start;
-       bracketed_id_context_state = YY_START;
-       BEGIN SC_BRACKETED_ID;
+        bracketed_id_start = loc->start;
+        bracketed_id_context_state = YY_START;
+        BEGIN SC_BRACKETED_ID;
       }
   }
   ":" {
       }
   }
   ":" {
@@ -377,33 +393,33 @@ splice     (\\[ \f\t\v]*\n)*
   {id} {
     if (bracketed_id_str)
       {
   {id} {
     if (bracketed_id_str)
       {
-       complain_at (*loc, _("unexpected identifier in bracketed name: %s"),
-                    quote (yytext));
+        complain_at (*loc, _("unexpected identifier in bracketed name: %s"),
+                     quote (yytext));
       }
     else
       {
       }
     else
       {
-       bracketed_id_str = uniqstr_new (yytext);
-       bracketed_id_loc = *loc;
+        bracketed_id_str = uniqstr_new (yytext);
+        bracketed_id_loc = *loc;
       }
   }
   "]" {
     BEGIN bracketed_id_context_state;
     if (bracketed_id_str)
       {
       }
   }
   "]" {
     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;
-         }
+        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"),
       }
     else
       complain_at (*loc, _("an identifier expected"));
   }
   . {
     complain_at (*loc, _("invalid character in bracketed name: %s"),
-                quote (yytext));
+                 quote (yytext));
   }
   <<EOF>> {
     BEGIN bracketed_id_context_state;
   }
   <<EOF>> {
     BEGIN bracketed_id_context_state;
@@ -425,37 +441,37 @@ splice     (\\[ \f\t\v]*\n)*
 
 
   /*---------------------------------------------------------------.
 
 
   /*---------------------------------------------------------------.
-  | Scanning a Yacc comment.  The initial `/ *' is already eaten.  |
+  | Scanning a Yacc comment.  The initial '/ *' is already eaten.  |
   `---------------------------------------------------------------*/
 
 <SC_YACC_COMMENT>
 {
   "*/"     BEGIN context_state;
   `---------------------------------------------------------------*/
 
 <SC_YACC_COMMENT>
 {
   "*/"     BEGIN context_state;
-  .|\n    ;
+  .|\n     ;
   <<EOF>>  unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
   /*------------------------------------------------------------.
   <<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>
 {
   "*"{splice}"/"  STRING_GROW; BEGIN context_state;
   `------------------------------------------------------------*/
 
 <SC_COMMENT>
 {
   "*"{splice}"/"  STRING_GROW; BEGIN context_state;
-  <<EOF>>        unexpected_eof (token_start, "*/"); BEGIN context_state;
+  <<EOF>>         unexpected_eof (token_start, "*/"); BEGIN context_state;
 }
 
 
   /*--------------------------------------------------------------.
 }
 
 
   /*--------------------------------------------------------------.
-  | Scanning a line comment.  The initial `//' is already eaten.  |
+  | Scanning a line comment.  The initial '//' is already eaten.  |
   `--------------------------------------------------------------*/
 
 <SC_LINE_COMMENT>
 {
   `--------------------------------------------------------------*/
 
 <SC_LINE_COMMENT>
 {
-  "\n"          STRING_GROW; BEGIN context_state;
-  {splice}      STRING_GROW;
-  <<EOF>>       BEGIN context_state;
+  "\n"           STRING_GROW; BEGIN context_state;
+  {splice}       STRING_GROW;
+  <<EOF>>        BEGIN context_state;
 }
 
 
 }
 
 
@@ -487,22 +503,24 @@ splice     (\\[ \f\t\v]*\n)*
 
   /*----------------------------------------------------------.
   | Scanning a Bison character literal, decoding its escapes. |
 
   /*----------------------------------------------------------.
   | Scanning a Bison character literal, decoding its escapes. |
-  | The initial quote is already eaten.                              |
+  | The initial quote is already eaten.                       |
   `----------------------------------------------------------*/
 
 <SC_ESCAPED_CHARACTER>
 {
   "'"|"\n" {
   `----------------------------------------------------------*/
 
 <SC_ESCAPED_CHARACTER>
 {
   "'"|"\n" {
-    STRING_GROW;
     STRING_FINISH;
     loc->start = token_start;
     STRING_FINISH;
     loc->start = token_start;
-    val->character = last_string[1];
+    val->character = last_string[0];
     {
       /* FIXME: Eventually, make these errors.  */
     {
       /* FIXME: Eventually, make these errors.  */
-      size_t length = strlen (last_string);
-      if (length < 3)
-        warn_at (*loc, _("empty character literal"));
-      else if (length > 3)
+      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')
         warn_at (*loc, _("extra characters in character literal"));
     }
     if (yytext[0] == '\n')
@@ -514,17 +532,17 @@ splice     (\\[ \f\t\v]*\n)*
   <<EOF>> {
     STRING_FINISH;
     loc->start = token_start;
   <<EOF>> {
     STRING_FINISH;
     loc->start = token_start;
+    val->character = last_string[0];
     {
     {
-      size_t length = strlen (last_string);
       /* FIXME: Eventually, make these errors.  */
       /* FIXME: Eventually, make these errors.  */
-      if (length < 2)
-        warn_at (*loc, _("empty character literal"));
-      else if (length > 2)
+      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"));
         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;
     }
     unexpected_eof (token_start, "'");
     STRING_FREE;
@@ -576,10 +594,9 @@ splice      (\\[ \f\t\v]*\n)*
 {
   \\[0-7]{1,3} {
     unsigned long int c = strtoul (yytext + 1, NULL, 8);
 {
   \\[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);
   }
     else
       obstack_1grow (&obstack_for_string, c);
   }
@@ -587,37 +604,40 @@ splice     (\\[ \f\t\v]*\n)*
   \\x[0-9abcdefABCDEF]+ {
     verify (UCHAR_MAX < ULONG_MAX);
     unsigned long int c = strtoul (yytext + 2, NULL, 16);
   \\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);
   }
 
     else
       obstack_1grow (&obstack_for_string, c);
   }
 
-  \\a  obstack_1grow (&obstack_for_string, '\a');
-  \\b  obstack_1grow (&obstack_for_string, '\b');
-  \\f  obstack_1grow (&obstack_for_string, '\f');
-  \\n  obstack_1grow (&obstack_for_string, '\n');
-  \\r  obstack_1grow (&obstack_for_string, '\r');
-  \\t  obstack_1grow (&obstack_for_string, '\t');
-  \\v  obstack_1grow (&obstack_for_string, '\v');
+  \\a   obstack_1grow (&obstack_for_string, '\a');
+  \\b   obstack_1grow (&obstack_for_string, '\b');
+  \\f   obstack_1grow (&obstack_for_string, '\f');
+  \\n   obstack_1grow (&obstack_for_string, '\n');
+  \\r   obstack_1grow (&obstack_for_string, '\r');
+  \\t   obstack_1grow (&obstack_for_string, '\t');
+  \\v   obstack_1grow (&obstack_for_string, '\v');
 
   /* \\[\"\'?\\] would be shorter, but it confuses xgettext.  */
   \\("\""|"'"|"?"|"\\")  obstack_1grow (&obstack_for_string, yytext[1]);
 
   \\(u|U[0-9abcdefABCDEF]{4})[0-9abcdefABCDEF]{4} {
     int c = convert_ucn_to_byte (yytext);
 
   /* \\[\"\'?\\] would be shorter, but it confuses xgettext.  */
   \\("\""|"'"|"?"|"\\")  obstack_1grow (&obstack_for_string, yytext[1]);
 
   \\(u|U[0-9abcdefABCDEF]{4})[0-9abcdefABCDEF]{4} {
     int c = convert_ucn_to_byte (yytext);
-    if (c < 0)
-      complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
-    else 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);
   }
     else
       obstack_1grow (&obstack_for_string, c);
   }
-  \\(.|\n)     {
-    complain_at (*loc, _("unrecognized escape sequence: %s"), quote (yytext));
-    STRING_GROW;
+  \\(.|\n)      {
+    char const *p = yytext + 1;
+    /* Quote only if escaping won't make the character visible.  */
+    if (isspace ((unsigned char) *p) && isprint ((unsigned char) *p))
+      p = quote (p);
+    else
+      p = quotearg_style_mem (escape_quoting_style, p, 1);
+    complain_at (*loc, _("invalid character after \\-escape: %s"), p);
   }
 }
 
   }
 }
 
@@ -627,21 +647,21 @@ splice     (\\[ \f\t\v]*\n)*
 
 <SC_CHARACTER,SC_STRING>
 {
 
 <SC_CHARACTER,SC_STRING>
 {
-  {splice}|\\{splice}[^\n\[\]] STRING_GROW;
+  {splice}|\\{splice}[^\n\[\]]  STRING_GROW;
 }
 
 <SC_CHARACTER>
 {
 }
 
 <SC_CHARACTER>
 {
-  "'"          STRING_GROW; BEGIN context_state;
-  \n           unexpected_newline (token_start, "'"); BEGIN context_state;
-  <<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;
 }
 
 <SC_STRING>
 {
 }
 
 <SC_STRING>
 {
-  "\""         STRING_GROW; BEGIN context_state;
-  \n           unexpected_newline (token_start, "\""); BEGIN context_state;
-  <<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;
 }
 
 
 }
 
 
@@ -649,7 +669,7 @@ splice       (\\[ \f\t\v]*\n)*
   | Strings, comments etc. can be found in user code.  |
   `---------------------------------------------------*/
 
   | Strings, comments etc. can be found in user code.  |
   `---------------------------------------------------*/
 
-<SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
+<SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE,SC_PREDICATE>
 {
   "'" {
     STRING_GROW;
 {
   "'" {
     STRING_GROW;
@@ -679,42 +699,63 @@ splice     (\\[ \f\t\v]*\n)*
 
 
   /*-----------------------------------------------------------.
 
 
   /*-----------------------------------------------------------.
-  | Scanning some code in braces (actions). The initial "{" is |
-  | already eaten.                                             |
+  | Scanning some code in braces (actions, predicates). The    |
+  | initial "{" is already eaten.                              |
   `-----------------------------------------------------------*/
 
   `-----------------------------------------------------------*/
 
-<SC_BRACED_CODE>
+<SC_BRACED_CODE,SC_PREDICATE>
 {
   "{"|"<"{splice}"%"  STRING_GROW; nesting++;
   "%"{splice}">"      STRING_GROW; nesting--;
 {
   "{"|"<"{splice}"%"  STRING_GROW; nesting++;
   "%"{splice}">"      STRING_GROW; nesting--;
-  "}" {
-    obstack_1grow (&obstack_for_string, '}');
 
 
-    --nesting;
-    if (nesting < 0)
-      {
-       STRING_FINISH;
-       loc->start = code_start;
-       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>> {
   "<"{splice}"<"  STRING_GROW;
 
   <<EOF>> {
+    int token = (YY_START == SC_BRACED_CODE) ? BRACED_CODE : BRACED_PREDICATE;
     unexpected_eof (code_start, "}");
     STRING_FINISH;
     loc->start = code_start;
     val->code = last_string;
     BEGIN INITIAL;
     unexpected_eof (code_start, "}");
     STRING_FINISH;
     loc->start = code_start;
     val->code = last_string;
     BEGIN INITIAL;
-    return BRACED_CODE;
+    return token;
+  }
+}
+
+<SC_BRACED_CODE>
+{
+  "}" {
+    obstack_1grow (&obstack_for_string, '}');
+
+    --nesting;
+    if (nesting < 0)
+      {
+        STRING_FINISH;
+        loc->start = code_start;
+        val->code = last_string;
+        BEGIN INITIAL;
+        return BRACED_CODE;
+      }
   }
 }
 
   }
 }
 
+<SC_PREDICATE>
+{
+  "}" {
+    --nesting;
+    if (nesting < 0)
+      {
+        STRING_FINISH;
+        loc->start = code_start;
+        val->code = last_string;
+        BEGIN INITIAL;
+        return BRACED_PREDICATE;
+      }
+    else
+      obstack_1grow (&obstack_for_string, '}');
+  }
+}
 
   /*--------------------------------------------------------------.
   | Scanning some prologue: from "%{" (already scanned) to "%}".  |
 
   /*--------------------------------------------------------------.
   | Scanning some prologue: from "%{" (already scanned) to "%}".  |
@@ -762,8 +803,8 @@ splice       (\\[ \f\t\v]*\n)*
   | By default, grow the string obstack with the input.  |
   `-----------------------------------------------------*/
 
   | 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;
+<SC_COMMENT,SC_LINE_COMMENT,SC_BRACED_CODE,SC_PREDICATE,SC_PROLOGUE,SC_EPILOGUE,SC_STRING,SC_CHARACTER,SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>. |
+  <SC_COMMENT,SC_LINE_COMMENT,SC_BRACED_CODE,SC_PREDICATE,SC_PROLOGUE,SC_EPILOGUE>\n    STRING_GROW;
 
 %%
 
 
 %%
 
@@ -779,35 +820,35 @@ no_cr_read (FILE *fp, char *buf, size_t size)
     {
       char *w = memchr (buf, '\r', bytes_read);
       if (w)
     {
       char *w = memchr (buf, '\r', bytes_read);
       if (w)
-       {
-         char const *r = ++w;
-         char const *lim = buf + bytes_read;
-
-         for (;;)
-           {
-             /* Found an '\r'.  Treat it like '\n', but ignore any
-                '\n' that immediately follows.  */
-             w[-1] = '\n';
-             if (r == lim)
-               {
-                 int ch = getc (fp);
-                 if (ch != '\n' && ungetc (ch, fp) != ch)
-                   break;
-               }
-             else if (*r == '\n')
-               r++;
-
-             /* Copy until the next '\r'.  */
-             do
-               {
-                 if (r == lim)
-                   return w - buf;
-               }
-             while ((*w++ = *r++) != '\r');
-           }
-
-         return w - buf;
-       }
+        {
+          char const *r = ++w;
+          char const *lim = buf + bytes_read;
+
+          for (;;)
+            {
+              /* Found an '\r'.  Treat it like '\n', but ignore any
+                 '\n' that immediately follows.  */
+              w[-1] = '\n';
+              if (r == lim)
+                {
+                  int ch = getc (fp);
+                  if (ch != '\n' && ungetc (ch, fp) != ch)
+                    break;
+                }
+              else if (*r == '\n')
+                r++;
+
+              /* Copy until the next '\r'.  */
+              do
+                {
+                  if (r == lim)
+                    return w - buf;
+                }
+              while ((*w++ = *r++) != '\r');
+            }
+
+          return w - buf;
+        }
     }
 
   return bytes_read;
     }
 
   return bytes_read;
@@ -838,7 +879,7 @@ scan_integer (char const *number, int base, location loc)
 /*------------------------------------------------------------------.
 | 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  |
-| to a single-byte character.                                      |
+| to a single-byte character.                                       |
 `------------------------------------------------------------------*/
 
 static int
 `------------------------------------------------------------------*/
 
 static int
@@ -866,22 +907,22 @@ convert_ucn_to_byte (char const *ucn)
        about.  */
     static signed char const table[] =
       {
        about.  */
     static signed char const table[] =
       {
-       '\0',   -1,   -1,   -1,   -1,   -1,   -1, '\a',
-       '\b', '\t', '\n', '\v', '\f', '\r',   -1,   -1,
-         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        ' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'',
-        '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
-        '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
-        '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
-        '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
-        'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
-        'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
-        'X',  'Y',  'Z',  '[', '\\',  ']',  '^',  '_',
-        '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
-        'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
-        'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
-        'x',  'y',  'z',  '{',  '|',  '}',  '~'
+        '\0',   -1,   -1,   -1,   -1,   -1,   -1, '\a',
+        '\b', '\t', '\n', '\v', '\f', '\r',   -1,   -1,
+          -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+          -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+         ' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'',
+         '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+         '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+         '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+         '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
+         'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
+         'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
+         'X',  'Y',  'Z',  '[', '\\',  ']',  '^',  '_',
+         '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+         'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+         'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+         'x',  'y',  'z',  '{',  '|',  '}',  '~'
       };
 
     code = code < sizeof table ? table[code] : -1;
       };
 
     code = code < sizeof table ? table[code] : -1;
@@ -893,7 +934,7 @@ convert_ucn_to_byte (char const *ucn)
 
 
 /*----------------------------------------------------------------.
 
 
 /*----------------------------------------------------------------.
-| Handle `#line INT "FILE"'.  ARGS has already skipped `#line '.  |
+| Handle '#line INT "FILE"'.  ARGS has already skipped '#line '.  |
 `----------------------------------------------------------------*/
 
 static void
 `----------------------------------------------------------------*/
 
 static void
@@ -901,8 +942,8 @@ handle_syncline (char *args, location loc)
 {
   char *after_num;
   unsigned long int lineno = strtoul (args, &after_num, 10);
 {
   char *after_num;
   unsigned long int lineno = strtoul (args, &after_num, 10);
-  char *file = strchr (after_num, '"') + 1;
-  *strchr (file, '"') = '\0';
+  char *file = mbschr (after_num, '"') + 1;
+  *mbschr (file, '"') = '\0';
   if (INT_MAX <= lineno)
     {
       warn_at (loc, _("line number overflow"));
   if (INT_MAX <= lineno)
     {
       warn_at (loc, _("line number overflow"));
@@ -915,8 +956,8 @@ handle_syncline (char *args, location loc)
 
 /*----------------------------------------------------------------.
 | For a token or comment starting at START, report message MSGID, |
 
 /*----------------------------------------------------------------.
 | 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.                                        |
+| which should say that an end marker was found before            |
+| the expected TOKEN_END.                                         |
 `----------------------------------------------------------------*/
 
 static void
 `----------------------------------------------------------------*/
 
 static void
@@ -925,6 +966,10 @@ unexpected_end (boundary start, char const *msgid, char const *token_end)
   location loc;
   loc.start = start;
   loc.end = scanner_cursor;
   location loc;
   loc.start = start;
   loc.end = scanner_cursor;
+  token_end = quote (token_end);
+  // Instead of '\'', display "'".
+  if (STREQ (token_end, "'\\''", '\'', '\\', '\'', '\'', 0,0,0,0,0))
+    token_end = "\"'\"";
   complain_at (loc, _(msgid), token_end);
 }
 
   complain_at (loc, _(msgid), token_end);
 }
 
@@ -937,7 +982,7 @@ unexpected_end (boundary start, char const *msgid, char const *token_end)
 static void
 unexpected_eof (boundary start, 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);
 }
 
 
 }
 
 
@@ -948,7 +993,7 @@ unexpected_eof (boundary start, char const *token_end)
 static void
 unexpected_newline (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);
 }
 
 
 }