Skip to content

Instantly share code, notes, and snippets.

@jcsirot
Created February 19, 2013 21:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jcsirot/4990325 to your computer and use it in GitHub Desktop.
Save jcsirot/4990325 to your computer and use it in GitHub Desktop.
Calculatrice (Code Story 2013)
package com.chelonix.codestory.evaluator;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.Stack;
/**
*
* @author sirot
*/
public class Evaluator implements TokenVisitor
{
Stack<Expression> ast = new Stack<Expression>();
Stack<Stack<Operator.Op>> operators = new Stack<Stack<Operator.Op>>() {{
add(new Stack<Operator.Op>());
}};
public BigDecimal evaluate(String expression) throws IOException
{
ExpressionLexer tokenizer = new ExpressionLexer().parse(expression);
Iterator<Token> it = tokenizer.iterator();
while(it.hasNext()) {
it.next().visit(this);
}
Stack<Operator.Op> ops = operators.pop();
while(! ops.empty()) {
buildAST(ops.pop());
}
return ast.pop().evaluate();
}
private void buildAST(Operator.Op operator)
{
Expression right = ast.pop();
Expression left = ast.pop();
switch (operator) {
case PLUS:
ast.push(new PlusExpression(left, right));
break;
case MULTIPLY:
ast.push(new MultExpression(left, right));
break;
case MINUS:
ast.push(new MinusExpression(left, right));
break;
case DIVIDE:
ast.push(new DivideExpression(left, right));
break;
default:
throw new RuntimeException("Mismatch parenthesis");
}
}
public void visit(Literal literal) {
ast.add(new LitteralExpression(literal.getValue()));
}
public void visit(LeftParenthesis lp) {
operators.add(new Stack<Operator.Op>());
}
public void visit(RightParenthesis rp) {
Stack<Operator.Op> ops = operators.pop();
while (! ops.isEmpty()) {
Operator.Op op = ops.pop();
buildAST(op);
}
}
public void visit(Operator operator)
{
Operator.Op op = operator.getOperator();
Stack<Operator.Op> ops = operators.peek();
while(! ops.isEmpty()) {
Operator.Op topOp = ops.peek();
if (op.hasLowerOrEqualsPrecedence(topOp)) {
buildAST(ops.pop());
} else {
break;
}
}
ops.push(op);
}
private static class LitteralExpression implements Expression
{
private final BigDecimal value;
private LitteralExpression(BigDecimal value)
{
this.value = value;
}
public BigDecimal evaluate()
{
return value;
}
}
private static class PlusExpression implements Expression
{
private Expression left, right;
private PlusExpression(Expression left, Expression right)
{
this.right = right;
this.left = left;
}
public BigDecimal evaluate()
{
return left.evaluate().add(right.evaluate());
}
}
private static class MultExpression implements Expression
{
private Expression left, right;
private MultExpression(Expression left, Expression right)
{
this.right = right;
this.left = left;
}
public BigDecimal evaluate()
{
return left.evaluate().multiply(right.evaluate());
}
}
private static class DivideExpression implements Expression
{
private Expression left, right;
private DivideExpression(Expression left, Expression right)
{
this.right = right;
this.left = left;
}
public BigDecimal evaluate()
{
return left.evaluate().divide(right.evaluate());
}
}
private static class MinusExpression implements Expression
{
private Expression left, right;
private MinusExpression(Expression left, Expression right)
{
this.right = right;
this.left = left;
}
public BigDecimal evaluate()
{
return left.evaluate().subtract(right.evaluate());
}
}
private static interface Expression
{
BigDecimal evaluate();
}
}
package com.chelonix.codestory.evaluator;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
*
* @author sirot
*/
public class ExpressionLexer implements Iterable<Token>
{
private List<Token> tokens = new ArrayList<Token>();
public ExpressionLexer parse(CharSequence seq)
{
boolean nextMinusIsUnary = false;
StringBuilder buffer = new StringBuilder();
int index = 0;
while (index < seq.length()) {
char c = seq.charAt(index++);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
buffer.append(c);
nextMinusIsUnary = false;
break;
case '+':
pushNumber(buffer);
tokens.add(Operator.PLUS);
buffer = new StringBuilder();
nextMinusIsUnary = false;
break;
case '*':
pushNumber(buffer);
tokens.add(Operator.MULTIPLY);
buffer = new StringBuilder();
nextMinusIsUnary = false;
break;
case '/':
pushNumber(buffer);
tokens.add(Operator.DIVIDE);
buffer = new StringBuilder();
nextMinusIsUnary = false;
break;
case ')':
pushNumber(buffer);
tokens.add(RightParenthesis.THIS);
buffer = new StringBuilder();
nextMinusIsUnary = false;
break;
case '(':
pushNumber(buffer);
tokens.add(LeftParenthesis.THIS);
buffer = new StringBuilder();
nextMinusIsUnary = true;
break;
case '-':
if (nextMinusIsUnary) {
buffer = new StringBuilder();
buffer.append("-");
nextMinusIsUnary = false;
} else {
pushNumber(buffer);
tokens.add(Operator.MINUS);
buffer = new StringBuilder();
nextMinusIsUnary = true;
}
break;
}
}
if (buffer.length() > 0) {
pushNumber(buffer);
}
return this;
}
private void pushNumber(StringBuilder buffer)
{
if (buffer.length() > 0) {
tokens.add(new Literal(new BigDecimal(buffer.toString())));
}
}
public Iterator<Token> iterator()
{
return tokens.iterator();
}
}
package com.chelonix.codestory.evaluator;
/**
*
* @author sirot
*/
public class LeftParenthesis implements Token
{
public static final LeftParenthesis THIS = new LeftParenthesis();
private LeftParenthesis() {}
public void visit(TokenVisitor visitor)
{
visitor.visit(this);
}
}
package com.chelonix.codestory.evaluator;
import java.math.BigDecimal;
/**
*
* @author sirot
*/
public class Literal implements Token
{
private BigDecimal value;
public Literal(BigDecimal value)
{
this.value = value;
}
BigDecimal getValue()
{
return value;
}
public void visit(TokenVisitor visitor)
{
visitor.visit(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Literal literal = (Literal) o;
if (value != null ? !(value.compareTo(literal.value) == 0) : literal.value != null) return false;
return true;
}
@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}
}
package com.chelonix.codestory.evaluator;
/**
*
* @author sirot
*/
public class Operator implements Token
{
public static final Operator PLUS = new Operator(Op.PLUS);
public static final Operator MINUS = new Operator(Op.MINUS);
public static final Operator MULTIPLY = new Operator(Op.MULTIPLY);
public static final Operator DIVIDE = new Operator(Op.DIVIDE);
private Op op;
private Operator(Op op)
{
this.op = op;
}
Op getOperator()
{
return op;
}
public void visit(TokenVisitor visitor)
{
visitor.visit(this);
}
/**
*
* @author sirot
*/
public static enum Op
{
PLUS("+", 1),
MINUS("-", 1),
MULTIPLY("*", 2),
DIVIDE("/", 2);
private final String symbol;
private final int precedence;
Op(String symbol, int precedence)
{
this.symbol = symbol;
this.precedence = precedence;
}
public String getSymbol()
{
return symbol;
}
public boolean hasLowerOrEqualsPrecedence(Op other)
{
return this.precedence <= other.precedence;
}
}
}
package com.chelonix.codestory.evaluator;
/**
*
* @author sirot
*/
public class RightParenthesis implements Token
{
public static final RightParenthesis THIS = new RightParenthesis();
private RightParenthesis() {}
public void visit(TokenVisitor visitor)
{
visitor.visit(this);
}
}
package com.chelonix.codestory.evaluator;
/**
*
* @author sirot
*/
public interface Token
{
public void visit(TokenVisitor visitor);
}
package com.chelonix.codestory.evaluator;
/**
*
* @author sirot
*/
public interface TokenVisitor
{
void visit(Literal literal);
void visit(LeftParenthesis lp);
void visit(RightParenthesis rp);
void visit(Operator op);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment