package function.builtin; import java.text.MessageFormat; import error.LispException; import function.*; import function.builtin.cons.LIST; import function.builtin.special.LAMBDA; import sexpression.*; import table.*; @FunctionNames({ "EVAL" }) public class EVAL extends LispFunction { public static LispFunction lookupFunctionOrLambda(SExpression functionExpression) { LispFunction function = FunctionTable.lookupFunction(functionExpression.toString()); if (function == null) function = createLambdaFunction(functionExpression); return function; } private static LispFunction createLambdaFunction(SExpression lambdaExpression) { if (lambdaExpression.isFunction()) return ((LambdaExpression) lambdaExpression).getFunction(); else if (LAMBDA.isLambdaExpression(lambdaExpression)) return LAMBDA.createFunction((Cons) lambdaExpression); else throw new UndefinedFunctionException(lambdaExpression); } public static SExpression lookupSymbol(String symbolName) { if (symbolName.equals("NIL")) return Nil.getInstance(); else if (symbolName.equals("T")) return Symbol.T; else if (symbolName.startsWith(":")) return new Symbol(symbolName); return ExecutionContext.getInstance().lookupSymbolValue(symbolName); } public static SExpression eval(SExpression sExpression) { Cons argumentList = LIST.makeList(sExpression); EVAL eval = new EVAL(); return eval.call(argumentList); } private ArgumentValidator argumentValidator; public EVAL() { this.argumentValidator = new ArgumentValidator("EVAL"); this.argumentValidator.setExactNumberOfArguments(1); } public SExpression call(Cons argumentList) { argumentValidator.validate(argumentList); SExpression argument = argumentList.getFirst(); if (argument.isList()) { if (argument.isCons()) return evaluateList((Cons) argument); return argument; // NIL } if (argument.isSymbol()) { SExpression symbolValue = lookupSymbol(argument.toString()); if (symbolValue != null) return symbolValue; throw new UndefinedSymbolException(argument); } return argument; // NUMBER or STRING } private SExpression evaluateList(Cons list) { SExpression functionName = list.getFirst(); SExpression arguments = list.getRest(); LispFunction function = lookupFunctionOrLambda(functionName); ArgumentValidator functionListValidator = new ArgumentValidator(functionName.toString()); functionListValidator.validate(list); Cons argumentList = (Cons) arguments; if (function.evaluateArguments()) argumentList = evaluateArgList(argumentList); return function.call(argumentList); } private Cons evaluateArgList(Cons arguments) { if (arguments.isNull()) return Nil.getInstance(); SExpression first = eval(arguments.getFirst()); SExpression rest = arguments.getRest(); return new Cons(first, evaluateArgList((Cons) rest)); } public static class UndefinedFunctionException extends LispException { private static final long serialVersionUID = 1L; private SExpression function; public UndefinedFunctionException(SExpression function) { this.function = function; } @Override public String getMessage() { return MessageFormat.format("undefined function: {0}", function); } } public static class UndefinedSymbolException extends LispException { private static final long serialVersionUID = 1L; private SExpression symbol; public UndefinedSymbolException(SExpression symbol) { this.symbol = symbol; } @Override public String getMessage() { return MessageFormat.format("symbol {0} has no value", symbol); } } }