mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-16 15:53:03 +00:00
java: add support for lookahead correction
Shamelessly stolen from Adrian Vogelsgesang's implementation in lalr1.cc. * data/skeletons/lalr1.java (yycdebugNnl, yyLacCheck, yyLacEstablish) (yyLacDiscard, yylacStack, yylacEstablished): New. (Context): Use it. * examples/java/calc/Calc.y: Enable LAC.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# Java skeleton for Bison -*- autoconf -*-
|
# Java skeleton for Bison -*- java -*-
|
||||||
|
|
||||||
# Copyright (C) 2007-2015, 2018-2020 Free Software Foundation, Inc.
|
# Copyright (C) 2007-2015, 2018-2020 Free Software Foundation, Inc.
|
||||||
|
|
||||||
@@ -80,6 +80,15 @@ m4_define([b4_define_state],
|
|||||||
]b4_yystype[ yylval = null;
|
]b4_yystype[ yylval = null;
|
||||||
]])
|
]])
|
||||||
|
|
||||||
|
# parse.lac
|
||||||
|
b4_percent_define_default([[parse.lac]], [[none]])
|
||||||
|
b4_percent_define_check_values([[[[parse.lac]], [[full]], [[none]]]])
|
||||||
|
b4_define_flag_if([lac])
|
||||||
|
m4_define([b4_lac_flag],
|
||||||
|
[m4_if(b4_percent_define_get([[parse.lac]]),
|
||||||
|
[none], [[0]], [[1]])])
|
||||||
|
|
||||||
|
|
||||||
## ------------- ##
|
## ------------- ##
|
||||||
## Parser File. ##
|
## Parser File. ##
|
||||||
## ------------- ##
|
## ------------- ##
|
||||||
@@ -93,6 +102,7 @@ b4_output_begin([b4_parser_file_name])[
|
|||||||
]b4_user_pre_prologue[
|
]b4_user_pre_prologue[
|
||||||
]b4_user_post_prologue[
|
]b4_user_post_prologue[
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Vector;
|
||||||
]b4_percent_code_get([[imports]])[
|
]b4_percent_code_get([[imports]])[
|
||||||
/**
|
/**
|
||||||
* A Bison parser, automatically generated from <tt>]m4_bpatsubst(b4_file_name, [^"\(.*\)"$], [\1])[</tt>.
|
* A Bison parser, automatically generated from <tt>]m4_bpatsubst(b4_file_name, [^"\(.*\)"$], [\1])[</tt>.
|
||||||
@@ -257,8 +267,10 @@ import java.text.MessageFormat;
|
|||||||
*/
|
*/
|
||||||
public ]b4_parser_class[(]b4_parse_param_decl([b4_lex_param_decl])[)]b4_maybe_throws([b4_init_throws])[
|
public ]b4_parser_class[(]b4_parse_param_decl([b4_lex_param_decl])[)]b4_maybe_throws([b4_init_throws])[
|
||||||
{
|
{
|
||||||
]b4_percent_code_get([[init]])[
|
]b4_percent_code_get([[init]])[]b4_lac_if([[
|
||||||
this.yylexer = new YYLexer (]b4_lex_param_call[);
|
this.yylacStack = new Vector<Integer>();
|
||||||
|
this.yylacEstablished = false;]])[
|
||||||
|
this.yylexer = new YYLexer(]b4_lex_param_call[);
|
||||||
]b4_parse_param_cons[
|
]b4_parse_param_cons[
|
||||||
}
|
}
|
||||||
]])[
|
]])[
|
||||||
@@ -269,7 +281,9 @@ import java.text.MessageFormat;
|
|||||||
*/
|
*/
|
||||||
]b4_lexer_if([[protected]], [[public]]) b4_parser_class[(]b4_parse_param_decl([[Lexer yylexer]])[)]b4_maybe_throws([b4_init_throws])[
|
]b4_lexer_if([[protected]], [[public]]) b4_parser_class[(]b4_parse_param_decl([[Lexer yylexer]])[)]b4_maybe_throws([b4_init_throws])[
|
||||||
{
|
{
|
||||||
]b4_percent_code_get([[init]])[
|
]b4_percent_code_get([[init]])[]b4_lac_if([[
|
||||||
|
this.yylacStack = new Vector<Integer>();
|
||||||
|
this.yylacEstablished = false;]])[
|
||||||
this.yylexer = yylexer;
|
this.yylexer = yylexer;
|
||||||
]b4_parse_param_cons[
|
]b4_parse_param_cons[
|
||||||
}
|
}
|
||||||
@@ -338,6 +352,11 @@ import java.text.MessageFormat;
|
|||||||
yylexer.yyerror(new ]b4_location_type[ (pos), msg);
|
yylexer.yyerror(new ]b4_location_type[ (pos), msg);
|
||||||
}]])[
|
}]])[
|
||||||
]b4_parse_trace_if([[
|
]b4_parse_trace_if([[
|
||||||
|
protected final void yycdebugNnl(String s) {
|
||||||
|
if (0 < yydebug)
|
||||||
|
yyDebugStream.print(s);
|
||||||
|
}
|
||||||
|
|
||||||
protected final void yycdebug(String s) {
|
protected final void yycdebug(String s) {
|
||||||
if (0 < yydebug)
|
if (0 < yydebug)
|
||||||
yyDebugStream.println(s);
|
yyDebugStream.println(s);
|
||||||
@@ -541,7 +560,12 @@ import java.text.MessageFormat;
|
|||||||
/* @@$. */
|
/* @@$. */
|
||||||
]b4_location_type[ yyloc;]])[
|
]b4_location_type[ yyloc;]])[
|
||||||
]b4_push_if([],[[
|
]b4_push_if([],[[
|
||||||
]b4_define_state[]b4_parse_trace_if([[
|
]b4_define_state[
|
||||||
|
]b4_lac_if([[
|
||||||
|
// Discard the LAC context in case there still is one left from a
|
||||||
|
// previous invocation.
|
||||||
|
yylacDiscard("init");]])[
|
||||||
|
]b4_parse_trace_if([[
|
||||||
yycdebug ("Starting parse");]])[
|
yycdebug ("Starting parse");]])[
|
||||||
yyerrstatus_ = 0;
|
yyerrstatus_ = 0;
|
||||||
yynerrs = 0;
|
yynerrs = 0;
|
||||||
@@ -635,19 +659,24 @@ b4_dollar_popdef[]dnl
|
|||||||
/* If the proper action on seeing token YYTOKEN is to reduce or to
|
/* If the proper action on seeing token YYTOKEN is to reduce or to
|
||||||
detect an error, take that action. */
|
detect an error, take that action. */
|
||||||
yyn += yytoken.getCode();
|
yyn += yytoken.getCode();
|
||||||
if (yyn < 0 || YYLAST_ < yyn || yycheck_[yyn] != yytoken.getCode())
|
if (yyn < 0 || YYLAST_ < yyn || yycheck_[yyn] != yytoken.getCode()) {]b4_lac_if([[
|
||||||
|
if (!yylacEstablish(yystack, yytoken)) {
|
||||||
|
label = YYERRLAB;
|
||||||
|
} else]])[
|
||||||
label = YYDEFAULT;
|
label = YYDEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
/* <= 0 means reduce or error. */
|
/* <= 0 means reduce or error. */
|
||||||
else if ((yyn = yytable_[yyn]) <= 0)
|
else if ((yyn = yytable_[yyn]) <= 0)
|
||||||
{
|
{
|
||||||
if (yyTableValueIsError (yyn))
|
if (yyTableValueIsError(yyn)) {
|
||||||
label = YYERRLAB;
|
label = YYERRLAB;
|
||||||
else
|
}]b4_lac_if([[ else if (!yylacEstablish(yystack, yytoken)) {
|
||||||
{
|
label = YYERRLAB;
|
||||||
yyn = -yyn;
|
}]])[ else {
|
||||||
label = YYREDUCE;
|
yyn = -yyn;
|
||||||
}
|
label = YYREDUCE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -665,7 +694,8 @@ b4_dollar_popdef[]dnl
|
|||||||
--yyerrstatus_;
|
--yyerrstatus_;
|
||||||
|
|
||||||
yystate = yyn;
|
yystate = yyn;
|
||||||
yystack.push (yystate, yylval]b4_locations_if([, yylloc])[);
|
yystack.push(yystate, yylval]b4_locations_if([, yylloc])[);]b4_lac_if([[
|
||||||
|
yylacDiscard("shift");]])[
|
||||||
label = YYNEWSTATE;
|
label = YYNEWSTATE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -701,7 +731,7 @@ b4_dollar_popdef[]dnl
|
|||||||
++yynerrs;
|
++yynerrs;
|
||||||
if (yychar == YYEMPTY_)
|
if (yychar == YYEMPTY_)
|
||||||
yytoken = null;
|
yytoken = null;
|
||||||
yyreportSyntaxError(new Context(yystack, yytoken]b4_locations_if([[, yylloc]])[));
|
yyreportSyntaxError(new Context(this, yystack, yytoken]b4_locations_if([[, yylloc]])[));
|
||||||
}
|
}
|
||||||
]b4_locations_if([[
|
]b4_locations_if([[
|
||||||
yyerrloc = yylloc;]])[
|
yyerrloc = yylloc;]])[
|
||||||
@@ -784,7 +814,8 @@ b4_dollar_popdef[]dnl
|
|||||||
yyloc = yylloc (yystack, 2);
|
yyloc = yylloc (yystack, 2);
|
||||||
yystack.pop (2);]])[
|
yystack.pop (2);]])[
|
||||||
|
|
||||||
/* Shift the error token. */]b4_parse_trace_if([[
|
/* Shift the error token. */]b4_lac_if([[
|
||||||
|
yylacDiscard("error recovery");]])[]b4_parse_trace_if([[
|
||||||
yySymbolPrint("Shifting", SymbolKind.get(yystos_[yyn]),
|
yySymbolPrint("Shifting", SymbolKind.get(yystos_[yyn]),
|
||||||
yylval]b4_locations_if([, yyloc])[);]])[
|
yylval]b4_locations_if([, yyloc])[);]])[
|
||||||
|
|
||||||
@@ -820,7 +851,9 @@ b4_dollar_popdef[]dnl
|
|||||||
this.yyn = 0;
|
this.yyn = 0;
|
||||||
this.yylen = 0;
|
this.yylen = 0;
|
||||||
this.yystate = 0;
|
this.yystate = 0;
|
||||||
this.yystack = new YYStack ();
|
this.yystack = new YYStack();]b4_lac_if([[
|
||||||
|
this.yylacStack = new Vector<Integer>();
|
||||||
|
this.yylacEstablished = false;]])[
|
||||||
this.label = YYNEWSTATE;
|
this.label = YYNEWSTATE;
|
||||||
|
|
||||||
/* Error handling. */
|
/* Error handling. */
|
||||||
@@ -881,13 +914,14 @@ b4_dollar_popdef[]dnl
|
|||||||
* a syntax error diagnostic.
|
* a syntax error diagnostic.
|
||||||
*/
|
*/
|
||||||
public static final class Context {
|
public static final class Context {
|
||||||
Context (YYStack stack, SymbolKind token]b4_locations_if([[, ]b4_location_type[ loc]])[)
|
Context(]b4_parser_class[ parser, YYStack stack, SymbolKind token]b4_locations_if([[, ]b4_location_type[ loc]])[) {
|
||||||
{
|
yyparser = parser;
|
||||||
yystack = stack;
|
yystack = stack;
|
||||||
yytoken = token;]b4_locations_if([[
|
yytoken = token;]b4_locations_if([[
|
||||||
yylocation = loc;]])[
|
yylocation = loc;]])[
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ]b4_parser_class[ yyparser;
|
||||||
private YYStack yystack;
|
private YYStack yystack;
|
||||||
|
|
||||||
|
|
||||||
@@ -921,7 +955,27 @@ b4_dollar_popdef[]dnl
|
|||||||
}
|
}
|
||||||
|
|
||||||
int getExpectedTokens(SymbolKind yyarg[], int yyoffset, int yyargn) {
|
int getExpectedTokens(SymbolKind yyarg[], int yyoffset, int yyargn) {
|
||||||
int yycount = yyoffset;
|
int yycount = yyoffset;]b4_lac_if([b4_parse_trace_if([[
|
||||||
|
// Execute LAC once. We don't care if it is successful, we
|
||||||
|
// only do it for the sake of debugging output.
|
||||||
|
if (!yyparser.yylacEstablished)
|
||||||
|
yyparser.yylacCheck(yystack, yytoken);
|
||||||
|
]])[
|
||||||
|
for (int yyx = 0; yyx < YYNTOKENS_; ++yyx)
|
||||||
|
{
|
||||||
|
SymbolKind yysym = SymbolKind.get(yyx);
|
||||||
|
if (yysym != ]b4_symbol(1, kind)[
|
||||||
|
&& yysym != ]b4_symbol(2, kind)[
|
||||||
|
&& yyparser.yylacCheck(yystack, yysym))
|
||||||
|
{
|
||||||
|
if (yyarg == null)
|
||||||
|
yycount += 1;
|
||||||
|
else if (yycount == yyargn)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
yyarg[yycount++] = yysym;
|
||||||
|
}
|
||||||
|
}]], [[
|
||||||
int yyn = yypact_[this.yystack.stateAt(0)];
|
int yyn = yypact_[this.yystack.stateAt(0)];
|
||||||
if (!yyPactValueIsDefault(yyn))
|
if (!yyPactValueIsDefault(yyn))
|
||||||
{
|
{
|
||||||
@@ -944,13 +998,152 @@ b4_dollar_popdef[]dnl
|
|||||||
else
|
else
|
||||||
yyarg[yycount++] = SymbolKind.get(yyx);
|
yyarg[yycount++] = SymbolKind.get(yyx);
|
||||||
}
|
}
|
||||||
}
|
}]])[
|
||||||
if (yyarg != null && yycount == yyoffset && yyoffset < yyargn)
|
if (yyarg != null && yycount == yyoffset && yyoffset < yyargn)
|
||||||
yyarg[yycount] = null;
|
yyarg[yycount] = null;
|
||||||
return yycount - yyoffset;
|
return yycount - yyoffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
]b4_lac_if([[
|
||||||
|
/** Check the lookahead yytoken.
|
||||||
|
* \returns true iff the token will be eventually shifted.
|
||||||
|
*/
|
||||||
|
boolean yylacCheck(YYStack yystack, SymbolKind yytoken)
|
||||||
|
{
|
||||||
|
// Logically, the yylacStack's lifetime is confined to this function.
|
||||||
|
// Clear it, to get rid of potential left-overs from previous call.
|
||||||
|
yylacStack.clear();
|
||||||
|
// Reduce until we encounter a shift and thereby accept the token.
|
||||||
|
yycdebugNnl("LAC: checking lookahead " + yytoken.getName() + ":");
|
||||||
|
int lacTop = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int topState = (yylacStack.isEmpty()
|
||||||
|
? yystack.stateAt(lacTop)
|
||||||
|
: yylacStack.lastElement());
|
||||||
|
int yyrule = yypact_[topState];
|
||||||
|
if (yyPactValueIsDefault(yyrule)
|
||||||
|
|| (yyrule += yytoken.getCode()) < 0 || YYLAST_ < yyrule
|
||||||
|
|| yycheck_[yyrule] != yytoken.getCode())
|
||||||
|
{
|
||||||
|
// Use the default action.
|
||||||
|
yyrule = yydefact_[+topState];
|
||||||
|
if (yyrule == 0) {
|
||||||
|
yycdebug(" Err");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use the action from yytable.
|
||||||
|
yyrule = yytable_[yyrule];
|
||||||
|
if (yyTableValueIsError(yyrule)) {
|
||||||
|
yycdebug(" Err");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (0 < yyrule) {
|
||||||
|
yycdebug(" S" + yyrule);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
yyrule = -yyrule;
|
||||||
|
}
|
||||||
|
// By now we know we have to simulate a reduce.
|
||||||
|
yycdebugNnl(" R" + (yyrule - 1));
|
||||||
|
// Pop the corresponding number of values from the stack.
|
||||||
|
{
|
||||||
|
int yylen = yyr2_[yyrule];
|
||||||
|
// First pop from the LAC stack as many tokens as possible.
|
||||||
|
int lacSize = yylacStack.size();
|
||||||
|
if (yylen < lacSize) {
|
||||||
|
yylacStack.setSize(lacSize - yylen);
|
||||||
|
yylen = 0;
|
||||||
|
} else if (lacSize != 0) {
|
||||||
|
yylacStack.clear();
|
||||||
|
yylen -= lacSize;
|
||||||
|
}
|
||||||
|
// Only afterwards look at the main stack.
|
||||||
|
// We simulate popping elements by incrementing lacTop.
|
||||||
|
lacTop += yylen;
|
||||||
|
}
|
||||||
|
// Keep topState in sync with the updated stack.
|
||||||
|
topState = (yylacStack.isEmpty()
|
||||||
|
? yystack.stateAt(lacTop)
|
||||||
|
: yylacStack.lastElement());
|
||||||
|
// Push the resulting state of the reduction.
|
||||||
|
int state = yyLRGotoState(topState, yyr1_[yyrule]);
|
||||||
|
yycdebugNnl(" G" + state);
|
||||||
|
yylacStack.add(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Establish the initial context if no initial context currently exists.
|
||||||
|
* \returns true iff the token will be eventually shifted.
|
||||||
|
*/
|
||||||
|
boolean yylacEstablish(YYStack yystack, SymbolKind yytoken) {
|
||||||
|
/* Establish the initial context for the current lookahead if no initial
|
||||||
|
context is currently established.
|
||||||
|
|
||||||
|
We define a context as a snapshot of the parser stacks. We define
|
||||||
|
the initial context for a lookahead as the context in which the
|
||||||
|
parser initially examines that lookahead in order to select a
|
||||||
|
syntactic action. Thus, if the lookahead eventually proves
|
||||||
|
syntactically unacceptable (possibly in a later context reached via a
|
||||||
|
series of reductions), the initial context can be used to determine
|
||||||
|
the exact set of tokens that would be syntactically acceptable in the
|
||||||
|
lookahead's place. Moreover, it is the context after which any
|
||||||
|
further semantic actions would be erroneous because they would be
|
||||||
|
determined by a syntactically unacceptable token.
|
||||||
|
|
||||||
|
yylacEstablish should be invoked when a reduction is about to be
|
||||||
|
performed in an inconsistent state (which, for the purposes of LAC,
|
||||||
|
includes consistent states that don't know they're consistent because
|
||||||
|
their default reductions have been disabled).
|
||||||
|
|
||||||
|
For parse.lac=full, the implementation of yylacEstablish is as
|
||||||
|
follows. If no initial context is currently established for the
|
||||||
|
current lookahead, then check if that lookahead can eventually be
|
||||||
|
shifted if syntactic actions continue from the current context. */
|
||||||
|
if (yylacEstablished) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
yycdebug("LAC: initial context established for " + yytoken.getName());
|
||||||
|
yylacEstablished = true;
|
||||||
|
return yylacCheck(yystack, yytoken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Discard any previous initial lookahead context because of event.
|
||||||
|
* \param event the event which caused the lookahead to be discarded.
|
||||||
|
* Only used for debbuging output. */
|
||||||
|
void yylacDiscard(String event) {
|
||||||
|
/* Discard any previous initial lookahead context because of Event,
|
||||||
|
which may be a lookahead change or an invalidation of the currently
|
||||||
|
established initial context for the current lookahead.
|
||||||
|
|
||||||
|
The most common example of a lookahead change is a shift. An example
|
||||||
|
of both cases is syntax error recovery. That is, a syntax error
|
||||||
|
occurs when the lookahead is syntactically erroneous for the
|
||||||
|
currently established initial context, so error recovery manipulates
|
||||||
|
the parser stacks to try to find a new initial context in which the
|
||||||
|
current lookahead is syntactically acceptable. If it fails to find
|
||||||
|
such a context, it discards the lookahead. */
|
||||||
|
if (yylacEstablished) {
|
||||||
|
yycdebug("LAC: initial context discarded due to " + event);
|
||||||
|
yylacEstablished = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The stack for LAC.
|
||||||
|
* Logically, the yylacStack's lifetime is confined to the function
|
||||||
|
* yylacCheck. We just store it as a member of this class to hold
|
||||||
|
* on to the memory and to avoid frequent reallocations.
|
||||||
|
*/
|
||||||
|
Vector<Integer> yylacStack;
|
||||||
|
/** Whether an initial LAC context was established. */
|
||||||
|
boolean yylacEstablished;
|
||||||
|
]])[
|
||||||
|
|
||||||
]b4_parse_error_bmatch(
|
]b4_parse_error_bmatch(
|
||||||
[detailed\|verbose], [[
|
[detailed\|verbose], [[
|
||||||
private int yysyntaxErrorArguments(Context yyctx, SymbolKind[] yyarg, int yyargn) {
|
private int yysyntaxErrorArguments(Context yyctx, SymbolKind[] yyarg, int yyargn) {
|
||||||
|
|||||||
@@ -23,11 +23,17 @@
|
|||||||
%define api.parser.public
|
%define api.parser.public
|
||||||
%define api.push-pull push
|
%define api.push-pull push
|
||||||
|
|
||||||
|
// Customized syntax error messages (see reportSyntaxError)...
|
||||||
%define parse.error custom
|
%define parse.error custom
|
||||||
%define parse.trace
|
|
||||||
|
|
||||||
|
// ... with locations...
|
||||||
%locations
|
%locations
|
||||||
|
|
||||||
|
// ... and accurate list of expected tokens.
|
||||||
|
%define parse.lac full
|
||||||
|
|
||||||
|
%define parse.trace
|
||||||
|
|
||||||
%code imports {
|
%code imports {
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
Reference in New Issue
Block a user