Home | History | Annotate | Download | only in src
      1 /* Bison Grammar Scanner                             -*- C -*-
      2 
      3    Copyright (C) 2002-2012 Free Software Foundation, Inc.
      4 
      5    This file is part of Bison, the GNU Compiler Compiler.
      6 
      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.
     11 
     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.
     16 
     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/>.  */
     19 
     20 %option debug nodefault noinput nounput noyywrap never-interactive
     21 %option prefix="gram_" outfile="lex.yy.c"
     22 
     23 %{
     24 /* Work around a bug in flex 2.5.31.  See Debian bug 333231
     25    <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>.  */
     26 #undef gram_wrap
     27 #define gram_wrap() 1
     28 
     29 #define FLEX_PREFIX(Id) gram_ ## Id
     30 #include "flex-scanner.h"
     31 
     32 #include "complain.h"
     33 #include "files.h"
     34 #include "gram.h"
     35 #include "quotearg.h"
     36 #include "reader.h"
     37 #include "uniqstr.h"
     38 
     39 #include <c-ctype.h>
     40 #include <mbswidth.h>
     41 #include <quote.h>
     42 
     43 #include "scan-gram.h"
     44 
     45 #define YY_DECL GRAM_LEX_DECL
     46 
     47 #define YY_USER_INIT					\
     48    code_start = scanner_cursor = loc->start;		\
     49 
     50 /* Location of scanner cursor.  */
     51 static boundary scanner_cursor;
     52 
     53 #define YY_USER_ACTION  location_compute (loc, &scanner_cursor, yytext, yyleng);
     54 
     55 static size_t no_cr_read (FILE *, char *, size_t);
     56 #define YY_INPUT(buf, result, size) ((result) = no_cr_read (yyin, buf, size))
     57 
     58 #define ROLLBACK_CURRENT_TOKEN                                  \
     59   do {                                                          \
     60     scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);	\
     61     yyless (0);                                                 \
     62   } while (0)
     63 
     64 /* A string representing the most recently saved token.  */
     65 static char *last_string;
     66 
     67 /* Bracketed identifier. */
     68 static uniqstr bracketed_id_str = 0;
     69 static location bracketed_id_loc;
     70 static boundary bracketed_id_start;
     71 static int bracketed_id_context_state = 0;
     72 
     73 void
     74 gram_scanner_last_string_free (void)
     75 {
     76   STRING_FREE;
     77 }
     78 
     79 static void handle_syncline (char *, location);
     80 static unsigned long int scan_integer (char const *p, int base, location loc);
     81 static int convert_ucn_to_byte (char const *hex_text);
     82 static void unexpected_eof (boundary, char const *);
     83 static void unexpected_newline (boundary, char const *);
     84 
     85 %}
     86  /* A C-like comment in directives/rules. */
     87 %x SC_YACC_COMMENT
     88  /* Strings and characters in directives/rules. */
     89 %x SC_ESCAPED_STRING SC_ESCAPED_CHARACTER
     90  /* A identifier was just read in directives/rules.  Special state
     91     to capture the sequence 'identifier :'. */
     92 %x SC_AFTER_IDENTIFIER
     93 
     94  /* Three types of user code:
     95     - prologue (code between '%{' '%}' in the first section, before %%);
     96     - actions, printers, union, etc, (between braced in the middle section);
     97     - epilogue (everything after the second %%). */
     98 %x SC_PROLOGUE SC_BRACED_CODE SC_EPILOGUE
     99  /* C and C++ comments in code. */
    100 %x SC_COMMENT SC_LINE_COMMENT
    101  /* Strings and characters in code. */
    102 %x SC_STRING SC_CHARACTER
    103  /* Bracketed identifiers support. */
    104 %x SC_BRACKETED_ID SC_RETURN_BRACKETED_ID
    105 
    106 letter	   [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
    107 notletter [^.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]{-}[%\{]
    108 id	  {letter}({letter}|[-0-9])*
    109 int	  [0-9]+
    110 
    111 /* POSIX says that a tag must be both an id and a C union member, but
    112    historically almost any character is allowed in a tag.  We disallow
    113    NUL and newline, as this simplifies our implementation.  */
    114 tag	 [^\0\n>]+
    115 
    116 /* Zero or more instances of backslash-newline.  Following GCC, allow
    117    white space between the backslash and the newline.  */
    118 splice	 (\\[ \f\t\v]*\n)*
    119 
    120 %%
    121 %{
    122   /* Nesting level of the current code in braces.  */
    123   int braces_level PACIFY_CC (= 0);
    124 
    125   /* Parent context state, when applicable.  */
    126   int context_state PACIFY_CC (= 0);
    127 
    128   /* Location of most recent identifier, when applicable.  */
    129   location id_loc PACIFY_CC (= empty_location);
    130 
    131   /* Where containing code started, when applicable.  Its initial
    132      value is relevant only when yylex is invoked in the SC_EPILOGUE
    133      start condition.  */
    134   boundary code_start = scanner_cursor;
    135 
    136   /* Where containing comment or string or character literal started,
    137      when applicable.  */
    138   boundary token_start PACIFY_CC (= scanner_cursor);
    139 %}
    140 
    141 
    142   /*-----------------------.
    143   | Scanning white space.  |
    144   `-----------------------*/
    145 
    146 <INITIAL,SC_AFTER_IDENTIFIER,SC_BRACKETED_ID,SC_RETURN_BRACKETED_ID>
    147 {
    148   /* Comments and white space.  */
    149   ","          warn_at (*loc, _("stray ',' treated as white space"));
    150   [ \f\n\t\v]  |
    151   "//".*       continue;
    152   "/*" {
    153     token_start = loc->start;
    154     context_state = YY_START;
    155     BEGIN SC_YACC_COMMENT;
    156   }
    157 
    158   /* #line directives are not documented, and may be withdrawn or
    159      modified in future versions of Bison.  */
    160   ^"#line "{int}(" \"".*"\"")?"\n" {
    161     handle_syncline (yytext + sizeof "#line " - 1, *loc);
    162   }
    163 }
    164 
    165 
    166   /*----------------------------.
    167   | Scanning Bison directives.  |
    168   `----------------------------*/
    169 
    170   /* For directives that are also command line options, the regex must be
    171 	"%..."
    172      after "[-_]"s are removed, and the directive must match the --long
    173      option name, with a single string argument.  Otherwise, add exceptions
    174      to ../build-aux/cross-options.pl.  */
    175 
    176 <INITIAL>
    177 {
    178   "%binary"                         return PERCENT_NONASSOC;
    179   "%code"                           return PERCENT_CODE;
    180   "%debug"                          return PERCENT_DEBUG;
    181   "%default"[-_]"prec"              return PERCENT_DEFAULT_PREC;
    182   "%define"                         return PERCENT_DEFINE;
    183   "%defines"                        return PERCENT_DEFINES;
    184   "%destructor"                     return PERCENT_DESTRUCTOR;
    185   "%dprec"                          return PERCENT_DPREC;
    186   "%error"[-_]"verbose"             return PERCENT_ERROR_VERBOSE;
    187   "%expect"                         return PERCENT_EXPECT;
    188   "%expect"[-_]"rr"                 return PERCENT_EXPECT_RR;
    189   "%file-prefix"                    return PERCENT_FILE_PREFIX;
    190   "%fixed"[-_]"output"[-_]"files"   return PERCENT_YACC;
    191   "%initial-action"                 return PERCENT_INITIAL_ACTION;
    192   "%glr-parser"                     return PERCENT_GLR_PARSER;
    193   "%language"                       return PERCENT_LANGUAGE;
    194   "%left"                           return PERCENT_LEFT;
    195   "%lex-param"                      return PERCENT_LEX_PARAM;
    196   "%locations"                      return PERCENT_LOCATIONS;
    197   "%merge"                          return PERCENT_MERGE;
    198   "%name"[-_]"prefix"               return PERCENT_NAME_PREFIX;
    199   "%no"[-_]"default"[-_]"prec"      return PERCENT_NO_DEFAULT_PREC;
    200   "%no"[-_]"lines"                  return PERCENT_NO_LINES;
    201   "%nonassoc"                       return PERCENT_NONASSOC;
    202   "%nondeterministic-parser"        return PERCENT_NONDETERMINISTIC_PARSER;
    203   "%nterm"                          return PERCENT_NTERM;
    204   "%output"                         return PERCENT_OUTPUT;
    205   "%parse-param"                    return PERCENT_PARSE_PARAM;
    206   "%prec"                           return PERCENT_PREC;
    207   "%printer"                        return PERCENT_PRINTER;
    208   "%pure"[-_]"parser"               return PERCENT_PURE_PARSER;
    209   "%require"                        return PERCENT_REQUIRE;
    210   "%right"                          return PERCENT_RIGHT;
    211   "%skeleton"                       return PERCENT_SKELETON;
    212   "%start"                          return PERCENT_START;
    213   "%term"                           return PERCENT_TOKEN;
    214   "%token"                          return PERCENT_TOKEN;
    215   "%token"[-_]"table"               return PERCENT_TOKEN_TABLE;
    216   "%type"                           return PERCENT_TYPE;
    217   "%union"                          return PERCENT_UNION;
    218   "%verbose"                        return PERCENT_VERBOSE;
    219   "%yacc"                           return PERCENT_YACC;
    220 
    221   "%"{id}|"%"{notletter}([[:graph:]])+ {
    222     complain_at (*loc, _("invalid directive: %s"), quote (yytext));
    223   }
    224 
    225   "="                     return EQUAL;
    226   "|"                     return PIPE;
    227   ";"                     return SEMICOLON;
    228   "<*>"                   return TYPE_TAG_ANY;
    229   "<>"                    return TYPE_TAG_NONE;
    230 
    231   {id} {
    232     val->uniqstr = uniqstr_new (yytext);
    233     id_loc = *loc;
    234     bracketed_id_str = NULL;
    235     BEGIN SC_AFTER_IDENTIFIER;
    236   }
    237 
    238   {int} {
    239     val->integer = scan_integer (yytext, 10, *loc);
    240     return INT;
    241   }
    242   0[xX][0-9abcdefABCDEF]+ {
    243     val->integer = scan_integer (yytext, 16, *loc);
    244     return INT;
    245   }
    246 
    247   /* Identifiers may not start with a digit.  Yet, don't silently
    248      accept "1FOO" as "1 FOO".  */
    249   {int}{id} {
    250     complain_at (*loc, _("invalid identifier: %s"), quote (yytext));
    251   }
    252 
    253   /* Characters.  */
    254   "'"	      token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
    255 
    256   /* Strings. */
    257   "\""	      token_start = loc->start; BEGIN SC_ESCAPED_STRING;
    258 
    259   /* Prologue. */
    260   "%{"        code_start = loc->start; BEGIN SC_PROLOGUE;
    261 
    262   /* Code in between braces.  */
    263   "{" {
    264     STRING_GROW;
    265     braces_level = 0;
    266     code_start = loc->start;
    267     BEGIN SC_BRACED_CODE;
    268   }
    269 
    270   /* A type. */
    271   "<"{tag}">" {
    272     obstack_grow (&obstack_for_string, yytext + 1, yyleng - 2);
    273     STRING_FINISH;
    274     val->uniqstr = uniqstr_new (last_string);
    275     STRING_FREE;
    276     return TYPE;
    277   }
    278 
    279   "%%" {
    280     static int percent_percent_count;
    281     if (++percent_percent_count == 2)
    282       BEGIN SC_EPILOGUE;
    283     return PERCENT_PERCENT;
    284   }
    285 
    286   "[" {
    287     bracketed_id_str = NULL;
    288     bracketed_id_start = loc->start;
    289     bracketed_id_context_state = YY_START;
    290     BEGIN SC_BRACKETED_ID;
    291   }
    292 
    293   [^\[%A-Za-z0-9_<>{}\"\'*;|=/, \f\n\t\v]+|. {
    294     complain_at (*loc, "%s: %s",
    295                  ngettext ("invalid character", "invalid characters", yyleng),
    296                  quote_mem (yytext, yyleng));
    297   }
    298 
    299   <<EOF>> {
    300     loc->start = loc->end = scanner_cursor;
    301     yyterminate ();
    302   }
    303 }
    304 
    305 
    306   /*-----------------------------------------------------------------.
    307   | Scanning after an identifier, checking whether a colon is next.  |
    308   `-----------------------------------------------------------------*/
    309 
    310 <SC_AFTER_IDENTIFIER>
    311 {
    312   "[" {
    313     if (bracketed_id_str)
    314       {
    315 	ROLLBACK_CURRENT_TOKEN;
    316 	BEGIN SC_RETURN_BRACKETED_ID;
    317 	*loc = id_loc;
    318 	return ID;
    319       }
    320     else
    321       {
    322 	bracketed_id_start = loc->start;
    323 	bracketed_id_context_state = YY_START;
    324 	BEGIN SC_BRACKETED_ID;
    325       }
    326   }
    327   ":" {
    328     BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
    329     *loc = id_loc;
    330     return ID_COLON;
    331   }
    332   . {
    333     ROLLBACK_CURRENT_TOKEN;
    334     BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
    335     *loc = id_loc;
    336     return ID;
    337   }
    338   <<EOF>> {
    339     BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
    340     *loc = id_loc;
    341     return ID;
    342   }
    343 }
    344 
    345   /*--------------------------------.
    346   | Scanning bracketed identifiers. |
    347   `--------------------------------*/
    348 
    349 <SC_BRACKETED_ID>
    350 {
    351   {id} {
    352     if (bracketed_id_str)
    353       {
    354 	complain_at (*loc, _("unexpected identifier in bracketed name: %s"),
    355 		     quote (yytext));
    356       }
    357     else
    358       {
    359 	bracketed_id_str = uniqstr_new (yytext);
    360 	bracketed_id_loc = *loc;
    361       }
    362   }
    363   "]" {
    364     BEGIN bracketed_id_context_state;
    365     if (bracketed_id_str)
    366       {
    367 	if (INITIAL == bracketed_id_context_state)
    368 	  {
    369 	    val->uniqstr = bracketed_id_str;
    370 	    bracketed_id_str = 0;
    371 	    *loc = bracketed_id_loc;
    372 	    return BRACKETED_ID;
    373 	  }
    374       }
    375     else
    376       complain_at (*loc, _("an identifier expected"));
    377   }
    378 
    379   [^\].A-Za-z0-9_/ \f\n\t\v]+|. {
    380     complain_at (*loc, "%s: %s",
    381                  ngettext ("invalid character in bracketed name",
    382                            "invalid characters in bracketed name", yyleng),
    383                  quote_mem (yytext, yyleng));
    384   }
    385 
    386   <<EOF>> {
    387     BEGIN bracketed_id_context_state;
    388     unexpected_eof (bracketed_id_start, "]");
    389   }
    390 }
    391 
    392 <SC_RETURN_BRACKETED_ID>
    393 {
    394   . {
    395     ROLLBACK_CURRENT_TOKEN;
    396     val->uniqstr = bracketed_id_str;
    397     bracketed_id_str = 0;
    398     *loc = bracketed_id_loc;
    399     BEGIN INITIAL;
    400     return BRACKETED_ID;
    401   }
    402 }
    403 
    404 
    405   /*---------------------------------------------------------------.
    406   | Scanning a Yacc comment.  The initial '/ *' is already eaten.  |
    407   `---------------------------------------------------------------*/
    408 
    409 <SC_YACC_COMMENT>
    410 {
    411   "*/"     BEGIN context_state;
    412   .|\n	   continue;
    413   <<EOF>>  unexpected_eof (token_start, "*/"); BEGIN context_state;
    414 }
    415 
    416 
    417   /*------------------------------------------------------------.
    418   | Scanning a C comment.  The initial '/ *' is already eaten.  |
    419   `------------------------------------------------------------*/
    420 
    421 <SC_COMMENT>
    422 {
    423   "*"{splice}"/"  STRING_GROW; BEGIN context_state;
    424   <<EOF>>	  unexpected_eof (token_start, "*/"); BEGIN context_state;
    425 }
    426 
    427 
    428   /*--------------------------------------------------------------.
    429   | Scanning a line comment.  The initial '//' is already eaten.  |
    430   `--------------------------------------------------------------*/
    431 
    432 <SC_LINE_COMMENT>
    433 {
    434   "\n"		 STRING_GROW; BEGIN context_state;
    435   {splice}	 STRING_GROW;
    436   <<EOF>>	 BEGIN context_state;
    437 }
    438 
    439 
    440   /*------------------------------------------------.
    441   | Scanning a Bison string, including its escapes. |
    442   | The initial quote is already eaten.             |
    443   `------------------------------------------------*/
    444 
    445 <SC_ESCAPED_STRING>
    446 {
    447   "\""|"\n" {
    448     if (yytext[0] == '\n')
    449       unexpected_newline (token_start, "\"");
    450     STRING_FINISH;
    451     loc->start = token_start;
    452     val->chars = last_string;
    453     BEGIN INITIAL;
    454     return STRING;
    455   }
    456   <<EOF>> {
    457     unexpected_eof (token_start, "\"");
    458     STRING_FINISH;
    459     loc->start = token_start;
    460     val->chars = last_string;
    461     BEGIN INITIAL;
    462     return STRING;
    463   }
    464 }
    465 
    466   /*----------------------------------------------------------.
    467   | Scanning a Bison character literal, decoding its escapes. |
    468   | The initial quote is already eaten.			      |
    469   `----------------------------------------------------------*/
    470 
    471 <SC_ESCAPED_CHARACTER>
    472 {
    473   "'"|"\n" {
    474     STRING_FINISH;
    475     loc->start = token_start;
    476     val->character = last_string[0];
    477     {
    478       /* FIXME: Eventually, make these errors.  */
    479       if (last_string[0] == '\0')
    480         {
    481           warn_at (*loc, _("empty character literal"));
    482           /* '\0' seems dangerous even if we are about to complain.  */
    483           val->character = '\'';
    484         }
    485       else if (last_string[1] != '\0')
    486         warn_at (*loc, _("extra characters in character literal"));
    487     }
    488     if (yytext[0] == '\n')
    489       unexpected_newline (token_start, "'");
    490     STRING_FREE;
    491     BEGIN INITIAL;
    492     return CHAR;
    493   }
    494   <<EOF>> {
    495     STRING_FINISH;
    496     loc->start = token_start;
    497     val->character = last_string[0];
    498     {
    499       /* FIXME: Eventually, make these errors.  */
    500       if (last_string[0] == '\0')
    501         {
    502           warn_at (*loc, _("empty character literal"));
    503           /* '\0' seems dangerous even if we are about to complain.  */
    504           val->character = '\'';
    505         }
    506       else if (last_string[1] != '\0')
    507         warn_at (*loc, _("extra characters in character literal"));
    508     }
    509     unexpected_eof (token_start, "'");
    510     STRING_FREE;
    511     BEGIN INITIAL;
    512     return CHAR;
    513   }
    514 }
    515 
    516 <SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING>
    517 {
    518   \0	    complain_at (*loc, _("invalid null character"));
    519 }
    520 
    521 
    522   /*----------------------------.
    523   | Decode escaped characters.  |
    524   `----------------------------*/
    525 
    526 <SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>
    527 {
    528   \\[0-7]{1,3} {
    529     unsigned long int c = strtoul (yytext + 1, NULL, 8);
    530     if (!c || UCHAR_MAX < c)
    531       complain_at (*loc, _("invalid number after \\-escape: %s"),
    532                    yytext+1);
    533     else
    534       obstack_1grow (&obstack_for_string, c);
    535   }
    536 
    537   \\x[0-9abcdefABCDEF]+ {
    538     verify (UCHAR_MAX < ULONG_MAX);
    539     unsigned long int c = strtoul (yytext + 2, NULL, 16);
    540     if (!c || UCHAR_MAX < c)
    541       complain_at (*loc, _("invalid number after \\-escape: %s"),
    542                    yytext+1);
    543     else
    544       obstack_1grow (&obstack_for_string, c);
    545   }
    546 
    547   \\a	obstack_1grow (&obstack_for_string, '\a');
    548   \\b	obstack_1grow (&obstack_for_string, '\b');
    549   \\f	obstack_1grow (&obstack_for_string, '\f');
    550   \\n	obstack_1grow (&obstack_for_string, '\n');
    551   \\r	obstack_1grow (&obstack_for_string, '\r');
    552   \\t	obstack_1grow (&obstack_for_string, '\t');
    553   \\v	obstack_1grow (&obstack_for_string, '\v');
    554 
    555   /* \\[\"\'?\\] would be shorter, but it confuses xgettext.  */
    556   \\("\""|"'"|"?"|"\\")  obstack_1grow (&obstack_for_string, yytext[1]);
    557 
    558   \\(u|U[0-9abcdefABCDEF]{4})[0-9abcdefABCDEF]{4} {
    559     int c = convert_ucn_to_byte (yytext);
    560     if (c <= 0)
    561       complain_at (*loc, _("invalid number after \\-escape: %s"),
    562                    yytext+1);
    563     else
    564       obstack_1grow (&obstack_for_string, c);
    565   }
    566   \\(.|\n)	{
    567     char const *p = yytext + 1;
    568     /* Quote only if escaping won't make the character visible.  */
    569     if (c_isspace ((unsigned char) *p) && c_isprint ((unsigned char) *p))
    570       p = quote (p);
    571     else
    572       p = quotearg_style_mem (escape_quoting_style, p, 1);
    573     complain_at (*loc, _("invalid character after \\-escape: %s"), p);
    574   }
    575 }
    576 
    577   /*--------------------------------------------.
    578   | Scanning user-code characters and strings.  |
    579   `--------------------------------------------*/
    580 
    581 <SC_CHARACTER,SC_STRING>
    582 {
    583   {splice}|\\{splice}[^\n\[\]]	STRING_GROW;
    584 }
    585 
    586 <SC_CHARACTER>
    587 {
    588   "'"		STRING_GROW; BEGIN context_state;
    589   \n		unexpected_newline (token_start, "'"); BEGIN context_state;
    590   <<EOF>>	unexpected_eof (token_start, "'"); BEGIN context_state;
    591 }
    592 
    593 <SC_STRING>
    594 {
    595   "\""		STRING_GROW; BEGIN context_state;
    596   \n		unexpected_newline (token_start, "\""); BEGIN context_state;
    597   <<EOF>>	unexpected_eof (token_start, "\""); BEGIN context_state;
    598 }
    599 
    600 
    601   /*---------------------------------------------------.
    602   | Strings, comments etc. can be found in user code.  |
    603   `---------------------------------------------------*/
    604 
    605 <SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>
    606 {
    607   "'" {
    608     STRING_GROW;
    609     context_state = YY_START;
    610     token_start = loc->start;
    611     BEGIN SC_CHARACTER;
    612   }
    613   "\"" {
    614     STRING_GROW;
    615     context_state = YY_START;
    616     token_start = loc->start;
    617     BEGIN SC_STRING;
    618   }
    619   "/"{splice}"*" {
    620     STRING_GROW;
    621     context_state = YY_START;
    622     token_start = loc->start;
    623     BEGIN SC_COMMENT;
    624   }
    625   "/"{splice}"/" {
    626     STRING_GROW;
    627     context_state = YY_START;
    628     BEGIN SC_LINE_COMMENT;
    629   }
    630 }
    631 
    632 
    633 
    634   /*-----------------------------------------------------------.
    635   | Scanning some code in braces (actions). The initial "{" is |
    636   | already eaten.                                             |
    637   `-----------------------------------------------------------*/
    638 
    639 <SC_BRACED_CODE>
    640 {
    641   "{"|"<"{splice}"%"  STRING_GROW; braces_level++;
    642   "%"{splice}">"      STRING_GROW; braces_level--;
    643   "}" {
    644     obstack_1grow (&obstack_for_string, '}');
    645 
    646     --braces_level;
    647     if (braces_level < 0)
    648       {
    649 	STRING_FINISH;
    650 	loc->start = code_start;
    651 	val->code = last_string;
    652 	BEGIN INITIAL;
    653 	return BRACED_CODE;
    654       }
    655   }
    656 
    657   /* Tokenize '<<%' correctly (as '<<' '%') rather than incorrrectly
    658      (as '<' '<%').  */
    659   "<"{splice}"<"  STRING_GROW;
    660 
    661   <<EOF>> {
    662     unexpected_eof (code_start, "}");
    663     STRING_FINISH;
    664     loc->start = code_start;
    665     val->code = last_string;
    666     BEGIN INITIAL;
    667     return BRACED_CODE;
    668   }
    669 }
    670 
    671 
    672   /*--------------------------------------------------------------.
    673   | Scanning some prologue: from "%{" (already scanned) to "%}".  |
    674   `--------------------------------------------------------------*/
    675 
    676 <SC_PROLOGUE>
    677 {
    678   "%}" {
    679     STRING_FINISH;
    680     loc->start = code_start;
    681     val->chars = last_string;
    682     BEGIN INITIAL;
    683     return PROLOGUE;
    684   }
    685 
    686   <<EOF>> {
    687     unexpected_eof (code_start, "%}");
    688     STRING_FINISH;
    689     loc->start = code_start;
    690     val->chars = last_string;
    691     BEGIN INITIAL;
    692     return PROLOGUE;
    693   }
    694 }
    695 
    696 
    697   /*---------------------------------------------------------------.
    698   | Scanning the epilogue (everything after the second "%%", which |
    699   | has already been eaten).                                       |
    700   `---------------------------------------------------------------*/
    701 
    702 <SC_EPILOGUE>
    703 {
    704   <<EOF>> {
    705     STRING_FINISH;
    706     loc->start = code_start;
    707     val->chars = last_string;
    708     BEGIN INITIAL;
    709     return EPILOGUE;
    710   }
    711 }
    712 
    713 
    714   /*-----------------------------------------------------.
    715   | By default, grow the string obstack with the input.  |
    716   `-----------------------------------------------------*/
    717 
    718 <SC_COMMENT,SC_LINE_COMMENT,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE,SC_STRING,SC_CHARACTER,SC_ESCAPED_STRING,SC_ESCAPED_CHARACTER>.	|
    719 <SC_COMMENT,SC_LINE_COMMENT,SC_BRACED_CODE,SC_PROLOGUE,SC_EPILOGUE>\n	STRING_GROW;
    720 
    721 %%
    722 
    723 /* Read bytes from FP into buffer BUF of size SIZE.  Return the
    724    number of bytes read.  Remove '\r' from input, treating \r\n
    725    and isolated \r as \n.  */
    726 
    727 static size_t
    728 no_cr_read (FILE *fp, char *buf, size_t size)
    729 {
    730   size_t bytes_read = fread (buf, 1, size, fp);
    731   if (bytes_read)
    732     {
    733       char *w = memchr (buf, '\r', bytes_read);
    734       if (w)
    735 	{
    736 	  char const *r = ++w;
    737 	  char const *lim = buf + bytes_read;
    738 
    739 	  for (;;)
    740 	    {
    741 	      /* Found an '\r'.  Treat it like '\n', but ignore any
    742 		 '\n' that immediately follows.  */
    743 	      w[-1] = '\n';
    744 	      if (r == lim)
    745 		{
    746 		  int ch = getc (fp);
    747 		  if (ch != '\n' && ungetc (ch, fp) != ch)
    748 		    break;
    749 		}
    750 	      else if (*r == '\n')
    751 		r++;
    752 
    753 	      /* Copy until the next '\r'.  */
    754 	      do
    755 		{
    756 		  if (r == lim)
    757 		    return w - buf;
    758 		}
    759 	      while ((*w++ = *r++) != '\r');
    760 	    }
    761 
    762 	  return w - buf;
    763 	}
    764     }
    765 
    766   return bytes_read;
    767 }
    768 
    769 
    770 
    771 /*------------------------------------------------------.
    772 | Scan NUMBER for a base-BASE integer at location LOC.  |
    773 `------------------------------------------------------*/
    774 
    775 static unsigned long int
    776 scan_integer (char const *number, int base, location loc)
    777 {
    778   verify (INT_MAX < ULONG_MAX);
    779   unsigned long int num = strtoul (number, NULL, base);
    780 
    781   if (INT_MAX < num)
    782     {
    783       complain_at (loc, _("integer out of range: %s"), quote (number));
    784       num = INT_MAX;
    785     }
    786 
    787   return num;
    788 }
    789 
    790 
    791 /*------------------------------------------------------------------.
    792 | Convert universal character name UCN to a single-byte character,  |
    793 | and return that character.  Return -1 if UCN does not correspond  |
    794 | to a single-byte character.					    |
    795 `------------------------------------------------------------------*/
    796 
    797 static int
    798 convert_ucn_to_byte (char const *ucn)
    799 {
    800   verify (UCHAR_MAX <= INT_MAX);
    801   unsigned long int code = strtoul (ucn + 2, NULL, 16);
    802 
    803   /* FIXME: Currently we assume Unicode-compatible unibyte characters
    804      on ASCII hosts (i.e., Latin-1 on hosts with 8-bit bytes).  On
    805      non-ASCII hosts we support only the portable C character set.
    806      These limitations should be removed once we add support for
    807      multibyte characters.  */
    808 
    809   if (UCHAR_MAX < code)
    810     return -1;
    811 
    812 #if ! ('$' == 0x24 && '@' == 0x40 && '`' == 0x60 && '~' == 0x7e)
    813   {
    814     /* A non-ASCII host.  Use CODE to index into a table of the C
    815        basic execution character set, which is guaranteed to exist on
    816        all Standard C platforms.  This table also includes '$', '@',
    817        and '`', which are not in the basic execution character set but
    818        which are unibyte characters on all the platforms that we know
    819        about.  */
    820     static signed char const table[] =
    821       {
    822 	'\0',   -1,   -1,   -1,   -1,   -1,   -1, '\a',
    823 	'\b', '\t', '\n', '\v', '\f', '\r',   -1,   -1,
    824 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    825 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    826 	 ' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'',
    827 	 '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
    828 	 '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
    829 	 '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
    830 	 '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
    831 	 'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
    832 	 'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
    833 	 'X',  'Y',  'Z',  '[', '\\',  ']',  '^',  '_',
    834 	 '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
    835 	 'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
    836 	 'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
    837 	 'x',  'y',  'z',  '{',  '|',  '}',  '~'
    838       };
    839 
    840     code = code < sizeof table ? table[code] : -1;
    841   }
    842 #endif
    843 
    844   return code;
    845 }
    846 
    847 
    848 /*---------------------------------------------------------------------.
    849 | Handle '#line INT( "FILE")?\n'.  ARGS has already skipped '#line '.  |
    850 `---------------------------------------------------------------------*/
    851 
    852 static void
    853 handle_syncline (char *args, location loc)
    854 {
    855   char *file;
    856   unsigned long int lineno = strtoul (args, &file, 10);
    857   if (INT_MAX <= lineno)
    858     {
    859       warn_at (loc, _("line number overflow"));
    860       lineno = INT_MAX;
    861     }
    862 
    863   file = mbschr (file, '"');
    864   if (file)
    865     {
    866       *mbschr (file + 1, '"') = '\0';
    867       current_file = uniqstr_new (file + 1);
    868     }
    869   boundary_set (&scanner_cursor, current_file, lineno, 1);
    870 }
    871 
    872 
    873 /*----------------------------------------------------------------.
    874 | For a token or comment starting at START, report message MSGID, |
    875 | which should say that an end marker was found before		  |
    876 | the expected TOKEN_END.					  |
    877 `----------------------------------------------------------------*/
    878 
    879 static void
    880 unexpected_end (boundary start, char const *msgid, char const *token_end)
    881 {
    882   location loc;
    883   loc.start = start;
    884   loc.end = scanner_cursor;
    885   token_end = quote (token_end);
    886   /* Instead of '\'', display "'".  */
    887   if (!strcmp (token_end, "'\\''"))
    888     token_end = "\"'\"";
    889   complain_at (loc, _(msgid), token_end);
    890 }
    891 
    892 
    893 /*------------------------------------------------------------------------.
    894 | Report an unexpected EOF in a token or comment starting at START.       |
    895 | An end of file was encountered and the expected TOKEN_END was missing.  |
    896 `------------------------------------------------------------------------*/
    897 
    898 static void
    899 unexpected_eof (boundary start, char const *token_end)
    900 {
    901   unexpected_end (start, N_("missing %s at end of file"), token_end);
    902 }
    903 
    904 
    905 /*----------------------------------------.
    906 | Likewise, but for unexpected newlines.  |
    907 `----------------------------------------*/
    908 
    909 static void
    910 unexpected_newline (boundary start, char const *token_end)
    911 {
    912   unexpected_end (start, N_("missing %s at end of line"), token_end);
    913 }
    914 
    915 
    916 /*-------------------------.
    917 | Initialize the scanner.  |
    918 `-------------------------*/
    919 
    920 void
    921 gram_scanner_initialize (void)
    922 {
    923   obstack_init (&obstack_for_string);
    924 }
    925 
    926 
    927 /*-----------------------------------------------.
    928 | Free all the memory allocated to the scanner.  |
    929 `-----------------------------------------------*/
    930 
    931 void
    932 gram_scanner_free (void)
    933 {
    934   obstack_free (&obstack_for_string, 0);
    935   /* Reclaim Flex's buffers.  */
    936   yylex_destroy ();
    937 }
    938