0
package PJ2;
import java.util.*;

public class SimpleLispExpressionEvaluator
{
    // Current input Lisp expression
    private String inputExpr;

    // Main expression stack & current operation stack, see algorithm in evaluate()
    private Stack<Object> exprStack;
    private Stack<Double> currentOpStack;


    // default constructor
    // set inputExpr to "" 
    // create stack objects
    public SimpleLispExpressionEvaluator()
    {
    // add statements
        inputExpr = "";
        exprStack = new Stack<Object>();
        currentOpStack = new Stack<Double>();
    }

    // default constructor
    // set inputExpr to inputExpression 
    // create stack objects
    public SimpleLispExpressionEvaluator(String inputExpression) 
    {
    // add statements
        if(inputExpression == null)
        {
            throw new SimpleLispExpressionEvaluatorException();
        }

        inputExpr = inputExpression;
        exprStack = new Stack<Object>();
        currentOpStack = new Stack<Double>();

    }

    // set inputExpr to inputExpression 
    // clear stack objects
    public void reset(String inputExpression) 
    {
    // add statements
     if(inputExpression == null)
     {
         throw new SimpleLispExpressionEvaluatorException();
     }

     inputExpr = inputExpression;
     exprStack.clear();
     currentOpStack.clear();
    }

    // This function evaluate current operator with its operands
    // See complete algorithm in evaluate()
    //
    // Main Steps:
    //      Pop operands from exprStack and push them onto 
    //          currentOpStack until you find an operator
    //      Apply the operator to the operands on currentOpStack
    //          Push the result into exprStack
    //
    private void evaluateCurrentOperation()
    {
    // add statements
        String currOp;
        boolean numeric = true;
        do{
            currOp = (String.valueOf(exprStack.pop()));

            try{
                Double number = Double.parseDouble(currOp);
                currentOpStack.push(number);

            }catch(NumberFormatException nfe){
                numeric = false;
            }
        } while(numeric);


        double result;
        switch (currOp) {
            case "*":
                result = currentOpStack.pop();
                while(!currentOpStack.isEmpty()){
                    result *= currentOpStack.pop();
                }
                break;
            case "/":
                result = currentOpStack.pop();
                while(!currentOpStack.isEmpty()){
                    result /= currentOpStack.pop();
                }
                break;
            case "+":
                result = currentOpStack.pop();
                while(!currentOpStack.isEmpty()){
                    result += currentOpStack.pop();
                }
                break;
            case "-":
                result = currentOpStack.pop();
                while(!currentOpStack.isEmpty()){
                    result -= currentOpStack.pop();
                }
                break;

            default:
                result = currentOpStack.pop();
                break;
        }

        exprStack.push(result);

    }

    /**
     * This function evaluates current Lisp expression in inputExpr
     * It return result of the expression 
     *
     * The algorithm:  
     *
     * Step 1   Scan the tokens in the string.
     * Step 2       If you see an operand, push operand object onto the exprStack
     * Step 3           If you see "(", next token should be an operator
     * Step 4       If you see an operator, push operator object onto the exprStack
     * Step 5       If you see ")", do steps 6,7,8 in evaluateCurrentOperation() :
     * Step 6           Pop operands and push them onto currentOpStack 
     *                  until you find an operator
     * Step 7           Apply the operator to the operands on currentOpStack
     * Step 8           Push the result into exprStack
     * Step 9    If you run out of tokens, the value on the top of exprStack is
     *           is the result of the expression.
     */
    public double evaluate()
    {
        // only outline is given...
        // you need to add statements/local variables
        // you may delete or modify any statements in this method

        // use scanner to tokenize inputExpr
        Scanner inputExprScanner = new Scanner(inputExpr);

        // Use zero or more white space as delimiter,
        // which breaks the string into single character tokens
        inputExprScanner = inputExprScanner.useDelimiter("\\s*");

        // Step 1: Scan the tokens in the string.
        while (inputExprScanner.hasNext())
        {

            // Step 2: If you see an operand, push operand object onto the exprStack
            if (inputExprScanner.hasNextInt())
            {
                // This force scanner to grab all of the digits
                // Otherwise, it will just get one char
                String dataString = inputExprScanner.findInLine("\\d+");

        // more ...
                exprStack.push(Double.parseDouble(dataString));
            }
            else
            {
                // Get next token, only one char in string token
                String aToken = inputExprScanner.next();
                char item = aToken.charAt(0);

                switch (item)
                {
                case '(':
                    break;

                case ')':
                    evaluateCurrentOperation();
                    break; 

                case'*':
                    exprStack.push("*");
                    break;

                case'+':
                    exprStack.push("+");
                    break; 

                case'/':
                    exprStack.push("/");
                    break;

                case'-':
                    exprStack.push("-");
                    break;  

                // Step 3: If you see "(", next token shoube an operator
                // Step 4: If you see an operator, push operator object onto the exprStack
                // Step 5: If you see ")"  do steps 6,7,8 in evaluateCurrentOperation() :
                    default:  // error
                        throw new SimpleLispExpressionEvaluatorException(item + " is not a legal expression operator");
                } // end switch
            } // end else
        } // end while

        // Step 9: If you run out of tokens, the value on the top of exprStack is
        //         is the result of the expression.
        //
        //         return result
        return Double.parseDouble(String.valueOf(exprStack.pop()));
    }


    //=====================================================================

    // This static method is used by main() only
    private static void evaluateExprTest(String s, SimpleLispExpressionEvaluator expr)
    {
        Double result;
        System.out.println("Expression " + s);
    expr.reset(s);
        result = expr.evaluate();
        System.out.printf("Result %.2f\n", result);
        System.out.println("-----------------------------");
    }

    // define few test cases, exception may happen 
    public static void main (String args[])
    {
        SimpleLispExpressionEvaluator expr= new SimpleLispExpressionEvaluator();
        String test1 = "(+ (- 6) (* 2 3 4) (/ (+ 3) (* 1) (- 2 3 1)))";
        String test2 = "(+ (- 632) (* 21 3 4) (/ (+ 32) (* 1) (- 21 3 1)))";
        String test3 = "(+ (/ 2) (* 2) (/ (+ 1) (+ 1) (- 2 1 )))";
        String test4 = "(+ (/2))";
        String test5 = "(+ (/2 3 0))";
        String test6 = "(+ (/ 2) (* 2) (/ (+ 1) (+ 3) (- 2 1 ))))";
        String test7 = "( + 100 )";
        String test8 = "( + 100 5 5 5 )";
        String test9 = "(*(/100 5)4      10)";
        String test10 ="( + 100 )";
        String test11 ="( + ( - 6 ) ( * 2 3 4 ) ( / ( + 3 ) ( * 1 ) ( - 2 3 1 ) ) )";
        String test12 ="( + ( - 632 ) ( * 21 3 4 ) ( / ( + 32 ) ( * 1 ) ( - 21 3 1 ) ) )";
        String test13 ="";
        String test14 ="";

    evaluateExprTest(test1, expr);
    evaluateExprTest(test2, expr);
    evaluateExprTest(test3, expr);
    evaluateExprTest(test4, expr);
    evaluateExprTest(test5, expr);
    evaluateExprTest(test6, expr);
    evaluateExprTest(test7, expr);
    evaluateExprTest(test8, expr);
    evaluateExprTest(test9, expr);
    evaluateExprTest(test10, expr);
    evaluateExprTest(test11, expr);
    evaluateExprTest(test12, expr);
    //evaluateExprTest(test13, expr);
    //evaluateExprTest(test14, expr);
    }
}

When I run it only tests 3,4,5,7,8,9,10 work correctly based on: http://joeganley.com/code/jslisp.html

Expression (+ (- 6) (* 2 3 4) (/ (+ 3) (* 1) (- 2 3 1)))
Result 28.50
-----------------------------
Expression (+ (- 632) (* 21 3 4) (/ (+ 32) (* 1) (- 21 3 1)))
Result 885.88
-----------------------------
Expression (+ (/ 2) (* 2) (/ (+ 1) (+ 1) (- 2 1 )))
Result 5.00
-----------------------------
Expression (+ (/2))
Result 2.00
-----------------------------
Expression (+ (/2 3 0))
Result Infinity
-----------------------------
Expression ( + 100 )
Result 100.00
-----------------------------
Expression ( + 100 5 5 5 )
Result 115.00
-----------------------------
Expression (*(/100 5)4      10)
Result 800.00
-----------------------------
Expression ( + 100 )
Result 100.00
-----------------------------
Expression ( + ( - 6 ) ( * 2 3 4 ) ( / ( + 3 ) ( * 1 ) ( - 2 3 1 ) ) )
Result 28.50
-----------------------------
Expression ( + ( - 632 ) ( * 21 3 4 ) ( / ( + 32 ) ( * 1 ) ( - 21 3 1 ) ) )
Result 885.88
-----------------------------

These results only occur when I remove test 6 because it gives an exception and stops executing. Any idea on where I've gone wrong?

Error if 6 is uncommented out:

Exception in thread "main" java.util.EmptyStackException
    at java.util.Stack.peek(Unknown Source)
    at java.util.Stack.pop(Unknown Source)
    at PJ2.SimpleLispExpressionEvaluator.evaluateCurrentOperation(SimpleLispExpressionEvaluator.java:143)
    at PJ2.SimpleLispExpressionEvaluator.evaluate(SimpleLispExpressionEvaluator.java:248)
    at PJ2.SimpleLispExpressionEvaluator.evaluateExprTest(SimpleLispExpressionEvaluator.java:292)
    at PJ2.SimpleLispExpressionEvaluator.main(SimpleLispExpressionEvaluator.java:321)
BenMorel
  • 34,448
  • 50
  • 182
  • 322
yoshi105
  • 41
  • 2
  • 6
  • 1
    Do some debugging. You have clear instructions in the stack trace as to where the problem is. Either put some logging statements and run only test 6 or set a breakpoint and step through. Start just before line 143 and see what the state of data is and work backward to figure out why it's in the state it is instead of the state you expect, OR figure out why you are trying to perform an operation you shouldn't based on the data. – digitaljoel Apr 09 '13 at 04:25
  • What you're making there is an implementation of Tcl. Lisp works with objects, not strings. – Kaz Jul 17 '13 at 07:18

1 Answers1

5

That's not really Lisp. That's just arithmetic expressions in prefix form. About 0.1% of Lisp. Lisp is a bit more: functions, symbols, cons cells, list operations, ...

Your test expression 6 has an extra parenthesis at the end.

Generally you should develop your code so that syntax errors can be easily be detected. That means error detection, error handling and error reporting. A typical Lisp system (say, SBCL, CLISP, ...) will report that extra parenthesis to the user in plain english.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346