Convert Recur to kotlin
This commit is contained in:
parent
73bcd4da38
commit
b4de8fc3ea
11
pom.xml
11
pom.xml
|
@ -8,18 +8,9 @@
|
|||
<artifactId>transcendental-lisp</artifactId>
|
||||
<version>1.2.1</version>
|
||||
|
||||
<!-- TODO - remove after kotlin 1.3 is released -->
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>kotlin-eap</id>
|
||||
<name>Kotlin EAP</name>
|
||||
<url>https://dl.bintray.com/kotlin/kotlin-eap</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.version>1.3.0-rc-190</kotlin.version>
|
||||
<kotlin.version>1.3.0</kotlin.version>
|
||||
<junit5.version>5.3.1</junit5.version>
|
||||
<skipTests>false</skipTests>
|
||||
</properties>
|
||||
|
|
|
@ -7,7 +7,7 @@ import function.LispFunction
|
|||
import function.builtin.cons.LIST.makeList
|
||||
import function.builtin.special.Lambda.Lambda.createFunction
|
||||
import function.builtin.special.Lambda.Lambda.isLambdaExpression
|
||||
import function.builtin.special.RECUR.RecurNotInTailPositionException
|
||||
import function.builtin.special.Recur.RecurNotInTailPositionException
|
||||
import sexpression.BackquoteExpression
|
||||
import sexpression.Cons
|
||||
import sexpression.LambdaExpression
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
package function.builtin.special;
|
||||
|
||||
import error.LispException;
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispSpecialFunction;
|
||||
import sexpression.Cons;
|
||||
import sexpression.SExpression;
|
||||
import table.ExecutionContext;
|
||||
|
||||
import static function.builtin.Eval.evaluateFunctionArgumentList;
|
||||
|
||||
@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.INSTANCE;
|
||||
}
|
||||
|
||||
@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";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package function.builtin.special
|
||||
|
||||
import error.LispException
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispSpecialFunction
|
||||
import function.builtin.Eval.Companion.evaluateFunctionArgumentList
|
||||
import sexpression.Cons
|
||||
import sexpression.SExpression
|
||||
import table.ExecutionContext
|
||||
|
||||
@FunctionNames("RECUR")
|
||||
class Recur(name: String) : LispSpecialFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name)
|
||||
|
||||
private fun isRecurArgumentListEvaluated() =
|
||||
ExecutionContext.getCurrentFunction().isArgumentListEvaluated
|
||||
|
||||
override fun call(argumentList: Cons): SExpression {
|
||||
verifyValidRecurCall()
|
||||
argumentValidator.validate(argumentList)
|
||||
val recurArguments = getRecurArguments(argumentList)
|
||||
ExecutionContext.setRecur()
|
||||
|
||||
return recurArguments
|
||||
}
|
||||
|
||||
private fun verifyValidRecurCall() {
|
||||
if (!ExecutionContext.isInFunctionCall())
|
||||
throw RecurOutsideOfFunctionException()
|
||||
|
||||
if (ExecutionContext.isRecurInitializing())
|
||||
throw NestedRecurException()
|
||||
}
|
||||
|
||||
private fun getRecurArguments(argumentList: Cons): Cons {
|
||||
var recurArguments = argumentList
|
||||
|
||||
try {
|
||||
ExecutionContext.setRecurInitializing()
|
||||
|
||||
if (isRecurArgumentListEvaluated())
|
||||
recurArguments = evaluateFunctionArgumentList(argumentList)
|
||||
} finally {
|
||||
ExecutionContext.clearRecurInitializing()
|
||||
}
|
||||
|
||||
return recurArguments
|
||||
}
|
||||
|
||||
class RecurOutsideOfFunctionException : LispException() {
|
||||
|
||||
override val message = "recur called outside of function"
|
||||
}
|
||||
|
||||
class NestedRecurException : LispException() {
|
||||
|
||||
override val message = "nested call to recur"
|
||||
}
|
||||
|
||||
class RecurNotInTailPositionException : LispException() {
|
||||
|
||||
override val message = "recur not in tail position"
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ import function.builtin.Eval.UndefinedFunctionException
|
|||
import function.builtin.Eval.UndefinedSymbolException
|
||||
import function.builtin.Eval.UnmatchedAtSignException
|
||||
import function.builtin.Eval.UnmatchedCommaException
|
||||
import function.builtin.special.RECUR.RecurNotInTailPositionException
|
||||
import function.builtin.special.Recur.RecurNotInTailPositionException
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import org.junit.jupiter.api.Test
|
||||
|
|
|
@ -1,192 +0,0 @@
|
|||
package function.builtin.special;
|
||||
|
||||
import function.ArgumentValidator.BadArgumentTypeException;
|
||||
import function.ArgumentValidator.TooManyArgumentsException;
|
||||
import function.builtin.special.RECUR.NestedRecurException;
|
||||
import function.builtin.special.RECUR.RecurNotInTailPositionException;
|
||||
import function.builtin.special.RECUR.RecurOutsideOfFunctionException;
|
||||
import org.junit.Test;
|
||||
import sexpression.SExpression;
|
||||
import testutil.SymbolAndFunctionCleaner;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
import static testutil.TestUtilities.assertIsErrorWithMessage;
|
||||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
||||
import static testutil.TestUtilities.evaluateString;
|
||||
import static testutil.TestUtilities.parseString;
|
||||
|
||||
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 recurInNonTailPositionInArgumentList() {
|
||||
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (list (recur (- n 1)))))");
|
||||
evaluateString("(tail-recursive 900)");
|
||||
}
|
||||
|
||||
@Test(expected = RecurNotInTailPositionException.class)
|
||||
public void recurInNonTailPositionInBegin() {
|
||||
evaluateString("(defun tail-recursive (n) (begin (recur) 2))");
|
||||
evaluateString("(tail-recursive 900)");
|
||||
}
|
||||
|
||||
@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)");
|
||||
}
|
||||
|
||||
@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_InBegin() {
|
||||
evaluateString("(defun tail-recursive (n) (if (> n 1) (begin 1 2 (recur (- n 1))) 'PASS))");
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
|
||||
}
|
||||
|
||||
@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)"));
|
||||
}
|
||||
|
||||
@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))"));
|
||||
}
|
||||
|
||||
@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)"));
|
||||
}
|
||||
|
||||
@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)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recurWithLambda() {
|
||||
SExpression lambdaTailCall = evaluateString("((lambda (n) (if (= n 0) 'PASS (recur (- n 1)))) 2020)");
|
||||
assertSExpressionsMatch(parseString("PASS"), lambdaTailCall);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recurWithNestedLambda() {
|
||||
evaluateString("(defun nested-tail () ((lambda (n) (if (= n 0) 'PASS (recur (- n 1)))) 2020))");
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(nested-tail)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recurWithNestedTailRecursiveFunction() {
|
||||
evaluateString("(defun one (n) (if (= n 0) 0 (recur (- n 1))))");
|
||||
evaluateString("(defun two (n) (if (= n 0) 'PASS (recur (one n))))");
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(two 20)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedRecurException_HasCorrectAttributes() {
|
||||
assertIsErrorWithMessage(new NestedRecurException());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recrOutsideOfFunctionException_HasCorrectAttributes() {
|
||||
assertIsErrorWithMessage(new RecurOutsideOfFunctionException());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recurNotInTailPositionException_HasCorrectAttributes() {
|
||||
assertIsErrorWithMessage(new RecurNotInTailPositionException());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
package function.builtin.special
|
||||
|
||||
import function.ArgumentValidator.BadArgumentTypeException
|
||||
import function.ArgumentValidator.TooManyArgumentsException
|
||||
import function.builtin.special.Recur.NestedRecurException
|
||||
import function.builtin.special.Recur.RecurNotInTailPositionException
|
||||
import function.builtin.special.Recur.RecurOutsideOfFunctionException
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.fail
|
||||
import testutil.LispTestInstance
|
||||
import testutil.SymbolAndFunctionCleaner
|
||||
import testutil.TestUtilities.assertIsErrorWithMessage
|
||||
import testutil.TestUtilities.assertSExpressionsMatch
|
||||
import testutil.TestUtilities.evaluateString
|
||||
import testutil.TestUtilities.parseString
|
||||
|
||||
@LispTestInstance
|
||||
class RecurTest : SymbolAndFunctionCleaner() {
|
||||
|
||||
@Test
|
||||
fun recurOutsideOfFunction_ThrowsException() {
|
||||
assertThrows(RecurOutsideOfFunctionException::class.java) {
|
||||
evaluateString("(recur)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurOutsideOfFunction_AfterFunctionCall_ThrowsException() {
|
||||
evaluateString("(defun f (n) (if (= n 0) 'ZERO n))")
|
||||
evaluateString("(f 2)")
|
||||
|
||||
assertThrows(RecurOutsideOfFunctionException::class.java) {
|
||||
evaluateString("(recur)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurInSpecialFunction_DoesNotEvaluateArguments() {
|
||||
evaluateString("(define-special tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))")
|
||||
|
||||
assertThrows(BadArgumentTypeException::class.java) {
|
||||
evaluateString("(tail-recursive 900)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurInMacro_DoesNotEvaluateArguments() {
|
||||
evaluateString("(defmacro tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))")
|
||||
|
||||
assertThrows(BadArgumentTypeException::class.java) {
|
||||
evaluateString("(tail-recursive 900)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nestedRecur_ThrowsException() {
|
||||
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (recur (- n 1)))))")
|
||||
|
||||
assertThrows(NestedRecurException::class.java) {
|
||||
evaluateString("(tail-recursive 900)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun functionCallValidatesRecurArguments() {
|
||||
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1) 23)))")
|
||||
|
||||
assertThrows(TooManyArgumentsException::class.java) {
|
||||
evaluateString("(tail-recursive 900)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurInNonTailPositionInArgumentList() {
|
||||
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (list (recur (- n 1)))))")
|
||||
|
||||
assertThrows(RecurNotInTailPositionException::class.java) {
|
||||
evaluateString("(tail-recursive 900)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurInNonTailPositionInBegin() {
|
||||
evaluateString("(defun tail-recursive (n) (begin (recur) 2))")
|
||||
|
||||
assertThrows(RecurNotInTailPositionException::class.java) {
|
||||
evaluateString("(tail-recursive 900)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurInNonTailPositionInApply() {
|
||||
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (apply 'list (recur (- n 1)))))")
|
||||
|
||||
assertThrows(RecurNotInTailPositionException::class.java) {
|
||||
evaluateString("(tail-recursive 900)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurCallsCurrentFunction() {
|
||||
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))")
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurCallsCurrentFunction_InBegin() {
|
||||
evaluateString("(defun tail-recursive (n) (if (> n 1) (begin 1 2 (recur (- n 1))) 'PASS))")
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurInTailPositionWithApply() {
|
||||
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (apply 'recur (list (- n 1)))))")
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurInTailPositionWithFuncall() {
|
||||
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (call 'recur (- n 1))))")
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurCallsCurrentFunction_WithApply() {
|
||||
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))")
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(apply 'tail-recursive '(900))"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurCallsCurrentFunction_WithFuncall() {
|
||||
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))")
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(call 'tail-recursive '900)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun 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 (e: RecurNotInTailPositionException) {
|
||||
}
|
||||
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun 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 (e: RecurNotInTailPositionException) {
|
||||
}
|
||||
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun 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 (e: NestedRecurException) {
|
||||
}
|
||||
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun 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)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurWithLambda() {
|
||||
val lambdaTailCall = evaluateString("((lambda (n) (if (= n 0) 'PASS (recur (- n 1)))) 2020)")
|
||||
assertSExpressionsMatch(parseString("PASS"), lambdaTailCall)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurWithNestedLambda() {
|
||||
evaluateString("(defun nested-tail () ((lambda (n) (if (= n 0) 'PASS (recur (- n 1)))) 2020))")
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(nested-tail)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurWithNestedTailRecursiveFunction() {
|
||||
evaluateString("(defun one (n) (if (= n 0) 0 (recur (- n 1))))")
|
||||
evaluateString("(defun two (n) (if (= n 0) 'PASS (recur (one n))))")
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(two 20)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nestedRecurException_HasCorrectAttributes() {
|
||||
assertIsErrorWithMessage(NestedRecurException())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurOutsideOfFunctionException_HasCorrectAttributes() {
|
||||
assertIsErrorWithMessage(RecurOutsideOfFunctionException())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recurNotInTailPositionException_HasCorrectAttributes() {
|
||||
assertIsErrorWithMessage(RecurNotInTailPositionException())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue