Created
February 19, 2013 21:42
-
-
Save jcsirot/4990325 to your computer and use it in GitHub Desktop.
Calculatrice (Code Story 2013)
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
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(); | |
} | |
} |
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
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(); | |
} | |
} |
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
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); | |
} | |
} |
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
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; | |
} | |
} |
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
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; | |
} | |
} | |
} |
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
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); | |
} | |
} |
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
package com.chelonix.codestory.evaluator; | |
/** | |
* | |
* @author sirot | |
*/ | |
public interface Token | |
{ | |
public void visit(TokenVisitor visitor); | |
} |
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
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