| /* Parser and scanner for calc in Java. -*- Java -*- |
| |
| Copyright (C) 2018-2022, 2025 Free Software Foundation, Inc. |
| |
| This file is part of Bison, the GNU Compiler Compiler. |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
| |
| %language "Java" |
| |
| %define api.parser.class {Calc} |
| %define api.parser.public |
| %define api.push-pull push |
| |
| // Customized syntax error messages (see reportSyntaxError)... |
| %define parse.error custom |
| |
| // ... with locations... |
| %locations |
| |
| // ... and accurate list of expected tokens. |
| %define parse.lac full |
| |
| %define parse.trace |
| |
| %code imports { |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.StreamTokenizer; |
| import java.nio.CharBuffer; |
| } |
| |
| %code { |
| public static void main(String[] args) throws IOException { |
| CalcLexer scanner = new CalcLexer(System.in); |
| Calc parser = new Calc(scanner); |
| for (String arg : args) |
| if (arg.equals("-p")) |
| 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); |
| } |
| |
| static String i18n(String s) { |
| return s; |
| } |
| } |
| |
| /* Bison Declarations */ |
| %token |
| BANG "!" |
| PLUS "+" |
| MINUS "-" |
| STAR "*" |
| SLASH "/" |
| CARET "^" |
| LPAREN "(" |
| RPAREN ")" |
| EQUAL "=" |
| EOL _("end of line") |
| <Integer> |
| NUM _("number") |
| %type <Integer> exp |
| |
| %nonassoc "=" /* comparison */ |
| %left "-" "+" |
| %left "*" "/" |
| %precedence NEG /* negation--unary minus */ |
| %right "^" /* exponentiation */ |
| |
| /* Grammar follows */ |
| %% |
| input: |
| line |
| | input line |
| ; |
| |
| line: |
| EOL |
| | exp EOL { System.out.println($exp); } |
| | error EOL |
| ; |
| |
| exp: |
| NUM { $$ = $1; } |
| | exp "=" exp |
| { |
| if ($1.intValue() != $3.intValue()) |
| yyerror(@$, "calc: error: " + $1 + " != " + $3); |
| } |
| | exp "+" exp { $$ = $1 + $3; } |
| | exp "-" exp { $$ = $1 - $3; } |
| | exp "*" exp { $$ = $1 * $3; } |
| | exp "/" exp { $$ = $1 / $3; } |
| | "-" exp %prec NEG { $$ = -$2; } |
| | exp "^" exp { $$ = (int) Math.pow($1, $3); } |
| | "(" exp ")" { $$ = $2; } |
| | "(" error ")" { $$ = 1111; } |
| | "!" { $$ = 0; return YYERROR; } |
| | "-" error { $$ = 0; return YYERROR; } |
| ; |
| |
| %% |
| class CalcLexer implements Calc.Lexer { |
| |
| StreamTokenizer st; |
| PositionReader reader; |
| |
| public CalcLexer(InputStream is) { |
| reader = new PositionReader(new InputStreamReader(is)); |
| st = new StreamTokenizer(reader); |
| st.resetSyntax(); |
| st.eolIsSignificant(true); |
| st.wordChars('0', '9'); |
| } |
| |
| Position start = new Position(1, 0); |
| Position end = new Position(1, 0); |
| |
| /** |
| * 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)); |
| } |
| |
| /** |
| * Build and emit a syntax error message. |
| */ |
| public void reportSyntaxError(Calc.Context ctx) { |
| System.err.print(ctx.getLocation() + ": syntax error"); |
| { |
| final int TOKENMAX = 10; |
| Calc.SymbolKind[] arg = new Calc.SymbolKind[TOKENMAX]; |
| int n = ctx.getExpectedTokens(arg, TOKENMAX); |
| for (int i = 0; i < n; ++i) |
| System.err.print((i == 0 ? ": expected " : " or ") |
| + arg[i].getName()); |
| } |
| { |
| Calc.SymbolKind lookahead = ctx.getToken(); |
| if (lookahead != null) |
| System.err.print(" before " + lookahead.getName()); |
| } |
| System.err.println(""); |
| } |
| |
| /** |
| * Emit an error referring to the given location in a user-defined way. |
| * |
| * @@param loc The location of the element to which the |
| * error message is related. |
| * @@param msg The string for the error message. |
| */ |
| public void yyerror(Calc.Location loc, String msg) { |
| if (loc == null) |
| System.err.println(msg); |
| else |
| System.err.println(loc + ": " + msg); |
| } |
| |
| Integer yylval; |
| |
| /** |
| * The value of the last token read. Called getLVal in pull parsers. |
| */ |
| public Object getValue() { |
| return yylval; |
| } |
| |
| /** |
| * 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()); |
| switch (ttype) { |
| case StreamTokenizer.TT_EOF: |
| return YYEOF; |
| case StreamTokenizer.TT_EOL: |
| end.line += 1; |
| end.column = 0; |
| return EOL; |
| case StreamTokenizer.TT_WORD: |
| yylval = Integer.parseInt(st.sval); |
| end.set(reader.getPreviousPosition()); |
| return NUM; |
| case ' ': case '\t': |
| return getToken(); |
| case '!': |
| return BANG; |
| case '+': |
| return PLUS; |
| case '-': |
| return MINUS; |
| case '*': |
| return STAR; |
| case '/': |
| return SLASH; |
| case '^': |
| return CARET; |
| case '(': |
| return LPAREN; |
| case ')': |
| return RPAREN; |
| case '=': |
| return EQUAL; |
| default: |
| throw new AssertionError("invalid character: " + ttype); |
| } |
| } |
| } |
| |
| /** |
| * A class defining a point in the input. |
| */ |
| class Position { |
| public int line = 1; |
| public int column = 1; |
| |
| public Position() { |
| line = 1; |
| column = 1; |
| } |
| |
| public Position(int l, int t) { |
| line = l; |
| column = t; |
| } |
| |
| public Position(Position p) { |
| line = p.line; |
| column = p.column; |
| } |
| |
| public void set(Position p) { |
| line = p.line; |
| column = p.column; |
| } |
| |
| public boolean equals(Position l) { |
| return l.line == line && l.column == column; |
| } |
| |
| public String toString() { |
| return Integer.toString(line) + "." + Integer.toString(column); |
| } |
| |
| public int line() { |
| return line; |
| } |
| |
| public int column() { |
| return column; |
| } |
| } |
| |
| /** |
| * A Stream reader that keeps track of the current Position. |
| */ |
| class PositionReader extends BufferedReader { |
| |
| private Position position = new Position(); |
| // Position before the latest call to "read", i.e. position |
| // of the last character of the current token. |
| private Position previousPosition = new Position(); |
| |
| public PositionReader(Reader reader) { |
| super(reader); |
| } |
| |
| public int read() throws IOException { |
| previousPosition.set(position); |
| int res = super.read(); |
| if (res > -1) { |
| char c = (char) res; |
| if (c == '\r' || c == '\n') { |
| position.line += 1; |
| position.column = 1; |
| } else { |
| position.column += 1; |
| } |
| } |
| return res; |
| } |
| |
| public Position getPosition() { |
| return position; |
| } |
| |
| public Position getPreviousPosition() { |
| return previousPosition; |
| } |
| } |