package function; import static function.builtin.EVAL.eval; import java.util.ArrayList; import sexpression.*; import table.*; public class UserDefinedFunction extends LispFunction { private String name; private Cons body; private Cons lambdaExpression; private ExecutionContext executionContext; private SymbolTable functionScope; 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.executionContext = ExecutionContext.getInstance(); this.functionScope = executionContext.getScope(); for (this.formalParameters = new ArrayList<>(); lambdaList.isCons(); lambdaList = (Cons) lambdaList.getRest()) this.formalParameters.add(lambdaList.getFirst().toString()); this.argumentValidator = new ArgumentValidator(this.name); this.argumentValidator.setExactNumberOfArguments(this.formalParameters.size()); } public SExpression call(Cons argumentList) { argumentValidator.validate(argumentList); return evaluateInFunctionScope(argumentList); } private SExpression evaluateInFunctionScope(Cons argumentList) { SymbolTable callingScope = executionContext.getScope(); SymbolTable executionScope = bindParameterValuesToFunctionScope(argumentList); executionContext.setScope(executionScope); SExpression lastEvaluation = evaluateBody(); executionContext.setScope(callingScope); return lastEvaluation; } private SymbolTable bindParameterValuesToFunctionScope(Cons argumentList) { SymbolTable executionScope = new SymbolTable(functionScope); for (String parameter : formalParameters) { SExpression currentArg = argumentList.getFirst(); executionScope.put(parameter, currentArg); argumentList = (Cons) argumentList.getRest(); } return executionScope; } private SExpression evaluateBody() { SExpression lastEvaluation = Nil.getInstance(); for (Cons expression = body; expression.isCons(); expression = (Cons) expression.getRest()) lastEvaluation = eval(expression.getFirst()); return lastEvaluation; } public Cons getLambdaExpression() { return lambdaExpression; } }