package function; import java.util.ArrayList; import function.builtin.EVAL; import function.builtin.special.SETF; import sexpression.*; import table.SymbolTable; public class UserDefinedFunction extends LispFunction { private String name; private Cons body; private Cons lambdaExpression; private SymbolTable scope; private ArrayList formalParameters; private ArgumentValidator argumentValidator; public UserDefinedFunction(String name, Cons lambdaList, Cons body) { this.name = name; this.body = body; this.lambdaExpression = new Cons(new Symbol(name), new Cons(lambdaList, body)); this.scope = SETF.getSymbolTable(); for (this.formalParameters = new ArrayList<>(); lambdaList.consp(); lambdaList = (Cons) lambdaList.getCdr()) this.formalParameters.add(lambdaList.getCar().toString()); this.argumentValidator = new ArgumentValidator(this.name); this.argumentValidator.setExactNumberOfArguments(this.formalParameters.size()); } public SExpression call(Cons argumentList) { argumentValidator.validate(argumentList); // bind the values of the arguments to the formal parameter names bindParameterValues(argumentList); // store the environment of the S-expression that called this function // (the current environment) SymbolTable callingScope = SETF.getSymbolTable(); // replace the current environment with the environment of this // function SETF.setSymbolTable(scope); Cons currentSExpression = body; SExpression lastValue = null; // evaluate all the S-expressions making up this function's body while (currentSExpression.consp()) { lastValue = EVAL.eval(currentSExpression.getCar()); currentSExpression = (Cons) currentSExpression.getCdr(); } // replace the environment of the S-expression that called this // function SETF.setSymbolTable(callingScope); // remove the bindings of the arguments to the formal parameter names // in the environment of this function releaseParameterValues(); return lastValue; } private void bindParameterValues(Cons argumentList) { scope = new SymbolTable(scope); for (String parameter : formalParameters) { SExpression currentArg = argumentList.getCar(); scope.put(parameter, currentArg); argumentList = (Cons) argumentList.getCdr(); } } private void releaseParameterValues() { scope = new SymbolTable(scope.getParent()); } public Cons getLambdaExpression() { return lambdaExpression; } }