Hi Dennis, hi Paolo, I am not a Java programmer, so I'd like to have your acknowledgement on a change in the patch below: the Location inner class of the parser was not a static class, and I don't see why. It certainly makes it easier for a push parser that it is static, but maybe I'm missing something. Also, I do not see if that change can have impact on (realistic) user code.
I also relaxed the constraints over Lexer in the case of purely-push parsers: there's no reason to ask for yylex, getLVal and get(Start|End)Pos. Thanks in advance! commit 34a85e670b11fe21813e00d89ecbd761ab2d5caf Author: Akim Demaille <[email protected]> Date: Sat May 2 09:01:34 2020 +0200 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. diff --git a/NEWS b/NEWS index 389448e1..05185c75 100644 --- a/NEWS +++ b/NEWS @@ -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. diff --git a/TODO b/TODO index e2200fe1..31f890e8 100644 --- a/TODO +++ b/TODO @@ -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). diff --git a/data/skeletons/lalr1.java b/data/skeletons/lalr1.java index 8536359b..1baa497d 100644 --- a/data/skeletons/lalr1.java +++ b/data/skeletons/lalr1.java @@ -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; } ]])[ diff --git a/doc/bison.texi b/doc/bison.texi index 32d41924..734ca088 100644 --- a/doc/bison.texi +++ b/doc/bison.texi @@ -13336,9 +13336,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}. @@ -13348,7 +13348,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. @@ -13358,7 +13358,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}@}}. diff --git a/examples/java/README.md b/examples/java/README.md index 4be92c80..25b7f27c 100644 --- a/examples/java/README.md +++ b/examples/java/README.md @@ -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. <!--- diff --git a/examples/java/calc/Calc.y b/examples/java/calc/Calc.y index 5a5d2048..4bb07b5f 100644 --- a/examples/java/calc/Calc.y +++ b/examples/java/calc/Calc.y @@ -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 '+':
