package function.builtin.special; import static function.builtin.EVAL.eval; import static sexpression.Nil.NIL; import function.ArgumentValidator; import function.FunctionNames; import function.LispSpecialFunction; import sexpression.Cons; import sexpression.SExpression; import sexpression.Symbol; import table.ExecutionContext; import table.SymbolTable; @FunctionNames({ "LET" }) public class LET extends LispSpecialFunction { private ArgumentValidator argumentValidator; private ArgumentValidator variableDefinitionListValidator; private ArgumentValidator pairValidator; protected ExecutionContext executionContext; public LET(String name) { this.argumentValidator = new ArgumentValidator(name); this.argumentValidator.setMinimumNumberOfArguments(1); this.argumentValidator.setFirstArgumentExpectedType(Cons.class); this.variableDefinitionListValidator = new ArgumentValidator(name + "|pair-list|"); this.variableDefinitionListValidator.setEveryArgumentExpectedType(Cons.class); this.pairValidator = new ArgumentValidator(name + "|pair|"); this.pairValidator.setMinimumNumberOfArguments(1); this.pairValidator.setMaximumNumberOfArguments(2); this.pairValidator.setFirstArgumentExpectedType(Symbol.class); this.executionContext = ExecutionContext.getInstance(); } @Override public SExpression call(Cons argumentList) { argumentValidator.validate(argumentList); Cons variableDefinitions = (Cons) argumentList.getFirst(); Cons body = (Cons) argumentList.getRest(); return evaluateInScope(variableDefinitions, body); } private SExpression evaluateInScope(Cons variableDefinitions, Cons body) { SymbolTable localScope = createLocalScope(variableDefinitions); SExpression lastEvaluation = evaluateBody(body); restorePreviousScope(localScope); return lastEvaluation; } protected SymbolTable createLocalScope(Cons variableDefinitions) { SymbolTable localScope = new SymbolTable(executionContext.getScope()); addVariablesToScope(localScope, variableDefinitions); executionContext.setScope(localScope); return localScope; } protected void addVariablesToScope(SymbolTable scope, Cons variableDefinitions) { variableDefinitionListValidator.validate(variableDefinitions); for (; variableDefinitions.isCons(); variableDefinitions = (Cons) variableDefinitions.getRest()) addPairToScope((Cons) variableDefinitions.getFirst(), scope); } private void addPairToScope(Cons symbolValuePair, SymbolTable scope) { pairValidator.validate(symbolValuePair); Cons restOfPair = (Cons) symbolValuePair.getRest(); SExpression symbol = symbolValuePair.getFirst(); SExpression value = restOfPair.getFirst(); scope.put(symbol.toString(), eval(value)); } private SExpression evaluateBody(Cons body) { SExpression lastEvaluation = NIL; for (; body.isCons(); body = (Cons) body.getRest()) lastEvaluation = eval(body.getFirst()); return lastEvaluation; } private void restorePreviousScope(SymbolTable localScope) { executionContext.setScope(localScope.getParent()); } }