/* * Name: Mike Cifelli * Course: CIS 443 - Programming Languages * Assignment: Lisp Interpreter 1 */ package eval; import parser.*; import java.util.HashMap; /** * EVAL represents the EVAL function in Lisp. */ public class EVAL extends LispFunction { // A table to contain all the built-in and user-defined Lisp functions. private static HashMap functionTable = new HashMap(); static { // place all of the built-in functions into the function table functionTable.put("*", new MULTIPLY()); functionTable.put("+", new PLUS()); functionTable.put("-", new MINUS()); functionTable.put("/", new DIVIDE()); functionTable.put("<", new LESSP()); functionTable.put("=", new EQUALSP()); functionTable.put(">", new GREATERP()); functionTable.put("APPLY", new APPLY()); functionTable.put("ATOM", new ATOM()); functionTable.put("CAR", new CAR()); functionTable.put("CDR", new CDR()); functionTable.put("COND", new COND()); functionTable.put("CONS", new CONS()); functionTable.put("DEFUN", new DEFUN()); functionTable.put("EQ", new EQ()); functionTable.put("EQUAL", new EQUAL()); functionTable.put("EVAL", new EVAL()); functionTable.put("EXIT", new EXIT()); functionTable.put("FIRST", new CAR()); functionTable.put("FUNCALL", new FUNCALL()); functionTable.put("GREATERP", new GREATERP()); functionTable.put("LAMBDA", new LAMBDA()); functionTable.put("LENGTH", new LENGTH()); functionTable.put("LET", new LET()); functionTable.put("LIST", new LIST()); functionTable.put("LISTP", new LISTP()); functionTable.put("LOAD", new LOAD()); functionTable.put("NULL", new NULL()); functionTable.put("PRINT", new PRINT()); functionTable.put("QUOTE", new QUOTE()); functionTable.put("REST", new CDR()); functionTable.put("SETF", new SETF()); functionTable.put("SYMBOL-FUNCTION", new SYMBOL_FUNCTION()); } /** * Retrieve the function table. * * @return * the function table */ public static HashMap getFunctionTable() { return functionTable; } /** * Look up a function by its name. * * @param functionName * the name of the function to look up * @return * the function with the name functionName if it exists; null * otherwise */ public static LispFunction lookupFunction(String functionName) { return functionTable.get(functionName); } /** * Look up a symbol's value using its name. * * @param symbolName * the name of the symbol to look up (must not be null) * @return * the value of symbolName if it has one; null otherwise */ public static SExpression lookupSymbol(String symbolName) { if (symbolName.equals("NIL")) { return Nil.getUniqueInstance(); } else if (symbolName.equals("T")) { return Symbol.T; } else if (symbolName.startsWith(":")) { return new Symbol(symbolName); } return SETF.lookup(symbolName); } /** * Determine if the given list is dotted. * * @param list * the list to be tested (must not be null) * @return * true if list is dotted; false * otherwise */ public static boolean isDotted(Cons list) { if (list.nullp()) { return false; } SExpression cdr = list.getCdr(); if (cdr.listp()) { return isDotted((Cons) cdr); } // the cdr of 'list' is not a list, therefore it is dotted return true; } /** * Evaluate the given S-expression. * * @param sexpr * the S-expression to evaluate * @return * the value of sexpr */ public static SExpression eval(SExpression sexpr) { Cons expList = LIST.makeList(sexpr); EVAL evalFunction = new EVAL(); return evalFunction.call(expList); } // The number of arguments that EVAL takes. private static final int NUM_ARGS = 1; public SExpression call(Cons argList) { // retrieve the number of arguments passed to EVAL int argListLength = LENGTH.getLength(argList); // make sure we have received the proper number of arguments if (argListLength != NUM_ARGS) { Cons originalSExpr = new Cons(new Symbol("EVAL"), argList); String errMsg = "too " + ((argListLength > NUM_ARGS) ? "many" : "few") + " arguments given to EVAL: " + originalSExpr; throw new RuntimeException(errMsg); } SExpression arg = argList.getCar(); if (arg.listp()) { if (arg.consp()) { return evaluateList((Cons) arg); } return arg; // 'arg' is NIL } if (arg.symbolp()) { SExpression symbolValue = lookupSymbol(arg.toString()); if (symbolValue != null) { return symbolValue; } throw new RuntimeException("variable " + arg + " has no value"); } return arg; // 'arg' is a NUMBER or a STRING } // Evaluate the specified list. // // Parameters: list - the list to evaluate // Returns: the value of 'list' // Precondition: 'list' must not be null. private SExpression evaluateList(Cons list) { SExpression car = list.getCar(); SExpression cdr = list.getCdr(); LispFunction function = lookupFunction(car.toString()); if (function == null) { // check if the car of the list is a lambda expression if (car.functionp()) { function = ((LambdaExpression) car).getFunction(); } else if (LAMBDA.isLambdaExpression(car)) { Cons lexpr = (Cons) car; function = LAMBDA.createFunction(lexpr); } else { throw new RuntimeException("undefined function " + car); } } // make sure the list of arguments for 'function' is a list if (cdr.listp()) { Cons args = (Cons) cdr; // make sure the list of arguments is not dotted if (isDotted(args)) { throw new RuntimeException("argument list given to " + car + " is dotted: " + list); } // determine if we should evaluate the arguments that will be // passed to 'function' if (function.evaluateArguments()) { args = evaluateArgList(args); } return function.call(args); } // the list of arguments is not a list! throw new RuntimeException("argument list given to " + car + " is dotted: " + list); } // Evaluate a list of arguments for a function. // // Parameters: arguments - a list of arguments for a function // Returns: a list consisting of the values of the S-expressions found in // 'arguments' // Precondition: 'arguments' must not be null. private Cons evaluateArgList(Cons arguments) { if (arguments.nullp()) { return Nil.getUniqueInstance(); } SExpression car = eval(arguments.getCar()); SExpression cdr = arguments.getCdr(); if (cdr.listp()) { return new Cons(car, evaluateArgList((Cons) cdr)); } // remove any parameters found after a dot (put here in case the check // for a dotted parameter list is not done prior to this call) return new Cons(car, Nil.getUniqueInstance()); } }