2017-11-16 20:53:55 -05:00
|
|
|
package function.builtin.special;
|
|
|
|
|
2017-11-17 19:14:59 -05:00
|
|
|
import static org.junit.Assert.fail;
|
2017-11-16 20:53:55 -05:00
|
|
|
import static testutil.TestUtilities.assertSExpressionsMatch;
|
|
|
|
import static testutil.TestUtilities.evaluateString;
|
|
|
|
import static testutil.TestUtilities.parseString;
|
|
|
|
|
|
|
|
import org.junit.Test;
|
|
|
|
|
|
|
|
import function.ArgumentValidator.BadArgumentTypeException;
|
2017-11-17 17:57:14 -05:00
|
|
|
import function.ArgumentValidator.TooManyArgumentsException;
|
|
|
|
import function.builtin.special.RECUR.NestedRecurException;
|
2017-11-18 09:24:45 -05:00
|
|
|
import function.builtin.special.RECUR.RecurNotInTailPositionException;
|
2017-11-16 20:53:55 -05:00
|
|
|
import function.builtin.special.RECUR.RecurOutsideOfFunctionException;
|
2017-11-18 09:24:45 -05:00
|
|
|
import sexpression.SExpression;
|
2017-11-16 20:53:55 -05:00
|
|
|
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)");
|
|
|
|
}
|
|
|
|
|
2017-11-17 17:57:14 -05:00
|
|
|
@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)
|
2017-11-18 09:24:45 -05:00
|
|
|
public void recurInNonTailPositionInArgumentList() {
|
2017-11-17 17:57:14 -05:00
|
|
|
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (list (recur (- n 1)))))");
|
|
|
|
evaluateString("(tail-recursive 900)");
|
|
|
|
}
|
|
|
|
|
2017-11-17 19:14:59 -05:00
|
|
|
@Test(expected = RecurNotInTailPositionException.class)
|
2017-11-18 09:24:45 -05:00
|
|
|
public void recurInNonTailPositionInBegin() {
|
2017-11-17 19:14:59 -05:00
|
|
|
evaluateString("(defun tail-recursive (n) (begin (recur) 2))");
|
|
|
|
evaluateString("(tail-recursive 900)");
|
|
|
|
}
|
|
|
|
|
2017-11-18 10:21:57 -05:00
|
|
|
@Test(expected = RecurNotInTailPositionException.class)
|
|
|
|
public void recurInNonTailPositionInApply() {
|
|
|
|
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (apply 'list (recur (- n 1)))))");
|
|
|
|
evaluateString("(tail-recursive 900)");
|
|
|
|
}
|
|
|
|
|
2017-11-16 20:53:55 -05:00
|
|
|
@Test
|
|
|
|
public void recurCallsCurrentFunction() {
|
|
|
|
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
|
|
|
|
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
|
|
|
|
}
|
|
|
|
|
2017-11-17 19:14:59 -05:00
|
|
|
@Test
|
|
|
|
public void recurCallsCurrentFunction_InBegin() {
|
|
|
|
evaluateString("(defun tail-recursive (n) (if (> n 1) (begin 1 2 (recur (- n 1))) 'PASS))");
|
|
|
|
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
|
|
|
|
}
|
|
|
|
|
2017-11-18 09:24:45 -05:00
|
|
|
@Test
|
|
|
|
public void recurInTailPositionWithApply() {
|
|
|
|
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (apply 'recur (list (- n 1)))))");
|
|
|
|
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void recurInTailPositionWithFuncall() {
|
|
|
|
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (call 'recur (- n 1))))");
|
|
|
|
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
|
|
|
|
}
|
|
|
|
|
2017-11-17 17:57:14 -05:00
|
|
|
@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))"));
|
2017-11-16 20:53:55 -05:00
|
|
|
}
|
2017-11-17 19:14:59 -05:00
|
|
|
|
|
|
|
@Test
|
|
|
|
public void recurCallsCurrentFunction_WithFuncall() {
|
|
|
|
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
|
|
|
|
assertSExpressionsMatch(parseString("PASS"), evaluateString("(call 'tail-recursive '900)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void recurWorksAfterFailure() {
|
|
|
|
evaluateString("(defun bad-tail-recursive (n) (if (= n 0) 'PASS (list (recur (- n 1)))))");
|
|
|
|
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
|
|
|
|
|
|
|
|
try {
|
|
|
|
evaluateString("(bad-tail-recursive 900)");
|
|
|
|
fail("expectedException");
|
|
|
|
} catch (RecurNotInTailPositionException e) {}
|
|
|
|
|
|
|
|
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void recurWorksAfterFailure2() {
|
|
|
|
evaluateString("(defun bad-tail-recursive (n) (begin (recur) 2))");
|
|
|
|
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
|
|
|
|
|
|
|
|
try {
|
|
|
|
evaluateString("(bad-tail-recursive 900)");
|
|
|
|
fail("expectedException");
|
|
|
|
} catch (RecurNotInTailPositionException e) {}
|
|
|
|
|
|
|
|
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void recurWorksAfterNestedFailure() {
|
|
|
|
evaluateString("(defun bad-tail-recursive (n) (if (= n 0) 'PASS (recur (recur (- n 1)))))");
|
|
|
|
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
|
|
|
|
|
|
|
|
try {
|
|
|
|
evaluateString("(bad-tail-recursive 900)");
|
|
|
|
fail("expectedException");
|
|
|
|
} catch (NestedRecurException e) {}
|
|
|
|
|
|
|
|
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
|
|
|
|
}
|
2017-11-17 17:57:14 -05:00
|
|
|
|
2017-11-18 09:24:45 -05:00
|
|
|
@Test
|
|
|
|
public void recurWithNoArgs_AltersGlobalVariable() {
|
|
|
|
evaluateString("(defun tail-recursive () (if (= n 0) 'PASS (begin (setq n (- n 1)) (recur))))");
|
|
|
|
evaluateString("(setq n 200)");
|
|
|
|
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive)"));
|
|
|
|
}
|
2017-11-17 17:57:14 -05:00
|
|
|
|
2017-11-18 09:24:45 -05:00
|
|
|
@Test
|
|
|
|
public void recurWithLambda() {
|
|
|
|
SExpression lambdaTailCall = evaluateString("((lambda (n) (if (= n 0) 'PASS (recur (- n 1)))) 2020)");
|
|
|
|
assertSExpressionsMatch(parseString("PASS"), lambdaTailCall);
|
|
|
|
}
|
2017-11-16 20:53:55 -05:00
|
|
|
|
2017-11-18 09:24:45 -05:00
|
|
|
@Test
|
|
|
|
public void recurWithNestedLambda() {
|
|
|
|
evaluateString("(defun nested-tail () ((lambda (n) (if (= n 0) 'PASS (recur (- n 1)))) 2020))");
|
|
|
|
assertSExpressionsMatch(parseString("PASS"), evaluateString("(nested-tail)"));
|
|
|
|
}
|
2017-11-17 19:14:59 -05:00
|
|
|
|
2017-11-16 20:53:55 -05:00
|
|
|
}
|