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 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(); } }