Clean way to simplify a binary expression tree - java

The goal of my program is to display the symbolic derivative of a mathematical expression. After creating a new tree that represents the derivative, it is likely that I will be left with redundant terms.
For example, the following tree is not simplified.
Example of binary expression tree
The tree 0 + 5 * (x * 5) can be rewritten as 25 * x
My program uses many, many if and else blocks to reduce the tree by checking for constants multiplied by constants, etc. Then, it rearranges the sub tree accordingly.
Here is a tiny portion of my recursive function that simplifies the tree:
if(root.getVal().equals("*")) {
if(root.getLeftChild().getVal().equals("1")) {
return root.getRightChild();
}
else if(root.getRightChild().getVal().equals("1")) {
return root.getLeftChild();
}
else if(root.getLeftChild().getVal().equals("0")) {
return root.getLeftChild();
}
else if(root.getRightChild().getVal().equals("0")) {
return root.getRightChild();
}
else if(root.getLeftChild().getVal().equals("*")) {
if(root.getRightChild().getType().equals("constant")) {
if(root.getLeftChild().getLeftChild().getType().equals("constant")) { // Ex: (5*x)*6 ==> 30*x
int num1 = Integer.parseInt(root.getRightChild().getVal());
int num2 = Integer.parseInt(root.getLeftChild().getLeftChild().getVal());
OpNode mult = new OpNode("*");
mult.setLeftChild(new ConstNode(String.valueOf(num1 * num2)));
mult.setRightChild(root.getLeftChild().getRightChild());
return mult;
}
...
...
...
...
The function works great, other than the fact that I need to call it a few times to ensure the tree is fully reduced(incase a reduction opens up another reduction possibility). However, it is 200 lines long and growing, which leads me to believe there must be a much better way to do this.

One typical approach to this problem is the visitor pattern. Any time you need to walk a recursive structure, applying logic at each node which depends on the "type" of the node, this pattern is a good tool to have handy.
For this specific problem, and specifically in Java, I'd start by representing your expression "abstract syntax tree" more directly as a type hierarchy.
I've put together a simple example, assuming your AST handles +, -, *, / as well as literal numbers and named variables. I've called my Visitor a Folder---we sometimes use this name for visitor-alikes which replace ("fold") subtrees. (Think: optimization or de-sugaring passes in compilers.)
The trick to handling the "I need to sometimes repeat simplification" is to do a depth-first traversal: all children get fully simplified before we simplify their parents.
Here's the example (disclaimer: I hate Java, so I don't promise this is the most "idiomatic" implementation in the language):
interface Folder {
// we could use the name "fold" for all of these, overloading on the
// argument type, and the dispatch code in each concrete Expression
// class would still do the right thing (selecting an overload using
// the type of "this") --- but this is a little easier to follow
Expression foldBinaryOperation(BinaryOperation expr);
Expression foldUnaryOperation(UnaryOperation expr);
Expression foldNumber(Number expr);
Expression foldVariable(Variable expr);
}
abstract class Expression {
abstract Expression fold(Folder f);
// logic to build a readable representation for testing
abstract String repr();
}
enum BinaryOperator {
PLUS,
MINUS,
MUL,
DIV,
}
enum UnaryOperator {
NEGATE,
}
class BinaryOperation extends Expression {
public BinaryOperation(BinaryOperator operator,
Expression left, Expression right)
{
this.operator = operator;
this.left = left;
this.right = right;
}
public BinaryOperator operator;
public Expression left;
public Expression right;
public Expression fold(Folder f) {
return f.foldBinaryOperation(this);
}
public String repr() {
// parens for clarity
String result = "(" + left.repr();
switch (operator) {
case PLUS:
result += " + ";
break;
case MINUS:
result += " - ";
break;
case MUL:
result += " * ";
break;
case DIV:
result += " / ";
break;
}
result += right.repr() + ")";
return result;
}
}
class UnaryOperation extends Expression {
public UnaryOperation(UnaryOperator operator, Expression operand)
{
this.operator = operator;
this.operand = operand;
}
public UnaryOperator operator;
public Expression operand;
public Expression fold(Folder f) {
return f.foldUnaryOperation(this);
}
public String repr() {
String result = "";
switch (operator) {
case NEGATE:
result = "-";
break;
}
result += operand.repr();
return result;
}
}
class Number extends Expression {
public Number(double value)
{
this.value = value;
}
public double value;
public Expression fold(Folder f) {
return f.foldNumber(this);
}
public String repr() {
return Double.toString(value);
}
}
class Variable extends Expression {
public Variable(String name)
{
this.name = name;
}
public String name;
public Expression fold(Folder f) {
return f.foldVariable(this);
}
public String repr() {
return name;
}
}
// a base class providing "standard" traversal logic (we could have
// made Folder abstract and put these there
class DefaultFolder implements Folder {
public Expression foldBinaryOperation(BinaryOperation expr) {
// recurse into both sides of the binary operation
return new BinaryOperation(
expr.operator, expr.left.fold(this), expr.right.fold(this));
}
public Expression foldUnaryOperation(UnaryOperation expr) {
// recurse into operand
return new UnaryOperation(expr.operator, expr.operand.fold(this));
}
public Expression foldNumber(Number expr) {
// numbers are "terminal": no more recursive structure to walk
return expr;
}
public Expression foldVariable(Variable expr) {
// another non-recursive expression
return expr;
}
}
class Simplifier extends DefaultFolder {
public Expression foldBinaryOperation(BinaryOperation expr) {
// we want to do a depth-first traversal, ensuring that all
// sub-expressions are simplified before their parents...
// ... so begin by invoking the superclass "default"
// traversal logic.
BinaryOperation folded_expr =
// this cast is safe because we know the default fold
// logic never changes the type of the top-level expression
(BinaryOperation)super.foldBinaryOperation(expr);
// now apply our "shallow" simplification logic on the result
switch (folded_expr.operator) {
case PLUS:
// x + 0 => x
if (folded_expr.right instanceof Number
&& ((Number)(folded_expr.right)).value == 0)
return folded_expr.left;
// 0 + x => x
if (folded_expr.left instanceof Number
&& ((Number)(folded_expr.left)).value == 0)
return folded_expr.right;
break;
case MINUS:
// x - 0 => x
if (folded_expr.right instanceof Number
&& ((Number)(folded_expr.right)).value == 0)
return folded_expr.left;
// 0 - x => -x
if (folded_expr.left instanceof Number
&& ((Number)(folded_expr.left)).value == 0) {
// a weird case: we need to construct a UnaryOperator
// representing -right, then simplify it
UnaryOperation minus_right = new UnaryOperation(
UnaryOperator.NEGATE, folded_expr.right);
return foldUnaryOperation(minus_right);
}
break;
case MUL:
// 1 * x => x
if (folded_expr.left instanceof Number
&& ((Number)(folded_expr.left)).value == 1)
return folded_expr.right;
case DIV:
// x * 1 => x
// x / 1 => x
if (folded_expr.right instanceof Number
&& ((Number)(folded_expr.right)).value == 1)
return folded_expr.left;
break;
}
// no rules applied
return folded_expr;
}
public Expression foldUnaryOperation(UnaryOperation expr) {
// as before, go depth-first:
UnaryOperation folded_expr =
// see note in foldBinaryOperation about safety here
(UnaryOperation)super.foldUnaryOperation(expr);
switch (folded_expr.operator) {
case NEGATE:
// --x => x
if (folded_expr.operand instanceof UnaryOperation
&& ((UnaryOperation)folded_expr).operator ==
UnaryOperator.NEGATE)
return ((UnaryOperation)folded_expr.operand).operand;
// -(number) => -number
if (folded_expr.operand instanceof Number)
return new Number(-((Number)(folded_expr.operand)).value);
break;
}
// no rules applied
return folded_expr;
}
// we don't need to implement the other two; the inherited defaults are fine
}
public class Simplify {
public static void main(String[] args) {
Simplifier simplifier = new Simplifier();
Expression[] exprs = new Expression[] {
new BinaryOperation(
BinaryOperator.PLUS,
new Number(0.0),
new Variable("x")
),
new BinaryOperation(
BinaryOperator.PLUS,
new Number(17.3),
new UnaryOperation(
UnaryOperator.NEGATE,
new UnaryOperation(
UnaryOperator.NEGATE,
new BinaryOperation(
BinaryOperator.DIV,
new Number(0.0),
new Number(1.0)
)
)
)
),
};
for (Expression expr: exprs) {
System.out.println("Unsimplified: " + expr.repr());
Expression simplified = expr.fold(simplifier);
System.out.println("Simplified: " + simplified.repr());
}
}
}
And the output:
> java Simplify
Unsimplified: (0.0 + x)
Simplified: x
Unsimplified: (17.3 + --(0.0 / 1.0))
Simplified: 17.3

Related

How to write constructor that will accept 1 of 3 String values that I choose in the case (morning, evening, night)?

this is a subclass and am trying to shifts variable to accept only 1 of 3 values, that's morning evening night am a newbie so please forgive me for such a simple question
public class PartTimeStaffHire extends StaffHire
{
// instance variables - replace the example below with your own
private int workingHour
private double wagesPerHour
private String shifts;
private boolean terminated
This is subclass of StaffHire and accept all this variables and the last one am trying to accept only 1 of this 3 values
/**
* Constructor for objects of class PartTimeStaffHire
*/
public PartTimeStaffHire(int vacancy, String designation1, String typeofjob,
String staffName1, String joinDate, String qualification1, String appointed,
boolean joined, int hoursPerDay, double wagePerHour, String shift)
{
super(vacancy, designation1, typeofjob, staffName1, joinDate,
qualification1, appointed, joined);
workingHour = hoursPerDay;
wagesPerHour = wagePerHour;
shifts = shift;
if( shifts == "morning") {
shifts = "morning";
}
else if(shifts == "evening") {
shifts = "evening";
}
else if(shifts == "night") {
shifts = "night";
}
else {
System.out.println("Choose (morning, evening, night)" );
}
//(morning, evening, night)
}
public String shift()
{
if( shifts == "morning") {
shifts = "morning";
}
else if(shifts == "evening") {
shifts = "evening";
}
else if(shifts == "night") {
shifts = "night";
}
else {
System.out.println("Choose (morning, evening, night)" );
}
return shifts;
}
/**
* An example of a method - replace this comment with your own
*
* #param y a sample parameter for a method
* #return the sum of x and y
*/
public void print()
{
super.print();
System.out.println("The yearly salary is " + wagesPerHour);
System.out.println("Weekly working hours are " + workingHour;
System.out.println("##################" + shifts);
}
}
thanks in advance
If you know the possible values of a type, an enum is what you want to use.
So in this case you can create an enum Shift with the three possible constants:
public enum Shift {
MORNING,
EVENING,
NIGHT;
//this lets you convert a String such as `morning` to an enum constant
//if the enum not not one of the 3 constants (ignoring the case), this returns null. So you can use it to also validate the input.
public static Shift getShiftByName(String name) {
for(Shift s:values()) {
if(s.name().equalsIgnoreCase(name)) {
return s;
}
}
return null;
}
//if you want to convert it back to a String (for output), overwrite toString:
#Override
public String toString() {
//name() returns the constant's name as a String, such as "MORNING".
// since you use it as lowercase, overriding toString here makes sense
return name().toLowerCase();
}
}
You can then use it as
String strShiftInput = "morning";
Shift chosenChift = getShiftByName(strShiftInput);
if(chosenShift == null) {
//invalid input, handle accordingly
}
new PartTimeStaffHire(theOtherParameters, chosenShift);

visitor/listener for loop antlr4 [duplicate]

I'm creating a simple programming language for a school project. I'm using ANTLR 4 to generate a lexer and a parser from my grammar. Until now, I have been using ANTLRs listener pattern to apply the actual functionality of the programming language.
Now I would like to implement if/else statements but I'm not sure that these can actually be implemented when using the listener pattern as ANTLR decides in which order to traverse the parse tree when using listeners and I imagine that the implementation of if/else statements will require jumping around the parse tree depending on which condition in the statement is satisfied.
Can anyone tell me if it will be possible to implement if/else statements using ANTLR or if I will have to implement the visitor pattern myself? Also, can anyone give an extremely simple example of the implementation the statements?
By default, ANTLR 4 generates listeners. But if you give org.antlr.v4.Tool the command line parameter -visitor, ANTLR generates visitor classes for you. These work much like listeners, but give you more control over which (sub) trees are walked/visited. This is particularly useful if you want to exclude certain (sub) trees (like else/if blocks, as in your case). While this can be done using listeners, it's much cleaner to do this with a visitor. Using listeners, you'll need to introduce global variables that keep track if a (sub) tree needs to be evaluated, and which do not.
As it happens to be, I'm working on a small ANTLR 4 tutorial. It's not done yet, but I'll post a small working example that demonstrates the use of these visitor classes and an if statement construct.
1. Grammar
Here's a simple grammar supporting basic expressions, if-, while- and log-statements:
Mu.g4
grammar Mu;
parse
: block EOF
;
block
: stat*
;
stat
: assignment
| if_stat
| while_stat
| log
| OTHER {System.err.println("unknown char: " + $OTHER.text);}
;
assignment
: ID ASSIGN expr SCOL
;
if_stat
: IF condition_block (ELSE IF condition_block)* (ELSE stat_block)?
;
condition_block
: expr stat_block
;
stat_block
: OBRACE block CBRACE
| stat
;
while_stat
: WHILE expr stat_block
;
log
: LOG expr SCOL
;
expr
: expr POW<assoc=right> expr #powExpr
| MINUS expr #unaryMinusExpr
| NOT expr #notExpr
| expr op=(MULT | DIV | MOD) expr #multiplicationExpr
| expr op=(PLUS | MINUS) expr #additiveExpr
| expr op=(LTEQ | GTEQ | LT | GT) expr #relationalExpr
| expr op=(EQ | NEQ) expr #equalityExpr
| expr AND expr #andExpr
| expr OR expr #orExpr
| atom #atomExpr
;
atom
: OPAR expr CPAR #parExpr
| (INT | FLOAT) #numberAtom
| (TRUE | FALSE) #booleanAtom
| ID #idAtom
| STRING #stringAtom
| NIL #nilAtom
;
OR : '||';
AND : '&&';
EQ : '==';
NEQ : '!=';
GT : '>';
LT : '<';
GTEQ : '>=';
LTEQ : '<=';
PLUS : '+';
MINUS : '-';
MULT : '*';
DIV : '/';
MOD : '%';
POW : '^';
NOT : '!';
SCOL : ';';
ASSIGN : '=';
OPAR : '(';
CPAR : ')';
OBRACE : '{';
CBRACE : '}';
TRUE : 'true';
FALSE : 'false';
NIL : 'nil';
IF : 'if';
ELSE : 'else';
WHILE : 'while';
LOG : 'log';
ID
: [a-zA-Z_] [a-zA-Z_0-9]*
;
INT
: [0-9]+
;
FLOAT
: [0-9]+ '.' [0-9]*
| '.' [0-9]+
;
STRING
: '"' (~["\r\n] | '""')* '"'
;
COMMENT
: '#' ~[\r\n]* -> skip
;
SPACE
: [ \t\r\n] -> skip
;
OTHER
: .
;
Now let's say you would like to parse, and evaluate, input like this:
test.mu
a = true;
b = false;
if a && b {
log "1 :: a=" + a +", b=" + b;
}
else if a || b {
log "2 :: a=" + a +", b=" + b;
}
else {
log "3 :: a=" + a +", b=" + b;
}
log "Done!";
2. Visitor I
Start by generating the parser and visitor classes:
java -cp antlr-4.0-complete.jar org.antlr.v4.Tool Mu.g4 -visitor
The command above would have generated, among others the file MuBaseVisitor<T>. This is the class we're going to extend with out own logic:
EvalVisitor.java
public class EvalVisitor extends MuBaseVisitor<Value> {
// ...
}
where Value is just a wrapper for any of our language's types (String, Boolean, Double):
Value.java
public class Value {
public static Value VOID = new Value(new Object());
final Object value;
public Value(Object value) {
this.value = value;
}
public Boolean asBoolean() {
return (Boolean)value;
}
public Double asDouble() {
return (Double)value;
}
public String asString() {
return String.valueOf(value);
}
public boolean isDouble() {
return value instanceof Double;
}
#Override
public int hashCode() {
if(value == null) {
return 0;
}
return this.value.hashCode();
}
#Override
public boolean equals(Object o) {
if(value == o) {
return true;
}
if(value == null || o == null || o.getClass() != this.getClass()) {
return false;
}
Value that = (Value)o;
return this.value.equals(that.value);
}
#Override
public String toString() {
return String.valueOf(value);
}
}
3. Test I
To test the classes, use the following Main class:
Main.java
import org.antlr.v4.runtime.ANTLRFileStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
public class Main {
public static void main(String[] args) throws Exception {
MuLexer lexer = new MuLexer(new ANTLRFileStream("test.mu"));
MuParser parser = new MuParser(new CommonTokenStream(lexer));
ParseTree tree = parser.parse();
EvalVisitor visitor = new EvalVisitor();
visitor.visit(tree);
}
}
and compile and run the source files:
javac -cp antlr-4.0-complete.jar *.java
java -cp .:antlr-4.0-complete.jar Main
(on Windows, the last command would be: java -cp .;antlr-4.0-complete.jar Main)
After running Main, nothing happens (of course?). This is because we didn't implement any of the rules in our EvalVisitor class. To be able to evaluate the file test.mu properly, we need to provide a proper implementation for the following rules:
if_stat
andExpr
orExpr
plusExpr
assignment
idAtom
booleanAtom
stringAtom
log
#4. Visitor II & Test II
Here's a implementation of these rules:
import org.antlr.v4.runtime.misc.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EvalVisitor extends MuBaseVisitor<Value> {
// used to compare floating point numbers
public static final double SMALL_VALUE = 0.00000000001;
// store variables (there's only one global scope!)
private Map<String, Value> memory = new HashMap<String, Value>();
// assignment/id overrides
#Override
public Value visitAssignment(MuParser.AssignmentContext ctx) {
String id = ctx.ID().getText();
Value value = this.visit(ctx.expr());
return memory.put(id, value);
}
#Override
public Value visitIdAtom(MuParser.IdAtomContext ctx) {
String id = ctx.getText();
Value value = memory.get(id);
if(value == null) {
throw new RuntimeException("no such variable: " + id);
}
return value;
}
// atom overrides
#Override
public Value visitStringAtom(MuParser.StringAtomContext ctx) {
String str = ctx.getText();
// strip quotes
str = str.substring(1, str.length() - 1).replace("\"\"", "\"");
return new Value(str);
}
#Override
public Value visitNumberAtom(MuParser.NumberAtomContext ctx) {
return new Value(Double.valueOf(ctx.getText()));
}
#Override
public Value visitBooleanAtom(MuParser.BooleanAtomContext ctx) {
return new Value(Boolean.valueOf(ctx.getText()));
}
#Override
public Value visitNilAtom(MuParser.NilAtomContext ctx) {
return new Value(null);
}
// expr overrides
#Override
public Value visitParExpr(MuParser.ParExprContext ctx) {
return this.visit(ctx.expr());
}
#Override
public Value visitPowExpr(MuParser.PowExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
return new Value(Math.pow(left.asDouble(), right.asDouble()));
}
#Override
public Value visitUnaryMinusExpr(MuParser.UnaryMinusExprContext ctx) {
Value value = this.visit(ctx.expr());
return new Value(-value.asDouble());
}
#Override
public Value visitNotExpr(MuParser.NotExprContext ctx) {
Value value = this.visit(ctx.expr());
return new Value(!value.asBoolean());
}
#Override
public Value visitMultiplicationExpr(#NotNull MuParser.MultiplicationExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
switch (ctx.op.getType()) {
case MuParser.MULT:
return new Value(left.asDouble() * right.asDouble());
case MuParser.DIV:
return new Value(left.asDouble() / right.asDouble());
case MuParser.MOD:
return new Value(left.asDouble() % right.asDouble());
default:
throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);
}
}
#Override
public Value visitAdditiveExpr(#NotNull MuParser.AdditiveExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
switch (ctx.op.getType()) {
case MuParser.PLUS:
return left.isDouble() && right.isDouble() ?
new Value(left.asDouble() + right.asDouble()) :
new Value(left.asString() + right.asString());
case MuParser.MINUS:
return new Value(left.asDouble() - right.asDouble());
default:
throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);
}
}
#Override
public Value visitRelationalExpr(#NotNull MuParser.RelationalExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
switch (ctx.op.getType()) {
case MuParser.LT:
return new Value(left.asDouble() < right.asDouble());
case MuParser.LTEQ:
return new Value(left.asDouble() <= right.asDouble());
case MuParser.GT:
return new Value(left.asDouble() > right.asDouble());
case MuParser.GTEQ:
return new Value(left.asDouble() >= right.asDouble());
default:
throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);
}
}
#Override
public Value visitEqualityExpr(#NotNull MuParser.EqualityExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
switch (ctx.op.getType()) {
case MuParser.EQ:
return left.isDouble() && right.isDouble() ?
new Value(Math.abs(left.asDouble() - right.asDouble()) < SMALL_VALUE) :
new Value(left.equals(right));
case MuParser.NEQ:
return left.isDouble() && right.isDouble() ?
new Value(Math.abs(left.asDouble() - right.asDouble()) >= SMALL_VALUE) :
new Value(!left.equals(right));
default:
throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);
}
}
#Override
public Value visitAndExpr(MuParser.AndExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
return new Value(left.asBoolean() && right.asBoolean());
}
#Override
public Value visitOrExpr(MuParser.OrExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
return new Value(left.asBoolean() || right.asBoolean());
}
// log override
#Override
public Value visitLog(MuParser.LogContext ctx) {
Value value = this.visit(ctx.expr());
System.out.println(value);
return value;
}
// if override
#Override
public Value visitIf_stat(MuParser.If_statContext ctx) {
List<MuParser.Condition_blockContext> conditions = ctx.condition_block();
boolean evaluatedBlock = false;
for(MuParser.Condition_blockContext condition : conditions) {
Value evaluated = this.visit(condition.expr());
if(evaluated.asBoolean()) {
evaluatedBlock = true;
// evaluate this block whose expr==true
this.visit(condition.stat_block());
break;
}
}
if(!evaluatedBlock && ctx.stat_block() != null) {
// evaluate the else-stat_block (if present == not null)
this.visit(ctx.stat_block());
}
return Value.VOID;
}
// while override
#Override
public Value visitWhile_stat(MuParser.While_statContext ctx) {
Value value = this.visit(ctx.expr());
while(value.asBoolean()) {
// evaluate the code block
this.visit(ctx.stat_block());
// evaluate the expression
value = this.visit(ctx.expr());
}
return Value.VOID;
}
}
When you re-compile and run Main, the following would be printed to your console:
2 :: a=true, b=false
Done!
For an implementation of all other rules, see: https://github.com/bkiers/Mu
EDIT
From #pwwpche, in the comments:
for those using jdk1.8 and encounter IndexOutOfBoundsException, antlr 4.0
is somehow not compatible with jdk1.8. Download antlr-4.6-complete.jar, and
replace expr POW<assoc=right> expr with <assoc=right>expr POW expr will
eliminate the error and warnings.

ANTLR implementing "if statements" and "Loop statements" [duplicate]

I'm creating a simple programming language for a school project. I'm using ANTLR 4 to generate a lexer and a parser from my grammar. Until now, I have been using ANTLRs listener pattern to apply the actual functionality of the programming language.
Now I would like to implement if/else statements but I'm not sure that these can actually be implemented when using the listener pattern as ANTLR decides in which order to traverse the parse tree when using listeners and I imagine that the implementation of if/else statements will require jumping around the parse tree depending on which condition in the statement is satisfied.
Can anyone tell me if it will be possible to implement if/else statements using ANTLR or if I will have to implement the visitor pattern myself? Also, can anyone give an extremely simple example of the implementation the statements?
By default, ANTLR 4 generates listeners. But if you give org.antlr.v4.Tool the command line parameter -visitor, ANTLR generates visitor classes for you. These work much like listeners, but give you more control over which (sub) trees are walked/visited. This is particularly useful if you want to exclude certain (sub) trees (like else/if blocks, as in your case). While this can be done using listeners, it's much cleaner to do this with a visitor. Using listeners, you'll need to introduce global variables that keep track if a (sub) tree needs to be evaluated, and which do not.
As it happens to be, I'm working on a small ANTLR 4 tutorial. It's not done yet, but I'll post a small working example that demonstrates the use of these visitor classes and an if statement construct.
1. Grammar
Here's a simple grammar supporting basic expressions, if-, while- and log-statements:
Mu.g4
grammar Mu;
parse
: block EOF
;
block
: stat*
;
stat
: assignment
| if_stat
| while_stat
| log
| OTHER {System.err.println("unknown char: " + $OTHER.text);}
;
assignment
: ID ASSIGN expr SCOL
;
if_stat
: IF condition_block (ELSE IF condition_block)* (ELSE stat_block)?
;
condition_block
: expr stat_block
;
stat_block
: OBRACE block CBRACE
| stat
;
while_stat
: WHILE expr stat_block
;
log
: LOG expr SCOL
;
expr
: expr POW<assoc=right> expr #powExpr
| MINUS expr #unaryMinusExpr
| NOT expr #notExpr
| expr op=(MULT | DIV | MOD) expr #multiplicationExpr
| expr op=(PLUS | MINUS) expr #additiveExpr
| expr op=(LTEQ | GTEQ | LT | GT) expr #relationalExpr
| expr op=(EQ | NEQ) expr #equalityExpr
| expr AND expr #andExpr
| expr OR expr #orExpr
| atom #atomExpr
;
atom
: OPAR expr CPAR #parExpr
| (INT | FLOAT) #numberAtom
| (TRUE | FALSE) #booleanAtom
| ID #idAtom
| STRING #stringAtom
| NIL #nilAtom
;
OR : '||';
AND : '&&';
EQ : '==';
NEQ : '!=';
GT : '>';
LT : '<';
GTEQ : '>=';
LTEQ : '<=';
PLUS : '+';
MINUS : '-';
MULT : '*';
DIV : '/';
MOD : '%';
POW : '^';
NOT : '!';
SCOL : ';';
ASSIGN : '=';
OPAR : '(';
CPAR : ')';
OBRACE : '{';
CBRACE : '}';
TRUE : 'true';
FALSE : 'false';
NIL : 'nil';
IF : 'if';
ELSE : 'else';
WHILE : 'while';
LOG : 'log';
ID
: [a-zA-Z_] [a-zA-Z_0-9]*
;
INT
: [0-9]+
;
FLOAT
: [0-9]+ '.' [0-9]*
| '.' [0-9]+
;
STRING
: '"' (~["\r\n] | '""')* '"'
;
COMMENT
: '#' ~[\r\n]* -> skip
;
SPACE
: [ \t\r\n] -> skip
;
OTHER
: .
;
Now let's say you would like to parse, and evaluate, input like this:
test.mu
a = true;
b = false;
if a && b {
log "1 :: a=" + a +", b=" + b;
}
else if a || b {
log "2 :: a=" + a +", b=" + b;
}
else {
log "3 :: a=" + a +", b=" + b;
}
log "Done!";
2. Visitor I
Start by generating the parser and visitor classes:
java -cp antlr-4.0-complete.jar org.antlr.v4.Tool Mu.g4 -visitor
The command above would have generated, among others the file MuBaseVisitor<T>. This is the class we're going to extend with out own logic:
EvalVisitor.java
public class EvalVisitor extends MuBaseVisitor<Value> {
// ...
}
where Value is just a wrapper for any of our language's types (String, Boolean, Double):
Value.java
public class Value {
public static Value VOID = new Value(new Object());
final Object value;
public Value(Object value) {
this.value = value;
}
public Boolean asBoolean() {
return (Boolean)value;
}
public Double asDouble() {
return (Double)value;
}
public String asString() {
return String.valueOf(value);
}
public boolean isDouble() {
return value instanceof Double;
}
#Override
public int hashCode() {
if(value == null) {
return 0;
}
return this.value.hashCode();
}
#Override
public boolean equals(Object o) {
if(value == o) {
return true;
}
if(value == null || o == null || o.getClass() != this.getClass()) {
return false;
}
Value that = (Value)o;
return this.value.equals(that.value);
}
#Override
public String toString() {
return String.valueOf(value);
}
}
3. Test I
To test the classes, use the following Main class:
Main.java
import org.antlr.v4.runtime.ANTLRFileStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
public class Main {
public static void main(String[] args) throws Exception {
MuLexer lexer = new MuLexer(new ANTLRFileStream("test.mu"));
MuParser parser = new MuParser(new CommonTokenStream(lexer));
ParseTree tree = parser.parse();
EvalVisitor visitor = new EvalVisitor();
visitor.visit(tree);
}
}
and compile and run the source files:
javac -cp antlr-4.0-complete.jar *.java
java -cp .:antlr-4.0-complete.jar Main
(on Windows, the last command would be: java -cp .;antlr-4.0-complete.jar Main)
After running Main, nothing happens (of course?). This is because we didn't implement any of the rules in our EvalVisitor class. To be able to evaluate the file test.mu properly, we need to provide a proper implementation for the following rules:
if_stat
andExpr
orExpr
plusExpr
assignment
idAtom
booleanAtom
stringAtom
log
#4. Visitor II & Test II
Here's a implementation of these rules:
import org.antlr.v4.runtime.misc.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EvalVisitor extends MuBaseVisitor<Value> {
// used to compare floating point numbers
public static final double SMALL_VALUE = 0.00000000001;
// store variables (there's only one global scope!)
private Map<String, Value> memory = new HashMap<String, Value>();
// assignment/id overrides
#Override
public Value visitAssignment(MuParser.AssignmentContext ctx) {
String id = ctx.ID().getText();
Value value = this.visit(ctx.expr());
return memory.put(id, value);
}
#Override
public Value visitIdAtom(MuParser.IdAtomContext ctx) {
String id = ctx.getText();
Value value = memory.get(id);
if(value == null) {
throw new RuntimeException("no such variable: " + id);
}
return value;
}
// atom overrides
#Override
public Value visitStringAtom(MuParser.StringAtomContext ctx) {
String str = ctx.getText();
// strip quotes
str = str.substring(1, str.length() - 1).replace("\"\"", "\"");
return new Value(str);
}
#Override
public Value visitNumberAtom(MuParser.NumberAtomContext ctx) {
return new Value(Double.valueOf(ctx.getText()));
}
#Override
public Value visitBooleanAtom(MuParser.BooleanAtomContext ctx) {
return new Value(Boolean.valueOf(ctx.getText()));
}
#Override
public Value visitNilAtom(MuParser.NilAtomContext ctx) {
return new Value(null);
}
// expr overrides
#Override
public Value visitParExpr(MuParser.ParExprContext ctx) {
return this.visit(ctx.expr());
}
#Override
public Value visitPowExpr(MuParser.PowExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
return new Value(Math.pow(left.asDouble(), right.asDouble()));
}
#Override
public Value visitUnaryMinusExpr(MuParser.UnaryMinusExprContext ctx) {
Value value = this.visit(ctx.expr());
return new Value(-value.asDouble());
}
#Override
public Value visitNotExpr(MuParser.NotExprContext ctx) {
Value value = this.visit(ctx.expr());
return new Value(!value.asBoolean());
}
#Override
public Value visitMultiplicationExpr(#NotNull MuParser.MultiplicationExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
switch (ctx.op.getType()) {
case MuParser.MULT:
return new Value(left.asDouble() * right.asDouble());
case MuParser.DIV:
return new Value(left.asDouble() / right.asDouble());
case MuParser.MOD:
return new Value(left.asDouble() % right.asDouble());
default:
throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);
}
}
#Override
public Value visitAdditiveExpr(#NotNull MuParser.AdditiveExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
switch (ctx.op.getType()) {
case MuParser.PLUS:
return left.isDouble() && right.isDouble() ?
new Value(left.asDouble() + right.asDouble()) :
new Value(left.asString() + right.asString());
case MuParser.MINUS:
return new Value(left.asDouble() - right.asDouble());
default:
throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);
}
}
#Override
public Value visitRelationalExpr(#NotNull MuParser.RelationalExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
switch (ctx.op.getType()) {
case MuParser.LT:
return new Value(left.asDouble() < right.asDouble());
case MuParser.LTEQ:
return new Value(left.asDouble() <= right.asDouble());
case MuParser.GT:
return new Value(left.asDouble() > right.asDouble());
case MuParser.GTEQ:
return new Value(left.asDouble() >= right.asDouble());
default:
throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);
}
}
#Override
public Value visitEqualityExpr(#NotNull MuParser.EqualityExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
switch (ctx.op.getType()) {
case MuParser.EQ:
return left.isDouble() && right.isDouble() ?
new Value(Math.abs(left.asDouble() - right.asDouble()) < SMALL_VALUE) :
new Value(left.equals(right));
case MuParser.NEQ:
return left.isDouble() && right.isDouble() ?
new Value(Math.abs(left.asDouble() - right.asDouble()) >= SMALL_VALUE) :
new Value(!left.equals(right));
default:
throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);
}
}
#Override
public Value visitAndExpr(MuParser.AndExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
return new Value(left.asBoolean() && right.asBoolean());
}
#Override
public Value visitOrExpr(MuParser.OrExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(1));
return new Value(left.asBoolean() || right.asBoolean());
}
// log override
#Override
public Value visitLog(MuParser.LogContext ctx) {
Value value = this.visit(ctx.expr());
System.out.println(value);
return value;
}
// if override
#Override
public Value visitIf_stat(MuParser.If_statContext ctx) {
List<MuParser.Condition_blockContext> conditions = ctx.condition_block();
boolean evaluatedBlock = false;
for(MuParser.Condition_blockContext condition : conditions) {
Value evaluated = this.visit(condition.expr());
if(evaluated.asBoolean()) {
evaluatedBlock = true;
// evaluate this block whose expr==true
this.visit(condition.stat_block());
break;
}
}
if(!evaluatedBlock && ctx.stat_block() != null) {
// evaluate the else-stat_block (if present == not null)
this.visit(ctx.stat_block());
}
return Value.VOID;
}
// while override
#Override
public Value visitWhile_stat(MuParser.While_statContext ctx) {
Value value = this.visit(ctx.expr());
while(value.asBoolean()) {
// evaluate the code block
this.visit(ctx.stat_block());
// evaluate the expression
value = this.visit(ctx.expr());
}
return Value.VOID;
}
}
When you re-compile and run Main, the following would be printed to your console:
2 :: a=true, b=false
Done!
For an implementation of all other rules, see: https://github.com/bkiers/Mu
EDIT
From #pwwpche, in the comments:
for those using jdk1.8 and encounter IndexOutOfBoundsException, antlr 4.0
is somehow not compatible with jdk1.8. Download antlr-4.6-complete.jar, and
replace expr POW<assoc=right> expr with <assoc=right>expr POW expr will
eliminate the error and warnings.

Prefix expression to evaluate multiple expressions simultaneously

private class InputListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
Stack<Integer> operandStack = new Stack<Integer>();
Stack<Character> operatorStack = new Stack<Character>();
String input = inputTextField.getText();
StringTokenizer strToken = new StringTokenizer(input, " ", false);
while (strToken.hasMoreTokens())
{
String i = strToken.nextToken();
int operand;
char operator;
try
{
operand = Integer.parseInt(i);
operandStack.push(operand);
}
catch (NumberFormatException nfe)
{
operator = i.charAt(0);
operatorStack.push(operator);
}
}
int result = sum (operandStack, operatorStack);
resultTextField.setText(Integer.toString(result));
}
My prefix expression code will only evaluate one expression at a time (i.e. + 3 1). I want it to evaluate multiple expressions in one user-input expression (i.e. * + 16 4 + 3 1). How can I edit the code provided to make it evaluate multiple expressions? Thank you for your help.
To simply make your program do a bit more you can use a loop to keep operating on the operandStack and pushing the result of the previous result to the stack. I left my println statements in so you can see what its doing. Also I modified your method so it can sit inside a standalone main method.
You should look into the Shunting-yard algorithm, its quite fun to implement and it is somewhat like what your doing here. http://en.wikipedia.org/wiki/Shunting-yard_algorithm
public static void main(String[] args) {
Stack<Integer> operandStack = new Stack<Integer>();
Stack<Character> operatorStack = new Stack<Character>();
String input = "12 + 13 - 4";
StringTokenizer strToken = new StringTokenizer(input, " ", false);
while (strToken.hasMoreTokens()) {
String i = strToken.nextToken();
int operand;
char operator;
try {
operand = Integer.parseInt(i);
operandStack.push(operand);
} catch (NumberFormatException nfe) {
operator = i.charAt(0);
operatorStack.push(operator);
}
}
// loop until there is only 1 item left in the operandStack, this 1 item left is the result
while(operandStack.size() > 1) {
// some debugging println
System.out.println("Operate\n\tbefore");
System.out.println("\t"+operandStack);
System.out.println("\t"+operatorStack);
// perform the operations on the stack and push the result back onto the operandStack
operandStack.push(operate(operandStack, operatorStack));
System.out.println("\tafter");
System.out.println("\t"+operandStack);
System.out.println("\t"+operatorStack);
}
System.out.println("Result is: " + operandStack.peek());
}
/**
* Performs math operations and returns the result. Pops 2 items off the operandStack and 1 off the operator stack.
* #param operandStack
* #param operatorStack
* #return
*/
private static int operate(Stack<Integer> operandStack, Stack<Character> operatorStack) {
char op = operatorStack.pop();
Integer a = operandStack.pop();
Integer b = operandStack.pop();
switch(op) {
case '-':
return b - a;
case '+':
return a + b;
default:
throw new IllegalStateException("Unknown operator '"+op+"'");
}
}
I left the operate method (previously called sum) as close to what you had it as possible, however I think that your code could be improved by simply passing 2 integers and a operator to the function. Making the function alter your stacks isnt a great thing and could lead to confusing problems.
Consider making your method signature this instead:
private static int operate(Integer a, Integer b, char operator) {
switch(operator) {
case '-':
return b - a;
case '+':
return a + b;
default:
throw new IllegalStateException("Unknown operator '"+operator+"'");
}
}
and then popping from the stack and passing those to the method. Keeping your stack altering code all in one place.
operandStack.push(operate(operandStack.pop(), operandStack.pop(), operatorStack.pop()));

stack replaces all the entries in it with the last pushed element

I need some help on this one as I couldnt find a logical explanation for this anywhere. Maybe it is just a small mistake but I have been at the code for such a long time that I just cant see it. I have added comments where I am trying to push elements in my stack. I have two stacks one for all the operators and one for operands. The operator stack works fine but the operand stack is behaving weird. When I am trying to push elements it somehow replaces the stack elements with the latest element that I push. Whn I pop it returns the last element at all times. I have two different classes as operators and operands. Please let me know if you need the code for it. EDIT: The other classes dont do anything much though just a couple of getters, setters and checking for validity of tokens. Any help is appreciated. Thanks!
import java.util.*;
public class Evaluator {
private Stack<Operand> opdStack;
private Stack<Operator> oprStack;
public Evaluator() {
opdStack = new Stack<Operand>();
oprStack = new Stack<Operator>();
// HashMap<String, Operator> operators = new HashMap<String, Operator>();
Operator.operators.put("+",new AdditionOperator());
Operator.operators.put("-", new SubtractionOperator());
Operator.operators.put("*",new MultiplyOperator());
Operator.operators.put("/",new DivisionOperator());
Operator.operators.put("#",new BogusHash());
}
public int eval(String expr) {
String tok = "";
// init stack - necessary with operator priority schema;
// the priority of any operator in the operator stack other then
// the usual operators - "+-*/" - should be less than the priority
// of the usual operators
Operator newOpr = Operator.operators.get("#");
oprStack.push(newOpr);
String delimiters = "+-*/#! ";
StringTokenizer st = new StringTokenizer(expr,delimiters,true);
// the 3rd arg is true to indicate to use the delimiters as tokens, too
// but we'll filter out spaces
while (st.hasMoreTokens()) {
if ( !(tok = st.nextToken()).equals(" ")) { // filter out spaces
//System.out.println("equals");
if (Operand.check(tok)) { // check if tok is an operand
//System.out.println("tokens = "+tok);
//opdStack.push(new Operand ("0"));
opdStack.push(new Operand(tok));
System.out.println("stack peek "+ opdStack.peek().getValue());
//System.out.println(opdStack);
//System.out.println(oprStack);
} else {
//System.out.println("tokens = "+tok);
//System.out.println(newOpr);
if (!Operator.operators.containsKey(tok)) {
//System.out.println("tokens = "+tok);
//System.out.println(newOpr);
System.out.println("*****invalid token******"); System.exit(1);
}
Operator newOpr2 = Operator.operators.get(tok); // POINT 1
//System.out.println("Operator = "+oprStack.peek().priority());
while ( (oprStack.peek()).priority() >= newOpr2.priority()) {
//System.out.println("tokens while = "+tok);
System.out.println("tokens = "+tok);
Operator oldOpr = ((Operator)oprStack.pop());
//System.out.println("Opr"+oldOpr.priority());
Operand op2 = (Operand)opdStack.pop();
Operand op1 = (Operand)opdStack.pop();
System.out.println("run till here");
opdStack.push(oldOpr.execute(op1,op2));
System.out.println("Final res pushed opd= " + opdStack.peek().getValue());
}
oprStack.push(newOpr2);
//System.out.println("operand "+opdStack.toString());
}
}
}
//System.out.println("pop op2 = "+opdStack.pop().getValue()+" and pop op1 = "+opdStack.pop().getValue());
Operator newOp2 = ((Operator)oprStack.pop());
Operand op2 = (Operand)opdStack.get(0);
Operand op1 = (Operand)opdStack.get(1);
System.out.println("opd = "+ op1.getValue() + op2.getValue());
opdStack.push(newOp2.execute(op2, op1));
System.out.println("Full Stack opd size= "+ opdStack.size());
//System.out.println("Full Stack opd= "+ opdStack.toString());
Operand res = (Operand) opdStack.pop();
return res.getValue();
}
}
My Operand.java class
public class Operand {
static int val=0;
private static String strval="";
public Operand(String tok) {
// TODO Auto-generated constructor stub
strval = tok;
val = Integer.parseInt(strval);
// System.out.println("value = "+val);
}
public Operand(int value){
val = value;
//System.out.println("value = "+val);
}
public static boolean check(String tok) {
// TODO Auto-generated method stub
boolean bool;
try {
Integer.parseInt(tok);
bool = true;
} catch (Exception e){
//System.out.println("Type conversion "+e);
bool = false;
}
return bool;
}
public int getValue(){
//int re = Integer.parseInt(this.toString());
System.out.println("return value = " + val);
return val;
}
}

Categories