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
There are now two examples in examples/java: a very simple calculator, and
one that tracks locations to provide accurate error messages.
There are now examples/java: a very simple calculator, and a more complete
one (push-parser, location tracking, and debug traces).
The lexcalc example (a simple example in C based on Flex and Bison) now
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.
** Java
*** Examples
Have an example with a push parser. Use autocompletion in that case.
*** calc.at
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
* and ending positions.
*/
public class ]b4_location_type[ {
public static class ]b4_location_type[ {
/**
* The first, inclusive, position in the range.
*/
@@ -182,8 +182,7 @@ import java.text.MessageFormat;
]b4_token_enums[
/** Deprecated, use ]b4_symbol(0, id)[ instead. */
public static final int EOF = ]b4_symbol(0, id)[;
]b4_locations_if([[
]b4_pull_if([b4_locations_if([[
/**
* Method to retrieve the beginning position of the last scanned token.
* @@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.
*/
int yylex()]b4_maybe_throws([b4_lex_throws])[;
]])[
/**
* 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;
}
]b4_locations_if([
]b4_locations_if([[
/**
* Push parse given input from an external lexer.
*
@@ -842,11 +841,10 @@ b4_dollar_popdef[]dnl
*
* @@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])])
{
return push_parse(yylextoken, yylexval, new b4_location_type (yylexpos));
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));
}
])[]])[
]])])[
]b4_both_if([[
/**
@@ -857,21 +855,18 @@ b4_dollar_popdef[]dnl
* @@return <tt>true</tt> if the parsing succeeds. Note that this does not
* imply that there were no syntax errors.
*/
public boolean parse()]b4_maybe_throws([b4_list2([b4_lex_throws], [b4_throws])])[
{
if (yylexer == null)
throw new NullPointerException("Null Lexer");
int status;
do {
int token = yylexer.yylex();
]b4_yystype[ lval = yylexer.getLVal();
]b4_locations_if([dnl
b4_location_type yyloc = new b4_location_type (yylexer.getStartPos (),
yylexer.getEndPos ());])[]b4_locations_if([[
status = push_parse(token,lval,yyloc);]], [[
status = push_parse(token,lval);]])[
} while (status == YYPUSH_MORE);
return (status == YYACCEPT);
public boolean parse()]b4_maybe_throws([b4_list2([b4_lex_throws], [b4_throws])])[ {
if (yylexer == null)
throw new NullPointerException("Null Lexer");
int status;
do {
int token = yylexer.yylex();
]b4_yystype[ lval = yylexer.getLVal();]b4_locations_if([[
]b4_location_type[ yyloc = new ]b4_location_type[(yylexer.getStartPos(), yylexer.getEndPos());
status = push_parse(token, lval, yyloc);]], [[
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
@deftypemethod {Lexer} {int} yylex ()
Return the next token. Its type is the return value, its semantic
value and location are saved and returned by the their methods in the
interface.
Return the next token. Its type is the return value, its semantic value and
location are saved and returned by the their methods in the interface. Not
needed for push-only parsers.
Use @samp{%define lex_throws} to specify any uncaught exceptions.
Default is @code{java.io.IOException}.
@@ -13353,7 +13353,7 @@ Default is @code{java.io.IOException}.
@deftypemethodx {Lexer} {Position} getEndPos ()
Return respectively the first position of the last token that @code{yylex}
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
share the same Position boundaries.
@@ -13363,7 +13363,8 @@ The return type can be changed using @code{%define api.position.type
@end deftypemethod
@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
@{@var{class-name}@}}.

View File

@@ -9,7 +9,7 @@ afterwards.
The usual calculator, a very simple version.
## 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.public
%define api.push-pull push
%define parse.error custom
%define parse.trace
@@ -20,12 +21,19 @@
%code {
public static void main(String[] args) throws IOException {
CalcLexer l = new CalcLexer(System.in);
Calc p = new Calc(l);
CalcLexer scanner = new CalcLexer(System.in);
Calc parser = new Calc(scanner);
for (String arg : args)
if (arg.equals("-p"))
p.setDebugLevel(1);
if (!p.parse())
parser.setDebugLevel(1);
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);
}
@@ -105,12 +113,12 @@ class CalcLexer implements Calc.Lexer {
Position start = new Position(1, 0);
Position end = new Position(1, 0);
public Position getStartPos() {
return new Position(start);
}
public Position getEndPos() {
return new Position(end);
/**
* The location of the last token read.
* Implemented with getStartPos and getEndPos in pull parsers.
*/
public Calc.Location getLocation() {
return new Calc.Location(new Position(start), new Position(end));
}
/**
@@ -150,11 +158,17 @@ class CalcLexer implements Calc.Lexer {
Integer yylval;
public Object getLVal() {
/**
* The value of the last token read. Called getLVal in pull parsers.
*/
public Object getValue() {
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());
int ttype = st.nextToken();
end.set(reader.getPosition());
@@ -170,7 +184,7 @@ class CalcLexer implements Calc.Lexer {
end.set(reader.getPreviousPosition());
return NUM;
case ' ': case '\t':
return yylex();
return getToken();
case '!':
return BANG;
case '+':