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

84 lines
2.5 KiB
Java

package function.builtin.special;
import static function.builtin.EVAL.eval;
import static function.builtin.predicate.EQUAL.isEqual;
import static sexpression.Nil.NIL;
import static sexpression.Symbol.T;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispSpecialFunction;
import sexpression.Cons;
import sexpression.Nil;
import sexpression.SExpression;
@FunctionNames({ "CASE" })
public class CASE extends LispSpecialFunction {
private ArgumentValidator argumentValidator;
public CASE(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setMinimumNumberOfArguments(1);
this.argumentValidator.setTrailingArgumentExpectedType(Cons.class);
this.argumentValidator.setTrailingArgumentExcludedType(Nil.class);
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
SExpression key = eval(argumentList.getFirst());
return callTailRecursive(key, (Cons) argumentList.getRest());
}
private SExpression callTailRecursive(SExpression key, Cons argumentList) {
if (argumentList.isNull())
return NIL;
Cons clause = (Cons) argumentList.getFirst();
Cons remainingClauses = (Cons) argumentList.getRest();
SExpression keyList = clause.getFirst();
if (isMatch(key, keyList))
return evaluateConsequents(clause.getRest());
return callTailRecursive(key, remainingClauses);
}
private boolean isMatch(SExpression key, SExpression keyList) {
if (keyList.isNull())
return false;
else if (keyList.isCons())
return containsMatch(key, keyList);
return isEqual(key, keyList) || isEqual(T, keyList);
}
private boolean containsMatch(SExpression key, SExpression keyList) {
for (; keyList.isCons(); keyList = advanceCons(keyList))
if (isEqual(key, getFirst(keyList)))
return true;
return false;
}
private SExpression advanceCons(SExpression knownCons) {
return ((Cons) knownCons).getRest();
}
private SExpression getFirst(SExpression knownCons) {
return ((Cons) knownCons).getFirst();
}
private SExpression evaluateConsequents(SExpression consequentList) {
SExpression lastConsequentValue = NIL;
for (; consequentList.isCons(); consequentList = advanceCons(consequentList))
lastConsequentValue = eval(getFirst(consequentList));
return lastConsequentValue;
}
}