package function.builtin.special; import static function.builtin.EVAL.evaluateFunctionArgumentList; import error.LispException; import function.ArgumentValidator; import function.FunctionNames; import function.LispSpecialFunction; import sexpression.Cons; import sexpression.SExpression; import table.ExecutionContext; @FunctionNames({ "RECUR" }) public class RECUR extends LispSpecialFunction { private ArgumentValidator argumentValidator; private ExecutionContext executionContext; public RECUR(String name) { this.argumentValidator = new ArgumentValidator(name); this.executionContext = ExecutionContext.getInstance(); } @Override public SExpression call(Cons argumentList) { verifyValidRecurCall(); argumentValidator.validate(argumentList); Cons recurArguments = getRecurArguments(argumentList); executionContext.setRecur(); return recurArguments; } private void verifyValidRecurCall() { if (!executionContext.isInFunctionCall()) throw new RecurOutsideOfFunctionException(); if (executionContext.isRecurInitializing()) throw new NestedRecurException(); } private Cons getRecurArguments(Cons argumentList) { Cons recurArguments = argumentList; try { executionContext.setRecurInitializing(); if (isRecurArgumentListEvaluated()) recurArguments = evaluateFunctionArgumentList(argumentList); } finally { executionContext.clearRecurInitializing(); } return recurArguments; } private boolean isRecurArgumentListEvaluated() { return executionContext.getCurrentFunction().isArgumentListEvaluated(); } public static class RecurOutsideOfFunctionException extends LispException { private static final long serialVersionUID = 1L; @Override public String getMessage() { return "recur called outide of function"; } } public static class NestedRecurException extends LispException { private static final long serialVersionUID = 1L; @Override public String getMessage() { return "nested call to recur"; } } public static class RecurNotInTailPositionException extends LispException { private static final long serialVersionUID = 1L; @Override public String getMessage() { return "recur not in tail position"; } } }