transcendental-lisp/src/function/builtin/special/LET.java

87 lines
3.0 KiB
Java

package function.builtin.special;
import static function.builtin.EVAL.eval;
import function.*;
import sexpression.*;
import table.*;
@FunctionNames({ "LET" })
public class LET extends LispSpecialFunction {
private ArgumentValidator argumentValidator;
private ArgumentValidator variableDefinitionListValidator;
private ArgumentValidator pairValidator;
private ExecutionContext executionContext;
public LET() {
this.argumentValidator = new ArgumentValidator("LET");
this.argumentValidator.setMinimumNumberOfArguments(1);
this.argumentValidator.setFirstArgumentExpectedType(Cons.class);
this.variableDefinitionListValidator = new ArgumentValidator("LET|pair-list|");
this.variableDefinitionListValidator.setEveryArgumentExpectedType(Cons.class);
this.pairValidator = new ArgumentValidator("LET|pair|");
this.pairValidator.setExactNumberOfArguments(2);
this.pairValidator.setFirstArgumentExpectedType(Symbol.class);
this.executionContext = ExecutionContext.getInstance();
}
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;
}
private SymbolTable createLocalScope(Cons variableDefinitions) {
SymbolTable localScope = new SymbolTable(executionContext.getScope());
addVariablesToScope(localScope, variableDefinitions);
executionContext.setScope(localScope);
return localScope;
}
private 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.getInstance();
for (; body.isCons(); body = (Cons) body.getRest())
lastEvaluation = eval(body.getFirst());
return lastEvaluation;
}
private void restorePreviousScope(SymbolTable localScope) {
executionContext.setScope(localScope.getParent());
}
}