95 lines
3.3 KiB
Java
95 lines
3.3 KiB
Java
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());
|
|
}
|
|
|
|
}
|