scanner: don't crash on strings containing a NUL byte
[bison.git] / src / scan-gram.l
blobad2904ce84a192414a050e8e1d512b07545bd527
1 /* Bison Grammar Scanner                             -*- C -*-
3    Copyright (C) 2002-2015, 2018-2020 Free Software Foundation, Inc.
5    This file is part of Bison, the GNU Compiler Compiler.
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20 %option debug nodefault noinput noyywrap never-interactive
21 %option prefix="gram_" outfile="lex.yy.c"
24 #include <errno.h>
26 #include <c-ctype.h>
27 #include <mbswidth.h>
28 #include <quote.h>
29 #include <quotearg.h>
31 #include "src/complain.h"
32 #include "src/files.h"
33 #include "src/getargs.h"
34 #include "src/gram.h"
35 #include "src/reader.h"
36 #include "src/scan-gram.h"
37 #include "src/uniqstr.h"
39 #define FLEX_PREFIX(Id) gram_ ## Id
40 #include "src/flex-scanner.h"
42 /* Work around a bug in flex 2.5.31.  See Debian bug 333231
43    <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>.  */
44 #undef gram_wrap
45 #define gram_wrap() 1
47 #define YY_DECL GRAM_LEX_DECL
49 /* Location of scanner cursor.  */
50 static boundary scanner_cursor;
52 #define YY_USER_ACTION  location_compute (loc, &scanner_cursor, yytext, yyleng);
54 /* Report that yytext is an extension, and evaluate to its token kind.  */
55 #define BISON_DIRECTIVE(Directive)                              \
56   (bison_directive (loc, yytext), PERCENT_ ## Directive)
58 #define RETURN_PERCENT_PARAM(Value)             \
59   RETURN_VALUE(PERCENT_PARAM, param_ ## Value)
61 #define RETURN_PERCENT_FLAG(Value)                      \
62   RETURN_VALUE(PERCENT_FLAG, uniqstr_new (Value))
64 #define RETURN_VALUE(Token, Value)              \
65   do {                                          \
66     val->Token = Value;                         \
67     return Token;                               \
68   } while (0)
70 #define ROLLBACK_CURRENT_TOKEN                                  \
71   do {                                                          \
72     scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);     \
73     scanner_cursor.byte -= yyleng;                              \
74     yyless (0);                                                 \
75   } while (0)
77 #define DEPRECATED_DIRECTIVE(Msg)                               \
78   do {                                                          \
79     deprecated_directive (loc, yytext, Msg);                    \
80     scanner_cursor.column -= mbsnwidth (Msg, strlen (Msg), 0);  \
81     scanner_cursor.byte -= strlen (Msg);                        \
82     for (size_t i = strlen (Msg); i != 0; --i)                  \
83       unput (Msg[i - 1]);                                       \
84   } while (0)
87 #define STRING_GROW_ESCAPE(Char)                                \
88   do {                                                          \
89     verify (UCHAR_MAX < ULONG_MAX);                             \
90     long c = Char;                                              \
91     bool valid = 0 < c && c <= UCHAR_MAX;                       \
92     if (!valid)                                                 \
93       complain (loc, complaint,                                 \
94                 _("invalid number after \\-escape: %s"),        \
95                 yytext + 1);                                    \
96     if (YY_START == SC_ESCAPED_CHARACTER)                       \
97       STRING_1GROW (valid ? c : '?');                           \
98     else                                                        \
99       STRING_GROW ();                                           \
100   } while (0)
103 /* The current file name.  Might change with #line.  */
104 static uniqstr current_file = NULL;
106 /* A string representing the most recently saved token.  */
107 static char *last_string = NULL;
109 /* Bracketed identifier. */
110 static uniqstr bracketed_id_str = NULL;
111 static location bracketed_id_loc;
112 static boundary bracketed_id_start;
113 static int bracketed_id_context_state = 0;
115 void
116 gram_scanner_last_string_free (void)
118   STRING_FREE ();
121 static void handle_syncline (char *, location);
122 static int scan_integer (char const *p, int base, location loc);
123 static int convert_ucn_to_byte (char const *hex_text);
124 static void unexpected_eof (boundary, char const *);
125 static void unexpected_newline (boundary, char const *);
128  /* A C-like comment in directives/rules. */
129 %x SC_YACC_COMMENT
130  /* Characters and strings in directives/rules. */
131 %x SC_ESCAPED_CHARACTER SC_ESCAPED_STRING SC_ESCAPED_TSTRING
132  /* A identifier was just read in directives/rules.  Special state
133     to capture the sequence 'identifier :'. */
134 %x SC_AFTER_IDENTIFIER
136  /* POSIX says that a tag must be both an id and a C union member, but
137     historically almost any character is allowed in a tag.  We
138     disallow NUL, as this simplifies our implementation.  We match
139     angle brackets in nested pairs: several languages use them for
140     generics/template types.  */
141 %x SC_TAG
143  /* Four types of user code:
144     - prologue (code between '%{' '%}' in the first section, before %%);
145     - actions, printers, union, etc, (between braced in the middle section);
146     - epilogue (everything after the second %%).
147     - predicate (code between '%?{' and '{' in middle section); */
148 %x SC_PROLOGUE SC_BRACED_CODE SC_EPILOGUE SC_PREDICATE
149  /* C and C++ comments in code. */
150 %x SC_COMMENT SC_LINE_COMMENT
151  /* Strings and characters in code. */
152 %x SC_STRING SC_CHARACTER
153  /* Bracketed identifiers support. */
154 %x SC_BRACKETED_ID SC_RETURN_BRACKETED_ID
156 letter    [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
157 id        {letter}({letter}|[-0-9])*
158 int       [0-9]+
159 xint      0[xX][0-9abcdefABCDEF]+
161 eol       \n|\r\n
163  /* UTF-8 Encoded Unicode Code Point, from Flex's documentation. */
164 mbchar    [\x09\x0A\x0D\x20-\x7E]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF]([\x80-\xBF]{2})|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x\90-\xBF]([\x80-\xBF]{2})|[\xF1-\xF3]([\x80-\xBF]{3})|\xF4[\x80-\x8F]([\x80-\xBF]{2})
166 /* Zero or more instances of backslash-newline.  Following GCC, allow
167    white space between the backslash and the newline.  */
168 splice   (\\[ \f\t\v]*{eol})*
170 /* An equal sign, with optional leading whitespaces. This is used in some
171    deprecated constructs. */
172 sp       [[:space:]]*
173 eqopt    ({sp}=)?
177   /* Nesting level.  Either for nested braces, or nested angle brackets
178      (but not mixed).  */
179   int nesting PACIFY_CC (= 0);
181   /* Parent context state, when applicable.  */
182   int context_state PACIFY_CC (= 0);
184   /* Location of most recent identifier, when applicable.  */
185   location id_loc PACIFY_CC (= empty_loc);
187   /* Where containing code started, when applicable.  Its initial
188      value is relevant only when yylex is invoked in the SC_EPILOGUE
189      start condition.  */
190   boundary code_start = scanner_cursor;
192   /* Where containing comment or string or character literal started,
193      when applicable.  */
194   boundary token_start PACIFY_CC (= scanner_cursor);
196   /* We cannot trust YY_USER_INIT, whose semantics changes over time
197      (it moved in Flex 2.5.38).  */
198   static bool first = true;
199   if (first)
200     {
201       scanner_cursor = loc->start;
202       first = false;
203     }
207   /*-----------------------.
208   | Scanning white space.  |
209   `-----------------------*/
211 <INITIAL,SC_AFTER_IDENTIFIER,SC_BRACKETED_ID,SC_RETURN_BRACKETED_ID>
213   /* Comments and white space.  */
214   "," {
215      complain (loc, Wother, _("stray ',' treated as white space"));
216   }
217   [ \f\t\v\r]|{eol}  |
218   "//".*       continue;
219   "/*" {
220     token_start = loc->start;
221     context_state = YY_START;
222     BEGIN SC_YACC_COMMENT;
223   }
225   ^"#line "{int}(" \"".*"\"")?{eol} {
226     handle_syncline (yytext + sizeof "#line " - 1, *loc);
227   }
231   /*----------------------------.
232   | Scanning Bison directives.  |
233   `----------------------------*/
235   /* For directives that are also command line options, the regex must be
236         "%..."
237      after "[-_]"s are removed, and the directive must match the --long
238      option name, with a single string argument.  Otherwise, add exceptions
239      to ../build-aux/cross-options.pl.  */
241 <INITIAL>
243   "%binary"                         return BISON_DIRECTIVE (NONASSOC);
244   "%code"                           return BISON_DIRECTIVE (CODE);
245   "%debug"                          RETURN_PERCENT_FLAG ("parse.trace");
246   "%default-prec"                   return BISON_DIRECTIVE (DEFAULT_PREC);
247   "%define"                         return BISON_DIRECTIVE (DEFINE);
248   "%defines"                        return BISON_DIRECTIVE (DEFINES);
249   "%destructor"                     return BISON_DIRECTIVE (DESTRUCTOR);
250   "%dprec"                          return BISON_DIRECTIVE (DPREC);
251   "%empty"                          return BISON_DIRECTIVE (EMPTY);
252   "%expect"                         return BISON_DIRECTIVE (EXPECT);
253   "%expect-rr"                      return BISON_DIRECTIVE (EXPECT_RR);
254   "%file-prefix"                    RETURN_VALUE (PERCENT_FILE_PREFIX, uniqstr_new (yytext));
255   "%initial-action"                 return BISON_DIRECTIVE (INITIAL_ACTION);
256   "%glr-parser"                     return BISON_DIRECTIVE (GLR_PARSER);
257   "%language"                       return BISON_DIRECTIVE (LANGUAGE);
258   "%left"                           return PERCENT_LEFT;
259   "%lex-param"                      RETURN_PERCENT_PARAM (lex);
260   "%locations"                      RETURN_PERCENT_FLAG ("locations");
261   "%merge"                          return BISON_DIRECTIVE (MERGE);
262   "%no-default-prec"                return BISON_DIRECTIVE (NO_DEFAULT_PREC);
263   "%no-lines"                       return BISON_DIRECTIVE (NO_LINES);
264   "%nonassoc"                       return PERCENT_NONASSOC;
265   "%nondeterministic-parser"        return BISON_DIRECTIVE (NONDETERMINISTIC_PARSER);
266   "%nterm"                          return BISON_DIRECTIVE (NTERM);
267   "%output"                         return BISON_DIRECTIVE (OUTPUT);
268   "%param"                          RETURN_PERCENT_PARAM (both);
269   "%parse-param"                    RETURN_PERCENT_PARAM (parse);
270   "%prec"                           return PERCENT_PREC;
271   "%precedence"                     return BISON_DIRECTIVE (PRECEDENCE);
272   "%printer"                        return BISON_DIRECTIVE (PRINTER);
273   "%require"                        return BISON_DIRECTIVE (REQUIRE);
274   "%right"                          return PERCENT_RIGHT;
275   "%skeleton"                       return BISON_DIRECTIVE (SKELETON);
276   "%start"                          return PERCENT_START;
277   "%term"                           return BISON_DIRECTIVE (TOKEN);
278   "%token"                          return PERCENT_TOKEN;
279   "%token-table"                    return BISON_DIRECTIVE (TOKEN_TABLE);
280   "%type"                           return PERCENT_TYPE;
281   "%union"                          return PERCENT_UNION;
282   "%verbose"                        return BISON_DIRECTIVE (VERBOSE);
283   "%yacc"                           return PERCENT_YACC;
285   /* Deprecated since Bison 2.3b (2008-05-27), but the warning is
286      issued only since Bison 3.4. */
287   "%pure"[-_]"parser"               RETURN_VALUE (PERCENT_PURE_PARSER, uniqstr_new (yytext));
289   /* Deprecated since Bison 3.0 (2013-07-25), but the warning is
290      issued only since Bison 3.3. */
291   "%error-verbose"                  RETURN_VALUE (PERCENT_ERROR_VERBOSE, uniqstr_new (yytext));
293   /* Deprecated since Bison 2.6 (2012-07-19), but the warning is
294      issued only since Bison 3.3. */
295   "%name"[-_]"prefix"{eqopt}{sp}    RETURN_VALUE (PERCENT_NAME_PREFIX, uniqstr_new (yytext));
297   /* Deprecated since Bison 2.7.90, 2012. */
298   "%default"[-_]"prec"              DEPRECATED_DIRECTIVE ("%default-prec");
299   "%error"[-_]"verbose"             RETURN_VALUE (PERCENT_ERROR_VERBOSE, uniqstr_new (yytext));
300   "%expect"[-_]"rr"                 DEPRECATED_DIRECTIVE ("%expect-rr");
301   "%file-prefix"{eqopt}             RETURN_VALUE (PERCENT_FILE_PREFIX, uniqstr_new (yytext));
302   "%fixed"[-_]"output"[-_]"files"   DEPRECATED_DIRECTIVE ("%output \"y.tab.c\"");
303   "%no"[-_]"default"[-_]"prec"      DEPRECATED_DIRECTIVE ("%no-default-prec");
304   "%no"[-_]"lines"                  DEPRECATED_DIRECTIVE ("%no-lines");
305   "%output"{eqopt}                  DEPRECATED_DIRECTIVE ("%output");
306   "%token"[-_]"table"               DEPRECATED_DIRECTIVE ("%token-table");
308   "%"{id} {
309     complain (loc, complaint, _("invalid directive: %s"), quote (yytext));
310     return GRAM_error;
311   }
313   ":"                     return COLON;
314   "="                     return EQUAL;
315   "|"                     return PIPE;
316   ";"                     return SEMICOLON;
318   {id} {
319     val->ID = uniqstr_new (yytext);
320     id_loc = *loc;
321     bracketed_id_str = NULL;
322     BEGIN SC_AFTER_IDENTIFIER;
323   }
325   {int}      RETURN_VALUE (INT, scan_integer (yytext, 10, *loc));
326   {xint}     RETURN_VALUE (INT, scan_integer (yytext, 16, *loc));
328   /* Identifiers may not start with a digit.  Yet, don't silently
329      accept "1FOO" as "1 FOO".  */
330   {int}{id} {
331     complain (loc, complaint, _("invalid identifier: %s"), quote (yytext));
332     return GRAM_error;
333   }
335   /* Characters.  */
336   "'"         token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
338   /* Strings. */
339   "\""        token_start = loc->start; STRING_1GROW ('"'); BEGIN SC_ESCAPED_STRING;
340   "_(\""      token_start = loc->start; STRING_1GROW ('"'); BEGIN SC_ESCAPED_TSTRING;
342   /* Prologue. */
343   "%{"        code_start = loc->start; BEGIN SC_PROLOGUE;
345   /* Code in between braces.  */
346   "{" {
347     STRING_GROW ();
348     nesting = 0;
349     code_start = loc->start;
350     BEGIN SC_BRACED_CODE;
351   }
353   /* Semantic predicate. */
354   "%?"([ \f\t\v]|{eol})*"{" {
355     nesting = 0;
356     code_start = loc->start;
357     BEGIN SC_PREDICATE;
358   }
360   /* A type. */
361   "<*>"       return TAG_ANY;
362   "<>"        return TAG_NONE;
363   "<"         {
364     nesting = 0;
365     token_start = loc->start;
366     BEGIN SC_TAG;
367   }
369   "%%" {
370     static int percent_percent_count;
371     if (++percent_percent_count == 2)
372       BEGIN SC_EPILOGUE;
373     return PERCENT_PERCENT;
374   }
376   "[" {
377     bracketed_id_str = NULL;
378     bracketed_id_start = loc->start;
379     bracketed_id_context_state = YY_START;
380     BEGIN SC_BRACKETED_ID;
381   }
383   [^\[%A-Za-z0-9_<>{}""''*;|=/, \f\r\n\t\v]+|. {
384     complain (loc, complaint, "%s: %s",
385               ngettext ("invalid character", "invalid characters", yyleng),
386               quote_mem (yytext, yyleng));
387     return GRAM_error;
388   }
390   <<EOF>> {
391     loc->start = loc->end = scanner_cursor;
392     yyterminate ();
393   }
397   /*--------------------------------------------------------------.
398   | Supporting \0 complexifies our implementation for no expected |
399   | added value.                                                  |
400   `--------------------------------------------------------------*/
402 <SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING,SC_ESCAPED_TSTRING,SC_TAG>
404   \0         {
405     complain (loc, complaint, _("invalid null character"));
406     STRING_FINISH ();
407     STRING_FREE ();
408     return GRAM_error;
409   }
413   /*-----------------------------------------------------------------.
414   | Scanning after an identifier, checking whether a colon is next.  |
415   `-----------------------------------------------------------------*/
417 <SC_AFTER_IDENTIFIER>
419   "[" {
420     if (bracketed_id_str)
421       {
422         ROLLBACK_CURRENT_TOKEN;
423         BEGIN SC_RETURN_BRACKETED_ID;
424         *loc = id_loc;
425         return ID;
426       }
427     else
428       {
429         bracketed_id_start = loc->start;
430         bracketed_id_context_state = YY_START;
431         BEGIN SC_BRACKETED_ID;
432       }
433   }
434   ":" {
435     ROLLBACK_CURRENT_TOKEN;
436     BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
437     *loc = id_loc;
438     return ID_COLON;
439   }
440   . {
441     ROLLBACK_CURRENT_TOKEN;
442     BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
443     *loc = id_loc;
444     return ID;
445   }
446   <<EOF>> {
447     BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
448     *loc = id_loc;
449     return ID;
450   }
453   /*--------------------------------.
454   | Scanning bracketed identifiers. |
455   `--------------------------------*/
457 <SC_BRACKETED_ID>
459   {id} {
460     if (bracketed_id_str)
461       {
462         complain (loc, complaint,
463                   _("unexpected identifier in bracketed name: %s"),
464                   quote (yytext));
465         return GRAM_error;
466       }
467     else
468       {
469         bracketed_id_str = uniqstr_new (yytext);
470         bracketed_id_loc = *loc;
471       }
472   }
473   "]" {
474     BEGIN bracketed_id_context_state;
475     if (bracketed_id_str)
476       {
477         if (INITIAL == bracketed_id_context_state)
478           {
479             val->BRACKETED_ID = bracketed_id_str;
480             bracketed_id_str = 0;
481             *loc = bracketed_id_loc;
482             return BRACKETED_ID;
483           }
484       }
485     else
486       {
487         complain (loc, complaint, _("an identifier expected"));
488         return GRAM_error;
489       }
490   }
492   [^\].A-Za-z0-9_/ \f\r\n\t\v]+|. {
493     complain (loc, complaint, "%s: %s",
494               ngettext ("invalid character in bracketed name",
495                         "invalid characters in bracketed name", yyleng),
496               quote_mem (yytext, yyleng));
497     return GRAM_error;
498   }
500   <<EOF>> {
501     BEGIN bracketed_id_context_state;
502     unexpected_eof (bracketed_id_start, "]");
503   }
506 <SC_RETURN_BRACKETED_ID>
508   . {
509     ROLLBACK_CURRENT_TOKEN;
510     val->BRACKETED_ID = bracketed_id_str;
511     bracketed_id_str = 0;
512     *loc = bracketed_id_loc;
513     BEGIN INITIAL;
514     return BRACKETED_ID;
515   }
519   /*---------------------------------------------------------------.
520   | Scanning a Yacc comment.  The initial '/ *' is already eaten.  |
521   `---------------------------------------------------------------*/
523 <SC_YACC_COMMENT>
525   "*/"     BEGIN context_state;
526   .|{eol}  continue;
527   <<EOF>>  unexpected_eof (token_start, "*/"); BEGIN context_state;
531   /*------------------------------------------------------------.
532   | Scanning a C comment.  The initial '/ *' is already eaten.  |
533   `------------------------------------------------------------*/
535 <SC_COMMENT>
537   "*"{splice}"/"  STRING_GROW (); BEGIN context_state;
538   <<EOF>>         unexpected_eof (token_start, "*/"); BEGIN context_state;
542   /*--------------------------------------------------------------.
543   | Scanning a line comment.  The initial '//' is already eaten.  |
544   `--------------------------------------------------------------*/
546 <SC_LINE_COMMENT>
548   {eol}          STRING_GROW (); BEGIN context_state;
549   {splice}       STRING_GROW ();
550   <<EOF>>        BEGIN context_state;
554   /*------------------------------------------------.
555   | Scanning a Bison string, including its escapes. |
556   | The initial quote is already eaten.             |
557   `------------------------------------------------*/
559 <SC_ESCAPED_STRING>
561   "\"" {
562     STRING_1GROW ('"');
563     STRING_FINISH ();
564     BEGIN INITIAL;
565     loc->start = token_start;
566     complain (loc, Wyacc,
567               _("POSIX Yacc does not support string literals"));
568     RETURN_VALUE (STRING, last_string);
569   }
572 <SC_ESCAPED_TSTRING>
574   "\")" {
575     STRING_1GROW ('"');
576     STRING_FINISH ();
577     BEGIN INITIAL;
578     loc->start = token_start;
579     complain (loc, Wyacc,
580               _("POSIX Yacc does not support string literals"));
581     RETURN_VALUE (TSTRING, last_string);
582   }
585 <SC_ESCAPED_STRING,SC_ESCAPED_TSTRING>
587   <<EOF>>   unexpected_eof (token_start, "\"");
588   "\n"      unexpected_newline (token_start, "\"");
592   /*----------------------------------------------------------.
593   | Scanning a Bison character literal, decoding its escapes. |
594   | The initial quote is already eaten.                       |
595   `----------------------------------------------------------*/
597 <SC_ESCAPED_CHARACTER>
599   "'" {
600     STRING_FINISH ();
601     BEGIN INITIAL;
602     loc->start = token_start;
604     if (last_string[0] == '\0')
605       {
606         complain (loc, complaint, _("empty character literal"));
607         STRING_FREE ();
608         return GRAM_error;
609       }
610     else if (last_string[1] != '\0')
611       {
612         complain (loc, complaint, _("extra characters in character literal"));
613         STRING_FREE ();
614         return GRAM_error;
615       }
616     else
617       {
618         val->CHAR = last_string[0];
619         STRING_FREE ();
620         return CHAR;
621       }
622   }
623   {eol}     unexpected_newline (token_start, "'");
624   <<EOF>>   unexpected_eof (token_start, "'");
629   /*--------------------------------------------------------------.
630   | Scanning a tag.  The initial angle bracket is already eaten.  |
631   `--------------------------------------------------------------*/
633 <SC_TAG>
635   ">" {
636     --nesting;
637     if (nesting < 0)
638       {
639         STRING_FINISH ();
640         loc->start = token_start;
641         val->TAG = uniqstr_new (last_string);
642         STRING_FREE ();
643         BEGIN INITIAL;
644         return TAG;
645       }
646     STRING_GROW ();
647   }
649   ([^<>]|->)+ STRING_GROW ();
650   "<"+   STRING_GROW (); nesting += yyleng;
652   <<EOF>>   unexpected_eof (token_start, ">");
655   /*----------------------------.
656   | Decode escaped characters.  |
657   `----------------------------*/
659 <SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING,SC_ESCAPED_TSTRING>
661   \\[0-7]{1,3} {
662     STRING_GROW_ESCAPE (strtol (yytext + 1, NULL, 8));
663   }
665   \\x[0-9abcdefABCDEF]+ {
666     STRING_GROW_ESCAPE (strtol (yytext + 2, NULL, 16));
667   }
669   \\a   STRING_GROW_ESCAPE ('\a');
670   \\b   STRING_GROW_ESCAPE ('\b');
671   \\f   STRING_GROW_ESCAPE ('\f');
672   \\n   STRING_GROW_ESCAPE ('\n');
673   \\r   STRING_GROW_ESCAPE ('\r');
674   \\t   STRING_GROW_ESCAPE ('\t');
675   \\v   STRING_GROW_ESCAPE ('\v');
677   /* \\[\"\'?\\] would be shorter, but it confuses xgettext.  */
678   \\("\""|"'"|"?"|"\\")  STRING_GROW_ESCAPE (yytext[1]);
680   \\(u|U[0-9abcdefABCDEF]{4})[0-9abcdefABCDEF]{4} {
681     STRING_GROW_ESCAPE (convert_ucn_to_byte (yytext));
682   }
684   \\(.|{eol})      {
685     char const *p = yytext + 1;
686     /* Quote only if escaping won't make the character visible.  */
687     if (c_isspace ((unsigned char) *p) && c_isprint ((unsigned char) *p))
688       p = quote (p);
689     else
690       p = quotearg_style_mem (escape_quoting_style, p, 1);
691     complain (loc, complaint, _("invalid character after \\-escape: %s"),
692               p);
693     STRING_1GROW ('?');
694   }
697   /*--------------------------------------------.
698   | Scanning user-code characters and strings.  |
699   `--------------------------------------------*/
701 <SC_CHARACTER,SC_STRING>
703   {splice}|\\{splice}[^\n\[\]]  STRING_GROW ();
706 <SC_CHARACTER>
708   "'"           STRING_GROW (); BEGIN context_state;
709   {eol}         unexpected_newline (token_start, "'");
710   <<EOF>>       unexpected_eof (token_start, "'");
713 <SC_STRING>
715   "\""          STRING_GROW (); BEGIN context_state;
716   {eol}         unexpected_newline (token_start, "\"");
717   <<EOF>>       unexpected_eof (token_start, "\"");
721   /*---------------------------------------------------.
722   | Strings, comments etc. can be found in user code.  |
723   `---------------------------------------------------*/
725 <SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE,SC_PREDICATE>
727   "'" {
728     STRING_GROW ();
729     context_state = YY_START;
730     token_start = loc->start;
731     BEGIN SC_CHARACTER;
732   }
733   "\"" {
734     STRING_GROW ();
735     context_state = YY_START;
736     token_start = loc->start;
737     BEGIN SC_STRING;
738   }
739   "/"{splice}"*" {
740     STRING_GROW ();
741     context_state = YY_START;
742     token_start = loc->start;
743     BEGIN SC_COMMENT;
744   }
745   "/"{splice}"/" {
746     STRING_GROW ();
747     context_state = YY_START;
748     BEGIN SC_LINE_COMMENT;
749   }
754   /*-----------------------------------------------------------.
755   | Scanning some code in braces (actions, predicates). The    |
756   | initial "{" is already eaten.                              |
757   `-----------------------------------------------------------*/
759 <SC_BRACED_CODE,SC_PREDICATE>
761   "{"|"<"{splice}"%"  STRING_GROW (); nesting++;
762   "%"{splice}">"      STRING_GROW (); nesting--;
764   /* Tokenize '<<%' correctly (as '<<' '%') rather than incorrectly
765      (as '<' '<%').  */
766   "<"{splice}"<"  STRING_GROW ();
768   <<EOF>>   unexpected_eof (code_start, "}");
771 <SC_BRACED_CODE>
773   "}" {
774     STRING_1GROW ('}');
776     --nesting;
777     if (nesting < 0)
778       {
779         STRING_FINISH ();
780         loc->start = code_start;
781         BEGIN INITIAL;
782         RETURN_VALUE (BRACED_CODE, last_string);
783       }
784   }
787 <SC_PREDICATE>
789   "}" {
790     --nesting;
791     if (nesting < 0)
792       {
793         STRING_FINISH ();
794         loc->start = code_start;
795         BEGIN INITIAL;
796         RETURN_VALUE (BRACED_PREDICATE, last_string);
797       }
798     else
799       STRING_1GROW ('}');
800   }
803   /*--------------------------------------------------------------.
804   | Scanning some prologue: from "%{" (already scanned) to "%}".  |
805   `--------------------------------------------------------------*/
807 <SC_PROLOGUE>
809   "%}" {
810     STRING_FINISH ();
811     loc->start = code_start;
812     BEGIN INITIAL;
813     RETURN_VALUE (PROLOGUE, last_string);
814   }
816   <<EOF>>   unexpected_eof (code_start, "%}");
820   /*---------------------------------------------------------------.
821   | Scanning the epilogue (everything after the second "%%", which |
822   | has already been eaten).                                       |
823   `---------------------------------------------------------------*/
825 <SC_EPILOGUE>
827   <<EOF>> {
828     STRING_FINISH ();
829     loc->start = code_start;
830     BEGIN INITIAL;
831     RETURN_VALUE (EPILOGUE, last_string);
832   }
836   /*-----------------------------------------------------.
837   | By default, grow the string obstack with the input.  |
838   `-----------------------------------------------------*/
840 <SC_COMMENT,SC_LINE_COMMENT,SC_BRACED_CODE,SC_PREDICATE,SC_PROLOGUE,SC_EPILOGUE,SC_STRING,SC_CHARACTER,SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING,SC_ESCAPED_TSTRING>
842   /* Accept multibyte characters in one block instead of byte after
843      byte, so that add_column_width and mbsnwidth can compute correct
844      screen width.
846      Add a fallthrough "|." so that non UTF-8 input is still accepted
847      and does not jam the scanner.  */
848   {mbchar}|.   STRING_GROW ();
854 /*------------------------------------------------------.
855 | Scan NUMBER for a base-BASE integer at location LOC.  |
856 `------------------------------------------------------*/
858 static int
859 scan_integer (char const *number, int base, location loc)
861   verify (INT_MAX < ULONG_MAX);
862   if (base == 16)
863     complain (&loc, Wyacc,
864               _("POSIX Yacc does not support hexadecimal literals"));
866   errno = 0;
867   long num = strtol (number, NULL, base);
869   if (! (0 <= num && num <= INT_MAX && errno == 0))
870     {
871       complain (&loc, complaint, _("integer out of range: %s"),
872                 quote (number));
873       num = INT_MAX;
874     }
876   return num;
880 /*------------------------------------------------------------------.
881 | Convert universal character name UCN to a single-byte character,  |
882 | and return that character.  Return -1 if UCN does not correspond  |
883 | to a single-byte character.                                       |
884 `------------------------------------------------------------------*/
886 static int
887 convert_ucn_to_byte (char const *ucn)
889   verify (UCHAR_MAX <= INT_MAX);
890   long code = strtol (ucn + 2, NULL, 16);
892   /* FIXME: Currently we assume Unicode-compatible unibyte characters
893      on ASCII hosts (i.e., Latin-1 on hosts with 8-bit bytes).  On
894      non-ASCII hosts we support only the portable C character set.
895      These limitations should be removed once we add support for
896      multibyte characters.  */
898   if (! (0 <= code && code <= UCHAR_MAX))
899     return -1;
901 #if ! ('$' == 0x24 && '@' == 0x40 && '`' == 0x60 && '~' == 0x7e)
902   {
903     /* A non-ASCII host.  Use CODE to index into a table of the C
904        basic execution character set, which is guaranteed to exist on
905        all Standard C platforms.  This table also includes '$', '@',
906        and '`', which are not in the basic execution character set but
907        which are unibyte characters on all the platforms that we know
908        about.  */
909     static signed char const table[] =
910       {
911         '\0',   -1,   -1,   -1,   -1,   -1,   -1, '\a',
912         '\b', '\t', '\n', '\v', '\f', '\r',   -1,   -1,
913           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
914           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
915          ' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'',
916          '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
917          '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
918          '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
919          '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
920          'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
921          'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
922          'X',  'Y',  'Z',  '[', '\\',  ']',  '^',  '_',
923          '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
924          'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
925          'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
926          'x',  'y',  'z',  '{',  '|',  '}',  '~'
927       };
929     code = code < sizeof table ? table[code] : -1;
930   }
931 #endif
933   return code;
937 /*---------------------------------------------------------------------.
938 | Handle '#line INT( "FILE")?\n'.  ARGS has already skipped '#line '.  |
939 `---------------------------------------------------------------------*/
941 static void
942 handle_syncline (char *args, location loc)
944   char *file;
945   errno = 0;
946   long lineno = strtol (args, &file, 10);
947   if (! (0 <= lineno && lineno <= INT_MAX && errno == 0))
948     {
949       complain (&loc, Wother, _("line number overflow"));
950       lineno = INT_MAX;
951     }
953   file = strchr (file, '"');
954   if (file)
955     {
956       *strchr (file + 1, '"') = '\0';
957       current_file = uniqstr_new (file + 1);
958     }
959   boundary_set (&scanner_cursor, current_file, lineno, 1, 1);
963 /*----------------------------------------------------------------.
964 | For a token or comment starting at START, report message MSGID, |
965 | which should say that an end marker was found before the        |
966 | expected TOKEN_END. Then, pretend that TOKEN_END was found.     |
967 `----------------------------------------------------------------*/
969 static void
970 unexpected_end (boundary start, char const *msgid, char const *token_end)
972   location loc;
973   loc.start = start;
974   loc.end = scanner_cursor;
975   size_t i = strlen (token_end);
977   /* Adjust scanner cursor so that any later message does not count
978      the characters about to be inserted.  */
979   scanner_cursor.column -= i;
980   scanner_cursor.byte -= i;
982   while (i != 0)
983     unput (token_end[--i]);
985   token_end = quote (token_end);
986   /* Instead of '\'', display "'".  */
987   if (STREQ (token_end, "'\\''"))
988     token_end = "\"'\"";
989   complain (&loc, complaint, msgid, token_end);
993 /*------------------------------------------------------------------------.
994 | Report an unexpected EOF in a token or comment starting at START.       |
995 | An end of file was encountered and the expected TOKEN_END was missing.  |
996 | After reporting the problem, pretend that TOKEN_END was found.          |
997 `------------------------------------------------------------------------*/
999 static void
1000 unexpected_eof (boundary start, char const *token_end)
1002   unexpected_end (start, _("missing %s at end of file"), token_end);
1006 /*----------------------------------------.
1007 | Likewise, but for unexpected newlines.  |
1008 `----------------------------------------*/
1010 static void
1011 unexpected_newline (boundary start, char const *token_end)
1013   unexpected_end (start, _("missing %s at end of line"), token_end);
1017 void
1018 gram_scanner_open (const char *gram)
1020   gram__flex_debug = trace_flag & trace_scan;
1021   gram_debug = trace_flag & trace_parse;
1022   obstack_init (&obstack_for_string);
1023   current_file = gram;
1024   gram_in = xfopen (gram, "r");
1028 void
1029 gram_scanner_close (void)
1031   xfclose (gram_in);
1032   /* Reclaim Flex's buffers.  */
1033   yylex_destroy ();
1037 void
1038 gram_scanner_free (void)
1040   obstack_free (&obstack_for_string, 0);