Files
calc/calc.java

393 lines
12 KiB
Java

import java.util.Collections;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.function.DoubleUnaryOperator;
import java.util.function.DoubleBinaryOperator;
import java.util.stream.Collectors;
/**
* calc - an infix notation calculator
*/
public class calc {
static final String SEPARATOR = ",";
static final String OPERATORS = "-+*/^%";
static final String FUNCTIONS = "sin cos tan asin acos atan exp min max";
private static boolean debug = false;
public calc(boolean debug) throws Exception {
this.debug = debug;
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.print("> ");
String input = scanner.nextLine();
if(input.equals("quit")) break;
if(input.equals("help") || input.equals("?"))
usage();
Stack<String> rpn = parse(input);
if (debug) System.out.println("RPN: " + rpn);
Expression ast = toAST(rpn);
if (debug) System.out.println("AST: " + ast);
double val = ast.eval();
System.out.println(val);
}
}
private Stack<String> parse(String input) throws Exception {
Stack<String> output = new Stack<String>();
Stack<String> stackOps = new Stack<String>();
input = input.replace(" ", "")
.replace("°", "*" + Double.toString(Math.PI) + "/180")
.replace("(-", "(0-")
.replace(",-", ",0-")
.replace("(+", "(0+")
.replace(",+", ",0+")
.replace("pi", Double.toString(Math.PI))
.replace("π", Double.toString(Math.PI))
.replace("e", Double.toString(Math.E))
.toLowerCase();
if(input.charAt(0) == '-' || input.charAt(0) == '+')
input = "0" + input;
StringTokenizer stringTokenizer = new StringTokenizer(input,
OPERATORS + SEPARATOR + "()", true);
while(stringTokenizer.hasMoreTokens()) {
String token = stringTokenizer.nextToken();
if(isSeparator(token)) {
while(!stackOps.empty() &&
!isOpenBracket(stackOps.lastElement())) {
output.push(stackOps.pop());
}
} else if(isOpenBracket(token)) {
stackOps.push(token);
} else if(isCloseBracket(token)) {
while(!stackOps.empty()
&& !isOpenBracket(stackOps.lastElement()))
output.push(stackOps.pop());
stackOps.pop();
if(!stackOps.empty() && isFunction(stackOps.lastElement()))
output.push(stackOps.pop());
} else if(isNumber(token)) {
output.push(token);
} else if(isOperator(token)) {
while(!stackOps.empty()
&& isOperator(stackOps.lastElement())
&& precedence(token) <= precedence(stackOps
.lastElement())) {
output.push(stackOps.pop());
}
stackOps.push(token);
} else if(isFunction(token)) {
stackOps.push(token);
} else {
throw new Exception("Unrecognized token");
}
}
while(!stackOps.empty()) {
output.push(stackOps.pop());
}
Collections.reverse(output);
return output;
}
private static Expression toAST(Stack<String> input) {
Stack<Expression> stack = new Stack<Expression>();
while(!input.isEmpty()) {
String top = input.pop();
if(isNumber(top)) {
stack.push(new Number(Double.parseDouble(top)));
} else {
Expression e1 = stack.pop();
switch(top) {
case "+":
stack.push(new Add(stack.pop(), e1));
break;
case "-":
stack.push(new Subtract(stack.pop(), e1));
break;
case "*":
stack.push(new Multiply(stack.pop(), e1));
break;
case "/":
stack.push(new Divide(stack.pop(), e1));
break;
case "^":
stack.push(new Power(stack.pop(), e1));
break;
case "%":
stack.push(new Modulo(stack.pop(), e1));
break;
case "min":
stack.push(new Minimum(stack.pop(), e1));
break;
case "max":
stack.push(new Maximum(stack.pop(), e1));
break;
case "sin":
stack.push(new Sine(e1));
break;
case "cos":
stack.push(new Cosine(e1));
break;
case "tan":
stack.push(new Tangent(e1));
break;
case "asin":
stack.push(new Arcsine(e1));
break;
case "acos":
stack.push(new Arccosine(e1));
break;
case "atan":
stack.push(new Arctangent(e1));
break;
case "exp":
stack.push(new Exponent(e1));
break;
}
}
}
return stack.pop();
}
private static boolean isFunction(String func) {
return FUNCTIONS.indexOf(func) > -1;
}
private static int precedence(String op) {
String[] opSplit = OPERATORS.split(" ");
for(int i = 0; i < opSplit.length; i++)
if(opSplit[i].equals(op))
return i;
return Integer.MAX_VALUE;
}
private static boolean isOperator(String op) {
for(int i = 0; i < OPERATORS.length(); i++)
if(OPERATORS.charAt(i) == op.charAt(0))
return true;
return false;
}
private static boolean isNumber(String token) {
try {
double d = Double.parseDouble(token);
return true;
} catch(Exception e) {
return false;
}
}
private static boolean isSeparator(String token) {
return token.equals(",");
}
private static boolean isOpenBracket(String token) {
return token.equals("(");
}
private static boolean isCloseBracket(String token) {
return token.equals(")");
}
public static void main(String[] args) throws Exception {
boolean debug = false;
if(args.length > 0 && (args[0].equals("-v") || args[0].equals("--verbose")))
debug = true;
try {
new calc(debug);
} catch(Exception e) {
System.exit(1);
}
}
private static void usage() {
System.out.println("calc - an infix notation calculator");
System.out.println("Enter an expression below to be evaluated.");
}
public interface Expression {
public double eval();
public String toString();
}
static class Number implements Expression {
double val;
Number(double val) {
this.val = val;
}
public double eval() {
return val;
}
public String toString() {
return ""+val;
}
}
static class UnaryExpression implements Expression {
private String operator;
private double defaultValue;
private DoubleUnaryOperator op;
private Expression expression;
UnaryExpression(String operator, double defaultValue,
DoubleUnaryOperator op, Expression expression) {
this.operator = operator;
this.defaultValue = defaultValue;
this.op = op;
this.expression = expression;
}
public double eval() {
return op.applyAsDouble(expression.eval());
}
public String toString() {
return operator + "(" + expression.toString() + ")";
}
}
static class BinaryExpression implements Expression {
private String operator;
private double defaultValue;
private DoubleBinaryOperator op;
private Queue<Expression> expressions;
BinaryExpression(String operator, double defaultValue,
DoubleBinaryOperator op, Expression[] expressions) {
this.operator = operator;
this.defaultValue = defaultValue;
this.op = op;
this.expressions = new LinkedList<Expression>();
for(Expression expression : expressions)
this.expressions.add(expression);
}
public double eval() {
return expressions.stream()
.mapToDouble(Expression::eval)
.reduce(op)
.orElse(defaultValue);
}
public String toString() {
return expressions.stream()
.map(Object::toString)
.collect(Collectors.joining(" " + operator + " ", "(", ")"));
}
}
static class Negate extends UnaryExpression {
Negate(Expression expression) {
super("-", 0.0, (v) -> -v, expression);
}
}
static class SquareRoot extends UnaryExpression {
SquareRoot(Expression expression) {
super("√", 0.0, (v) -> Math.sqrt(v), expression);
}
}
static class Sine extends UnaryExpression {
Sine(Expression expression) {
super("sin", 0.0, (v) -> Math.sin(v), expression);
}
}
static class Cosine extends UnaryExpression {
Cosine(Expression expression) {
super("cos", 0.0, (v) -> Math.cos(v), expression);
}
}
static class Tangent extends UnaryExpression {
Tangent(Expression expression) {
super("tan", 0.0, (v) -> Math.tan(v), expression);
}
}
static class Arcsine extends UnaryExpression {
Arcsine(Expression expression) {
super("asin", 0.0, (v) -> Math.asin(v), expression);
}
}
static class Arccosine extends UnaryExpression {
Arccosine(Expression expression) {
super("acos", 0.0, (v) -> Math.acos(v), expression);
}
}
static class Arctangent extends UnaryExpression {
Arctangent(Expression expression) {
super("atan", 0.0, (v) -> Math.atan(v), expression);
}
}
static class Exponent extends UnaryExpression {
Exponent(Expression expression) {
super("exp", 0.0, (v) -> Math.exp(v), expression);
}
}
static class Minimum extends BinaryExpression {
Minimum(Expression... expressions) {
super("min", 0.0, (l, r) -> Math.min(l, r), expressions);
}
}
static class Maximum extends BinaryExpression {
Maximum(Expression... expressions) {
super("max", 0.0, (l, r) -> Math.max(l, r), expressions);
}
}
static class Add extends BinaryExpression {
Add(Expression... expressions) {
super("+", 0.0, (l, r) -> l + r, expressions);
}
}
static class Subtract extends BinaryExpression {
Subtract(Expression... expressions) {
super("-", 0.0, (l, r) -> l - r, expressions);
}
}
static class Multiply extends BinaryExpression {
Multiply(Expression... expressions) {
super("*", 0.0, (l, r) -> l * r, expressions);
}
}
static class Divide extends BinaryExpression {
Divide(Expression... expressions) {
super("/", 0.0, (l, r) -> l / r, expressions);
}
}
static class Power extends BinaryExpression {
Power(Expression... expressions) {
super("^", 0.0, (l, r) -> Math.pow(l, r), expressions);
}
}
static class Modulo extends BinaryExpression {
Modulo(Expression... expressions) {
super("%", 0.0, (l, r) -> l % r, expressions);
}
}
}