/*
* Primitive Collections for Java.
* Copyright (C) 2003 Søren Bak
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import bak.pcj.list.DoubleStack;
import bak.pcj.list.DoubleArrayStack;
/**
* This class implements an evaluator for postfix notation
* expressions. The class demonstrates the use of primitive
* stacks.
*
* @author Søren Bak
* @version 1.1 2003/17/2
*/
public class Calculator {
// ---------------------------------------------------------------
// Tokenization
// ---------------------------------------------------------------
// Lexemes for tokens
private static final int NUM = 0;
private static final int MUL = 1;
private static final int DIV = 2;
private static final int ADD = 3;
private static final int SUB = 4;
private static final int END = 5;
// Thrown to indicate a malformed expression
private static class ParserException extends Exception {
ParserException(String s) {
super(s);
}
}
// Tokenizer for expressions
private static class Tokenizer {
private char nextChar;
private int nextToken;
private double nextValue;
private int ptr;
private String input;
StringBuffer num = new StringBuffer();
Tokenizer(String input) throws ParserException {
setInput(input);
}
void setInput(String input) throws ParserException {
this.input = input;
ptr = 0;
nextChar();
advance();
}
// Advance to next character in input
private void nextChar() {
nextChar = ptr == input.length() ? '\0' : input.charAt(ptr++);
}
// Skip whitespace from input
private void skipws() {
while (nextChar == ' ' || nextChar == '\t')
nextChar();
}
// Advance to next token in input
void advance() throws ParserException {
skipws();
if (nextChar == '*') {
nextChar();
nextToken = MUL;
} else if (nextChar == '/') {
nextChar();
nextToken = DIV;
} else if (nextChar == '+') {
nextChar();
nextToken = ADD;
} else if (nextChar == '-') {
nextChar();
nextToken = SUB;
} else if (nextChar >= '0' && nextChar <= '9') {
num.setLength(0);
num.append(nextChar);
nextChar();
while ((nextChar >= '0' && nextChar <= '9') || nextChar == '.') {
num.append(nextChar);
nextChar();
}
try {
nextValue = Double.parseDouble(num.toString());
nextToken = NUM;
} catch (NumberFormatException e) {
throw new ParserException("Illegal number: " + num.toString());
}
} else if (nextChar == '\0') {
nextToken = END;
} else {
throw new ParserException("Illegal character: " + nextChar);
}
}
// Returns next token in input
int next()
{ return nextToken; }
// Returns double value of next NUM token in input
double value()
{ return nextValue; }
}
// ---------------------------------------------------------------
// Evaluation
// ---------------------------------------------------------------
/**
* Evaluates a postfix expression.
*
* @param input
* a character sequence representation of the
* expression to evaluate.
*
* @return the value of the expression.
*
* @throws NullPointerException
* if input is null.
*
* @throws ParserException
* if the expression is malformed.
*/
public double evaluate(String input) throws ParserException {
return evaluate(new Tokenizer(input));
}
private double evaluate(Tokenizer lex) throws ParserException {
try {
DoubleStack stack = new DoubleArrayStack();
int token;
while ((token = lex.next()) != END) {
if (token == NUM)
stack.push(lex.value());
else if (token == MUL) {
double v2 = stack.pop();
double v1 = stack.pop();
stack.push(v1*v2);
} else if (token == DIV) {
double v2 = stack.pop();
double v1 = stack.pop();
stack.push(v1/v2);
} else if (token == ADD) {
double v2 = stack.pop();
double v1 = stack.pop();
stack.push(v1+v2);
} else if (token == SUB) {
double v2 = stack.pop();
double v1 = stack.pop();
stack.push(v1-v2);
}
lex.advance();
}
double result = stack.pop();
if (!stack.isEmpty())
throw new ParserException("Stack is not empty");
return result;
} catch (IndexOutOfBoundsException e) { // Thrown when stack is empty
throw new ParserException("Stack was empty");
}
}
public static void main(String[] args) {
try {
Calculator calc = new Calculator();
System.out.println(calc.evaluate(args[0]));
} catch (ParserException pe) {
System.out.println("Syntax error: " + pe.getMessage());
} catch (ArithmeticException ae) {
System.out.println("Arithmetic error: " + ae.getMessage());
}
}
}