java: demonstrate push parsers

* data/skeletons/lalr1.java (Location): Make it a static class.
(Lexer.yylex, Lexer.getLVal, Lexer.getStartPos, Lexer.getEndPos):
These are not needed in push parsers.
* examples/java/calc/Calc.y: Demonstrate push parsers in the Java.
* doc/bison.texi: Push parsers have been supported for a long time,
remove incorrect statements stating the opposite.
This commit is contained in:
Akim Demaille
2020-05-02 09:01:34 +02:00
parent ac2ba46053
commit 13a1537dba
6 changed files with 55 additions and 48 deletions

4
NEWS
View File

@@ -216,8 +216,8 @@ GNU Bison NEWS
*** Examples *** Examples
There are now two examples in examples/java: a very simple calculator, and There are now examples/java: a very simple calculator, and a more complete
one that tracks locations to provide accurate error messages. one (push-parser, location tracking, and debug traces).
The lexcalc example (a simple example in C based on Flex and Bison) now The lexcalc example (a simple example in C based on Flex and Bison) now
also demonstrates location tracking. also demonstrates location tracking.

3
TODO
View File

@@ -25,9 +25,6 @@ should be updated to not use YYERRCODE. Returning an undef token is good
enough. enough.
** Java ** Java
*** Examples
Have an example with a push parser. Use autocompletion in that case.
*** calc.at *** calc.at
Stop hard-coding "Calc". Adjust local.at (look for FIXME). Stop hard-coding "Calc". Adjust local.at (look for FIXME).

View File

@@ -121,7 +121,7 @@ import java.text.MessageFormat;
* Locations represent a part of the input through the beginning * Locations represent a part of the input through the beginning
* and ending positions. * and ending positions.
*/ */
public class ]b4_location_type[ { public static class ]b4_location_type[ {
/** /**
* The first, inclusive, position in the range. * The first, inclusive, position in the range.
*/ */
@@ -182,8 +182,7 @@ import java.text.MessageFormat;
]b4_token_enums[ ]b4_token_enums[
/** Deprecated, use ]b4_symbol(0, id)[ instead. */ /** Deprecated, use ]b4_symbol(0, id)[ instead. */
public static final int EOF = ]b4_symbol(0, id)[; public static final int EOF = ]b4_symbol(0, id)[;
]b4_pull_if([b4_locations_if([[
]b4_locations_if([[
/** /**
* Method to retrieve the beginning position of the last scanned token. * Method to retrieve the beginning position of the last scanned token.
* @@return the position at which the last scanned token starts. * @@return the position at which the last scanned token starts.
@@ -209,7 +208,7 @@ import java.text.MessageFormat;
* @@return the token identifier corresponding to the next token. * @@return the token identifier corresponding to the next token.
*/ */
int yylex()]b4_maybe_throws([b4_lex_throws])[; int yylex()]b4_maybe_throws([b4_lex_throws])[;
]])[
/** /**
* Emit an error]b4_locations_if([ referring to the given location])[in a user-defined way. * Emit an error]b4_locations_if([ referring to the given location])[in a user-defined way.
* *
@@ -832,7 +831,7 @@ b4_dollar_popdef[]dnl
this.push_parse_initialized = true; this.push_parse_initialized = true;
} }
]b4_locations_if([ ]b4_locations_if([[
/** /**
* Push parse given input from an external lexer. * Push parse given input from an external lexer.
* *
@@ -842,11 +841,10 @@ b4_dollar_popdef[]dnl
* *
* @@return <tt>YYACCEPT, YYABORT, YYPUSH_MORE</tt> * @@return <tt>YYACCEPT, YYABORT, YYPUSH_MORE</tt>
*/ */
public int push_parse(int yylextoken, b4_yystype yylexval, b4_position_type yylexpos)b4_maybe_throws([b4_list2([b4_lex_throws], [b4_throws])]) public int push_parse(int yylextoken, ]b4_yystype[ yylexval, ]b4_position_type[ yylexpos)]b4_maybe_throws([b4_list2([b4_lex_throws], [b4_throws])])[ {
{ return push_parse(yylextoken, yylexval, new ]b4_location_type[(yylexpos));
return push_parse(yylextoken, yylexval, new b4_location_type (yylexpos));
} }
])[]])[ ]])])[
]b4_both_if([[ ]b4_both_if([[
/** /**
@@ -857,21 +855,18 @@ b4_dollar_popdef[]dnl
* @@return <tt>true</tt> if the parsing succeeds. Note that this does not * @@return <tt>true</tt> if the parsing succeeds. Note that this does not
* imply that there were no syntax errors. * imply that there were no syntax errors.
*/ */
public boolean parse()]b4_maybe_throws([b4_list2([b4_lex_throws], [b4_throws])])[ public boolean parse()]b4_maybe_throws([b4_list2([b4_lex_throws], [b4_throws])])[ {
{ if (yylexer == null)
if (yylexer == null) throw new NullPointerException("Null Lexer");
throw new NullPointerException("Null Lexer"); int status;
int status; do {
do { int token = yylexer.yylex();
int token = yylexer.yylex(); ]b4_yystype[ lval = yylexer.getLVal();]b4_locations_if([[
]b4_yystype[ lval = yylexer.getLVal(); ]b4_location_type[ yyloc = new ]b4_location_type[(yylexer.getStartPos(), yylexer.getEndPos());
]b4_locations_if([dnl status = push_parse(token, lval, yyloc);]], [[
b4_location_type yyloc = new b4_location_type (yylexer.getStartPos (), status = push_parse(token, lval);]])[
yylexer.getEndPos ());])[]b4_locations_if([[ } while (status == YYPUSH_MORE);
status = push_parse(token,lval,yyloc);]], [[ return status == YYACCEPT;
status = push_parse(token,lval);]])[
} while (status == YYPUSH_MORE);
return (status == YYACCEPT);
} }
]])[ ]])[

View File

@@ -13341,9 +13341,9 @@ changed using @code{%define api.location.type @{@var{class-name}@}}.
@end deftypemethod @end deftypemethod
@deftypemethod {Lexer} {int} yylex () @deftypemethod {Lexer} {int} yylex ()
Return the next token. Its type is the return value, its semantic Return the next token. Its type is the return value, its semantic value and
value and location are saved and returned by the their methods in the location are saved and returned by the their methods in the interface. Not
interface. needed for push-only parsers.
Use @samp{%define lex_throws} to specify any uncaught exceptions. Use @samp{%define lex_throws} to specify any uncaught exceptions.
Default is @code{java.io.IOException}. Default is @code{java.io.IOException}.
@@ -13353,7 +13353,7 @@ Default is @code{java.io.IOException}.
@deftypemethodx {Lexer} {Position} getEndPos () @deftypemethodx {Lexer} {Position} getEndPos ()
Return respectively the first position of the last token that @code{yylex} Return respectively the first position of the last token that @code{yylex}
returned, and the first position beyond it. These methods are not needed returned, and the first position beyond it. These methods are not needed
unless location tracking is active. unless location tracking and pull parsing are active.
They should return new objects for each call, to avoid that all the symbol They should return new objects for each call, to avoid that all the symbol
share the same Position boundaries. share the same Position boundaries.
@@ -13363,7 +13363,8 @@ The return type can be changed using @code{%define api.position.type
@end deftypemethod @end deftypemethod
@deftypemethod {Lexer} {Object} getLVal () @deftypemethod {Lexer} {Object} getLVal ()
Return the semantic value of the last token that yylex returned. Return the semantic value of the last token that yylex returned. Not needed
for push-only parsers.
The return type can be changed using @samp{%define api.value.type The return type can be changed using @samp{%define api.value.type
@{@var{class-name}@}}. @{@var{class-name}@}}.

View File

@@ -9,7 +9,7 @@ afterwards.
The usual calculator, a very simple version. The usual calculator, a very simple version.
## calc/Calc.y ## calc/Calc.y
The calculator, but with location tracking and debug traces. The calculator, but with location tracking, debug traces, and a push parser.
<!--- <!---

View File

@@ -2,6 +2,7 @@
%define api.parser.class {Calc} %define api.parser.class {Calc}
%define api.parser.public %define api.parser.public
%define api.push-pull push
%define parse.error custom %define parse.error custom
%define parse.trace %define parse.trace
@@ -20,12 +21,19 @@
%code { %code {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
CalcLexer l = new CalcLexer(System.in); CalcLexer scanner = new CalcLexer(System.in);
Calc p = new Calc(l); Calc parser = new Calc(scanner);
for (String arg : args) for (String arg : args)
if (arg.equals("-p")) if (arg.equals("-p"))
p.setDebugLevel(1); parser.setDebugLevel(1);
if (!p.parse()) int status;
do {
int token = scanner.getToken();
Object lval = scanner.getValue();
Calc.Location yyloc = scanner.getLocation();
status = parser.push_parse(token, lval, yyloc);
} while (status == Calc.YYPUSH_MORE);
if (status != Calc.YYACCEPT)
System.exit(1); System.exit(1);
} }
@@ -105,12 +113,12 @@ class CalcLexer implements Calc.Lexer {
Position start = new Position(1, 0); Position start = new Position(1, 0);
Position end = new Position(1, 0); Position end = new Position(1, 0);
public Position getStartPos() { /**
return new Position(start); * The location of the last token read.
} * Implemented with getStartPos and getEndPos in pull parsers.
*/
public Position getEndPos() { public Calc.Location getLocation() {
return new Position(end); return new Calc.Location(new Position(start), new Position(end));
} }
/** /**
@@ -150,11 +158,17 @@ class CalcLexer implements Calc.Lexer {
Integer yylval; Integer yylval;
public Object getLVal() { /**
* The value of the last token read. Called getLVal in pull parsers.
*/
public Object getValue() {
return yylval; return yylval;
} }
public int yylex() throws IOException { /**
* Fetch the next token. Called yylex in pull parsers.
*/
public int getToken() throws IOException {
start.set(reader.getPosition()); start.set(reader.getPosition());
int ttype = st.nextToken(); int ttype = st.nextToken();
end.set(reader.getPosition()); end.set(reader.getPosition());
@@ -170,7 +184,7 @@ class CalcLexer implements Calc.Lexer {
end.set(reader.getPreviousPosition()); end.set(reader.getPreviousPosition());
return NUM; return NUM;
case ' ': case '\t': case ' ': case '\t':
return yylex(); return getToken();
case '!': case '!':
return BANG; return BANG;
case '+': case '+':