/* Bison Grammar Scanner -*- C -*-
- Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation,
- Inc.
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ Free Software Foundation, Inc.
This file is part of Bison, the GNU Compiler Compiler.
You should have received a copy of the GNU General Public License
along with this program. If not, 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"
%{
#define gram_wrap() 1
#define FLEX_PREFIX(Id) gram_ ## Id
-#include "flex-scanner.h"
+#include <src/flex-scanner.h>
-#include "complain.h"
-#include "files.h"
-#include "gram.h"
-#include "quotearg.h"
-#include "reader.h"
-#include "uniqstr.h"
+#include <src/complain.h>
+#include <src/files.h>
+#include <src/gram.h>
+#include <quotearg.h>
+#include <src/reader.h>
+#include <src/uniqstr.h>
+#include <ctype.h>
#include <mbswidth.h>
#include <quote.h>
-#include "scan-gram.h"
+#include <src/scan-gram.h>
#define YY_DECL GRAM_LEX_DECL
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_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 { \
+ val->Field = Value; \
+ return Token; \
+ } while (0)
+
+#define ROLLBACK_CURRENT_TOKEN \
+ do { \
+ scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0); \
+ yyless (0); \
+ } while (0)
+
/* A string representing the most recently saved token. */
static char *last_string;
+/* Bracketed identifier. */
+static uniqstr bracketed_id_str = 0;
+static location bracketed_id_loc;
+static boundary bracketed_id_start;
+static int bracketed_id_context_state = 0;
+
void
gram_scanner_last_string_free (void)
{
/* A identifier was just read in directives/rules. Special state
to capture the sequence `identifier :'. */
%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 %%);
%x SC_COMMENT SC_LINE_COMMENT
/* Strings and characters in code. */
%x SC_STRING SC_CHARACTER
+ /* Bracketed identifiers support. */
+%x SC_BRACKETED_ID SC_RETURN_BRACKETED_ID
-letter [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
+letter [-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
id {letter}({letter}|[0-9])*
-directive %{letter}({letter}|[0-9]|-)*
+directive %{id}
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 and newline, as this simplifies our implementation. */
-tag [^\0\n>]+
+ 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<>]+
/* Zero or more instances of backslash-newline. Following GCC, allow
white space between the backslash and the newline. */
%%
%{
- /* Nesting level of the current code in braces. */
- int braces_level IF_LINT (= 0);
+ /* Nesting level. Either for nested braces, or nested angle brackets
+ (but not mixed). */
+ int nesting IF_LINT (= 0);
/* Parent context state, when applicable. */
int context_state IF_LINT (= 0);
| Scanning white space. |
`-----------------------*/
-<INITIAL,SC_AFTER_IDENTIFIER>
+<INITIAL,SC_AFTER_IDENTIFIER,SC_BRACKETED_ID,SC_RETURN_BRACKETED_ID>
{
/* Comments and white space. */
"," warn_at (*loc, _("stray `,' treated as white space"));
/*----------------------------.
| Scanning Bison directives. |
`----------------------------*/
+
+ /* For directives that are also command line options, the regex must be
+ "%..."
+ after "[-_]"s are removed, and the directive must match the --long
+ option name, with a single string argument. Otherwise, add exceptions
+ to ../build-aux/cross-options.pl. */
+
<INITIAL>
{
"%binary" return PERCENT_NONASSOC;
"%code" return PERCENT_CODE;
- "%debug" return PERCENT_DEBUG;
+ "%debug" RETURN_PERCENT_FLAG("parse.trace");
"%default"[-_]"prec" return PERCENT_DEFAULT_PREC;
"%define" return PERCENT_DEFINE;
"%defines" return PERCENT_DEFINES;
"%glr-parser" return PERCENT_GLR_PARSER;
"%language" return PERCENT_LANGUAGE;
"%left" return PERCENT_LEFT;
- "%lex-param" return PERCENT_LEX_PARAM;
- "%locations" return PERCENT_LOCATIONS;
+ "%lex-param" RETURN_PERCENT_PARAM(lex);
+ "%locations" RETURN_PERCENT_FLAG("locations");
"%merge" return PERCENT_MERGE;
"%name"[-_]"prefix" return PERCENT_NAME_PREFIX;
"%no"[-_]"default"[-_]"prec" return PERCENT_NO_DEFAULT_PREC;
"%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;
- "%pure"[-_]"parser" return PERCENT_PURE_PARSER;
+ "%pure"[-_]"parser" RETURN_PERCENT_FLAG("api.pure");
"%require" return PERCENT_REQUIRE;
"%right" return PERCENT_RIGHT;
"%skeleton" return PERCENT_SKELETON;
"=" return EQUAL;
"|" return PIPE;
";" return SEMICOLON;
- "<*>" return TYPE_TAG_ANY;
- "<>" return TYPE_TAG_NONE;
{id} {
val->uniqstr = uniqstr_new (yytext);
id_loc = *loc;
+ bracketed_id_str = NULL;
BEGIN SC_AFTER_IDENTIFIER;
}
return INT;
}
- /* Characters. We don't check there is only one. */
- "'" STRING_GROW; token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
+ /* Identifiers may not start with a digit. Yet, don't silently
+ accept "1FOO" as "1 FOO". */
+ {int}{id} {
+ complain_at (*loc, _("invalid identifier: %s"), quote (yytext));
+ }
+
+ /* Characters. */
+ "'" token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
/* Strings. */
"\"" token_start = loc->start; BEGIN SC_ESCAPED_STRING;
/* Code in between braces. */
"{" {
STRING_GROW;
- braces_level = 0;
+ nesting = 0;
code_start = loc->start;
BEGIN SC_BRACED_CODE;
}
/* A type. */
+ "<*>" return TAG_ANY;
+ "<>" return TAG_NONE;
"<"{tag}">" {
obstack_grow (&obstack_for_string, yytext + 1, yyleng - 2);
STRING_FINISH;
val->uniqstr = uniqstr_new (last_string);
STRING_FREE;
- return TYPE;
+ return TAG;
+ }
+ "<" {
+ nesting = 0;
+ token_start = loc->start;
+ BEGIN SC_TAG;
}
"%%" {
return PERCENT_PERCENT;
}
+ "[" {
+ bracketed_id_str = NULL;
+ bracketed_id_start = loc->start;
+ bracketed_id_context_state = YY_START;
+ BEGIN SC_BRACKETED_ID;
+ }
+
. {
complain_at (*loc, _("invalid character: %s"), quote (yytext));
}
}
+ /*--------------------------------------------------------------.
+ | Supporting \0 complexifies our implementation for no expected |
+ | added value. |
+ `--------------------------------------------------------------*/
+
+<SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING,SC_TAG>
+{
+ \0 complain_at (*loc, _("invalid null character"));
+}
+
+
/*-----------------------------------------------------------------.
| Scanning after an identifier, checking whether a colon is next. |
`-----------------------------------------------------------------*/
<SC_AFTER_IDENTIFIER>
{
+ "[" {
+ if (bracketed_id_str)
+ {
+ ROLLBACK_CURRENT_TOKEN;
+ BEGIN SC_RETURN_BRACKETED_ID;
+ *loc = id_loc;
+ return ID;
+ }
+ else
+ {
+ bracketed_id_start = loc->start;
+ bracketed_id_context_state = YY_START;
+ BEGIN SC_BRACKETED_ID;
+ }
+ }
":" {
+ BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
*loc = id_loc;
- BEGIN INITIAL;
return ID_COLON;
}
. {
- scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);
- yyless (0);
+ ROLLBACK_CURRENT_TOKEN;
+ BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
*loc = id_loc;
- BEGIN INITIAL;
return ID;
}
<<EOF>> {
+ BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
*loc = id_loc;
- BEGIN INITIAL;
return ID;
}
}
+ /*--------------------------------.
+ | Scanning bracketed identifiers. |
+ `--------------------------------*/
+
+<SC_BRACKETED_ID>
+{
+ {id} {
+ if (bracketed_id_str)
+ {
+ complain_at (*loc, _("unexpected identifier in bracketed name: %s"),
+ quote (yytext));
+ }
+ else
+ {
+ bracketed_id_str = uniqstr_new (yytext);
+ bracketed_id_loc = *loc;
+ }
+ }
+ "]" {
+ BEGIN bracketed_id_context_state;
+ if (bracketed_id_str)
+ {
+ if (INITIAL == bracketed_id_context_state)
+ {
+ val->uniqstr = bracketed_id_str;
+ bracketed_id_str = 0;
+ *loc = bracketed_id_loc;
+ return BRACKETED_ID;
+ }
+ }
+ else
+ complain_at (*loc, _("an identifier expected"));
+ }
+ . {
+ complain_at (*loc, _("invalid character in bracketed name: %s"),
+ quote (yytext));
+ }
+ <<EOF>> {
+ BEGIN bracketed_id_context_state;
+ unexpected_eof (bracketed_id_start, "]");
+ }
+}
+
+<SC_RETURN_BRACKETED_ID>
+{
+ . {
+ ROLLBACK_CURRENT_TOKEN;
+ val->uniqstr = bracketed_id_str;
+ bracketed_id_str = 0;
+ *loc = bracketed_id_loc;
+ BEGIN INITIAL;
+ return BRACKETED_ID;
+ }
+}
+
/*---------------------------------------------------------------.
| Scanning a Yacc comment. The initial `/ *' is already eaten. |
<SC_ESCAPED_CHARACTER>
{
"'"|"\n" {
- if (yytext[0] == '\n')
- unexpected_newline (token_start, "'");
- STRING_GROW;
STRING_FINISH;
loc->start = token_start;
- val->character = last_string[1];
+ val->character = last_string[0];
+ {
+ /* FIXME: Eventually, make these errors. */
+ if (last_string[0] == '\0')
+ {
+ warn_at (*loc, _("empty character literal"));
+ /* '\0' seems dangerous even if we are about to complain. */
+ val->character = '\'';
+ }
+ else if (last_string[1] != '\0')
+ warn_at (*loc, _("extra characters in character literal"));
+ }
+ if (yytext[0] == '\n')
+ unexpected_newline (token_start, "'");
STRING_FREE;
BEGIN INITIAL;
return CHAR;
}
<<EOF>> {
- unexpected_eof (token_start, "'");
STRING_FINISH;
loc->start = token_start;
- if (strlen(last_string) > 1)
- val->character = last_string[1];
- else
- val->character = last_string[0];
+ val->character = last_string[0];
+ {
+ /* FIXME: Eventually, make these errors. */
+ if (last_string[0] == '\0')
+ {
+ warn_at (*loc, _("empty character literal"));
+ /* '\0' seems dangerous even if we are about to complain. */
+ val->character = '\'';
+ }
+ else if (last_string[1] != '\0')
+ warn_at (*loc, _("extra characters in character literal"));
+ }
+ unexpected_eof (token_start, "'");
STRING_FREE;
BEGIN INITIAL;
return CHAR;
}
}
-<SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING>
+ /*-----------------------------------------------------------.
+ | Scanning a Bison nested tag. The initial angle bracket is |
+ | already eaten. |
+ `-----------------------------------------------------------*/
+
+<SC_TAG>
{
- \0 complain_at (*loc, _("invalid null character"));
-}
+ ">" {
+ --nesting;
+ if (nesting < 0)
+ {
+ STRING_FINISH;
+ loc->start = token_start;
+ val->uniqstr = uniqstr_new (last_string);
+ STRING_FREE;
+ BEGIN INITIAL;
+ return TAG;
+ }
+ STRING_GROW;
+ }
+ [^<>]+ STRING_GROW;
+ "<"+ STRING_GROW; nesting += yyleng;
+
+ <<EOF>> {
+ unexpected_eof (token_start, ">");
+ STRING_FINISH;
+ loc->start = token_start;
+ val->uniqstr = uniqstr_new (last_string);
+ STRING_FREE;
+ BEGIN INITIAL;
+ return TAG;
+ }
+}
/*----------------------------.
| Decode escaped characters. |
{
\\[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);
}
\\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);
}
\\(u|U[0-9abcdefABCDEF]{4})[0-9abcdefABCDEF]{4} {
int c = convert_ucn_to_byte (yytext);
- if (c < 0)
- complain_at (*loc, _("invalid escape sequence: %s"), quote (yytext));
- else if (! c)
- complain_at (*loc, _("invalid null character: %s"), quote (yytext));
+ if (c <= 0)
+ complain_at (*loc, _("invalid number after \\-escape: %s"),
+ yytext+1);
else
obstack_1grow (&obstack_for_string, c);
}
\\(.|\n) {
- complain_at (*loc, _("unrecognized escape sequence: %s"), quote (yytext));
- STRING_GROW;
+ char const *p = yytext + 1;
+ /* Quote only if escaping won't make the character visible. */
+ if (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);
}
}
<SC_BRACED_CODE>
{
- "{"|"<"{splice}"%" STRING_GROW; braces_level++;
- "%"{splice}">" STRING_GROW; braces_level--;
+ "{"|"<"{splice}"%" STRING_GROW; nesting++;
+ "%"{splice}">" STRING_GROW; nesting--;
"}" {
obstack_1grow (&obstack_for_string, '}');
- --braces_level;
- if (braces_level < 0)
+ --nesting;
+ if (nesting < 0)
{
STRING_FINISH;
loc->start = code_start;