package function; import java.util.ArrayList; import function.builtin.*; import sexpression.*; import table.SymbolTable; public class UserDefinedFunction extends LispFunction { // the number of arguments that this user-defined function takes. private final int NUM_ARGS; private String name; private Cons body; private Cons lexpr; private SymbolTable environment; private ArrayList parameters; /** * Create a new user-defined function with the specified name, lambda list * and body. * * @param name * the name of this user-defined function * @param lambdaList * a list of the formal parameters of this user-defined function (MUST BE * A PROPER LIST) * @param body * the body of this user-defined function (MUST BE A PROPER LIST) */ public UserDefinedFunction(String name, Cons lambdaList, Cons body) { this.name = name; this.body = body; this.lexpr = new Cons(new Symbol(name), new Cons(lambdaList, body)); this.environment = SETF.getEnvironment(); this.parameters = new ArrayList(); // retrieve the names of all the formal parameters of this function while (lambdaList.consp()) { this.parameters.add(lambdaList.getCar().toString()); lambdaList = (Cons) lambdaList.getCdr(); } this.NUM_ARGS = this.parameters.size(); } public SExpression call(Cons argList) { // retrieve the number of arguments passed to this function 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(name), argList); String errMsg = "too " + ((argListLength > NUM_ARGS) ? "many" : "few") + " arguments given to " + name + ": " + originalSExpr; throw new RuntimeException(errMsg); } // push a new symbol table onto this function's environment (for its // parameters) environment = new SymbolTable(environment); // bind the values of the arguments to the formal parameter names for (String param : parameters) { SExpression currentArg = argList.getCar(); environment.put(param, currentArg); argList = (Cons) argList.getCdr(); } // store the environment of the S-expression that called this function // (the current environment) SymbolTable currentEnvironment = SETF.getEnvironment(); // replace the current environment with the environment of this // function SETF.setEnvironment(environment); Cons currentSExpression = body; SExpression retval = null; // evaluate all the S-expressions making up this function's body while (currentSExpression.consp()) { retval = EVAL.eval(currentSExpression.getCar()); currentSExpression = (Cons) currentSExpression.getCdr(); } // replace the environment of the S-expression that called this // function SETF.setEnvironment(currentEnvironment); // remove the bindings of the arguments to the formal parameter names // in the environment of this function environment = new SymbolTable(environment.getParent()); return retval; } /** * Return the lambda expression that represents this user-defined function. * * @return * the lambda expression that represents this user-defined function */ public Cons getLexpr() { return lexpr; } }