package function.builtin.special; import static testutil.TestUtilities.assertSExpressionsMatch; import static testutil.TestUtilities.evaluateString; import static testutil.TestUtilities.parseString; import org.junit.Test; import function.ArgumentValidator.BadArgumentTypeException; import function.ArgumentValidator.TooManyArgumentsException; import function.builtin.EVAL.RecurNotInTailPositionException; import function.builtin.special.RECUR.NestedRecurException; import function.builtin.special.RECUR.RecurOutsideOfFunctionException; import testutil.SymbolAndFunctionCleaner; public class RECURTest extends SymbolAndFunctionCleaner { @Test(expected = RecurOutsideOfFunctionException.class) public void recurOutsideOfFunction_ThrowsException() { evaluateString("(recur)"); } @Test(expected = RecurOutsideOfFunctionException.class) public void recurOutsideOfFunction_AfterFunctionCall_ThrowsException() { evaluateString("(defun f (n) (if (= n 0) 'ZERO n))"); evaluateString("(f 2)"); evaluateString("(recur)"); } @Test(expected = BadArgumentTypeException.class) public void recurInSpecialFunction_DoesNotEvaluateArguments() { evaluateString("(define-special tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))"); evaluateString("(tail-recursive 900)"); } @Test(expected = BadArgumentTypeException.class) public void recurInMacro_DoesNotEvaluateArguments() { evaluateString("(defmacro tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))"); evaluateString("(tail-recursive 900)"); } @Test(expected = NestedRecurException.class) public void nestedRecur_ThrowsException() { evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (recur (- n 1)))))"); evaluateString("(tail-recursive 900)"); } @Test(expected = TooManyArgumentsException.class) public void functionCallValidatesRecurArguments() { evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1) 23)))"); evaluateString("(tail-recursive 900)"); } @Test(expected = RecurNotInTailPositionException.class) public void recurInNonTailPosition_ThrowsException() { evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (list (recur (- n 1)))))"); evaluateString("(tail-recursive 900)"); } @Test public void recurCallsCurrentFunction() { evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))"); assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)")); } @Test public void recurCallsCurrentFunction_WithApply() { evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))"); assertSExpressionsMatch(parseString("PASS"), evaluateString("(apply 'tail-recursive '(900))")); } // recur with funcall // recur non-tail in apply call // recur with no args, alters global variable // recur with anonymous function // recur with nested anonymous function }