I wrote a simple mathematical expression converter with Jay: Yacc for Java for my study.
http://www.cs.rit.edu/~ats/lp-2002-2/html/skript-23.html
The sample code in this page is a mathematical expression interpreter. You can calculate four arithmetic operations and more like 1 + 2 * 3
to 7
. My code which will be described in this post converts expressions into reverse Polish notation ones like 1 + 2 * 3
to 1 2 + 3 *
.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%{ | |
// vim: set ft=java : | |
import java.io.InputStreamReader; | |
import java.io.IOException; | |
import java.io.Reader; | |
import java.io.ObjectOutputStream; | |
import java.io.StreamTokenizer; | |
import java.util.ArrayList; | |
import jay.yydebug.yyAnim; | |
import jay.yydebug.yyDebugAdapter; | |
abstract class Node { | |
} | |
class Operator extends Node { | |
private Node left, right; | |
protected String op; | |
protected Operator(Node left, Node right) { | |
this.left = left; | |
this.right = right; | |
} | |
public String toString() { | |
return String.format("%s %s %s", left, right, op); | |
} | |
} | |
class Add extends Operator { | |
public Add(Node left, Node right) { | |
super(left, right); | |
this.op = "+"; | |
} | |
} | |
class Sub extends Operator { | |
public Sub(Node left, Node right) { | |
super(left, right); | |
this.op = "-"; | |
} | |
} | |
class Mul extends Operator { | |
public Mul(Node left, Node right) { | |
super(left, right); | |
this.op = "*"; | |
} | |
} | |
class Div extends Operator { | |
public Div(Node left, Node right) { | |
super(left, right); | |
this.op = "/"; | |
} | |
} | |
class Mod extends Operator { | |
public Mod(Node left, Node right) { | |
super(left, right); | |
this.op = "%"; | |
} | |
} | |
class Value extends Node { | |
private Object value; | |
public Value(Object value) { | |
this.value = value; | |
} | |
public String toString() { | |
return value.toString(); | |
} | |
} | |
/** recognizes, stores, and evaluates arithmetic expressions | |
using a parser generated with jay. | |
*/ | |
public class Expr { | |
/** node factory. | |
*/ | |
//protected static VM vm = new VM(); | |
protected static Node root; | |
public static void main(String args []) throws Exception { | |
Object trace = null; | |
if (args.length > 0) | |
trace = new yyAnim("Expr", Integer.parseInt(args[0])); | |
Expr parser = new Expr(); | |
Scanner scanner = new Scanner(new InputStreamReader(System.in)); | |
while (scanner.ttype != scanner.TT_EOF) | |
try { | |
Node n = (Node)parser.yyparse(scanner, trace); | |
if (n != null) | |
System.out.println(n); | |
} catch (yyException ye) { System.err.println(scanner+": "+ye); } | |
} | |
%} | |
%token <String> Int, Real | |
%type <Node> line, sum, product, term | |
%% | |
line : sum // $$ = $1 | |
| /* null */ { $$ = null; } | |
sum : product // $$ = $1 | |
| sum '+' product { $$ = new Add($1, $3); } | |
| sum '-' product { $$ = new Sub($1, $3); } | |
product : term // $$ = $1 | |
| product '*' term { $$ = new Mul($1, $3); } | |
| product '/' term { $$ = new Div($1, $3); } | |
| product '%' term { $$ = new Mod($1, $3); } | |
term : '+' term { $$ = $2; } | |
| '-' term { $$ = $2; } // FIXME | |
| '(' sum ')' { $$ = $2; } | |
| Int { $$ = new Value(Integer.parseInt($1)); } | |
| Real { $$ = new Value(Double.parseDouble($1)); } | |
%% | |
/** lexical analyzer for arithmetic expressions. | |
*/ | |
public static class Scanner extends StreamTokenizer implements yyInput { | |
public Scanner (Reader r) { | |
super(r); | |
resetSyntax(); | |
commentChar('#'); // comments from # to end-of-line | |
wordChars('0', '9'); // parse decimal numbers as words | |
wordChars('.', '.'); | |
whitespaceChars(0, ' '); // ignore control-* and space | |
eolIsSignificant(true); // need '\n' | |
} | |
/** moves to next input token. | |
Consumes end of line and pretends (once) that it is end of file. | |
@return false at end of file and once at each end of line. | |
*/ | |
public boolean advance () throws IOException { | |
if (ttype != TT_EOF) nextToken(); | |
return ttype != TT_EOF && ttype != TT_EOL; | |
} | |
/** determines current input, sets value to String for Int and Real. | |
@return Int, Real or token's character value. | |
*/ | |
public int token () { | |
switch (ttype) { | |
case TT_EOL: | |
case TT_EOF: assert false; // should not happen | |
case TT_WORD: value = sval; | |
return sval.indexOf(".") < 0 ? Int : Real; | |
default: value = null; | |
return ttype; | |
} | |
} | |
/** value associated with current input. | |
*/ | |
protected Object value; | |
/** produces value associated with current input. | |
@return value. | |
*/ | |
public Object value () { | |
return value; | |
} | |
} | |
} |