transcendental-lisp/src/main/function/builtin/special/COND.java

69 lines
2.1 KiB
Java

package function.builtin.special;
import static function.builtin.EVAL.eval;
import static recursion.TailCalls.done;
import static recursion.TailCalls.tailCall;
import static sexpression.Nil.NIL;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispSpecialFunction;
import recursion.TailCall;
import sexpression.Cons;
import sexpression.Nil;
import sexpression.SExpression;
@FunctionNames({ "COND" })
public class COND extends LispSpecialFunction {
private ArgumentValidator argumentValidator;
public COND(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
this.argumentValidator.setEveryArgumentExcludedType(Nil.class);
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
return callTailRecursive(argumentList).invoke();
}
private TailCall<SExpression> callTailRecursive(Cons argumentList) {
if (argumentList.isNull())
return done(NIL);
Cons clause = (Cons) argumentList.getFirst();
Cons remainingClauses = (Cons) argumentList.getRest();
SExpression test = eval(clause.getFirst());
if (isTestSuccessful(test))
return done(evaluateConsequents(clause.getRest(), test));
return tailCall(() -> callTailRecursive(remainingClauses));
}
private boolean isTestSuccessful(SExpression test) {
return test != NIL;
}
private SExpression evaluateConsequents(SExpression consequentList, SExpression test) {
SExpression lastConsequentValue = test;
for (; consequentList.isCons(); consequentList = advanceCons(consequentList))
lastConsequentValue = eval(getFirst(consequentList));
return lastConsequentValue;
}
private SExpression advanceCons(SExpression knownCons) {
return ((Cons) knownCons).getRest();
}
private SExpression getFirst(SExpression knownCons) {
return ((Cons) knownCons).getFirst();
}
}