/* Bison Grammar Scanner -*- C -*-
- Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation,
- Inc.
+ Copyright (C) 2002-2011 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"
%{
#include <src/reader.h>
#include <src/uniqstr.h>
+#include <ctype.h>
#include <mbswidth.h>
#include <quote.h>
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 complex tag, with nested angles brackets. */
%x SC_TAG
- /* Three types of user code:
+ /* Four types of user code:
- prologue (code between `%{' `%}' in the first section, before %%);
- actions, printers, union, etc, (between braced in the middle section);
- - epilogue (everything after the second %%). */
-%x SC_PROLOGUE SC_BRACED_CODE SC_EPILOGUE
+ - 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. */
%x SC_STRING SC_CHARACTER
+ /* Bracketed identifiers support. */
+%x SC_BRACKETED_ID SC_RETURN_BRACKETED_ID
letter [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
-id {letter}({letter}|[0-9])*
-directive %{letter}({letter}|[0-9]|-)*
+id {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
| 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;
{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;
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;
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));
}
<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;
{
\\[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);
}
}
| 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;
/*-----------------------------------------------------------.
- | 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--;
+
+ /* Tokenize `<<%' correctly (as `<<' `%') rather than incorrrectly
+ (as `<' `<%'). */
+ "<"{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;
+ return token;
+ }
+}
+
+<SC_BRACED_CODE>
+{
"}" {
obstack_1grow (&obstack_for_string, '}');
return BRACED_CODE;
}
}
+}
- /* Tokenize `<<%' correctly (as `<<' `%') rather than incorrrectly
- (as `<' `<%'). */
- "<"{splice}"<" STRING_GROW;
-
- <<EOF>> {
- unexpected_eof (code_start, "}");
- 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 "%}". |
`--------------------------------------------------------------*/
| 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;
%%