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>
|
<artifactId>transcendental-lisp</artifactId>
|
||||||
<version>1.2.1</version>
|
<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>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<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>
|
<junit5.version>5.3.1</junit5.version>
|
||||||
<skipTests>false</skipTests>
|
<skipTests>false</skipTests>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import function.LispFunction
|
||||||
import function.builtin.cons.LIST.makeList
|
import function.builtin.cons.LIST.makeList
|
||||||
import function.builtin.special.Lambda.Lambda.createFunction
|
import function.builtin.special.Lambda.Lambda.createFunction
|
||||||
import function.builtin.special.Lambda.Lambda.isLambdaExpression
|
import function.builtin.special.Lambda.Lambda.isLambdaExpression
|
||||||
import function.builtin.special.RECUR.RecurNotInTailPositionException
|
import function.builtin.special.Recur.RecurNotInTailPositionException
|
||||||
import sexpression.BackquoteExpression
|
import sexpression.BackquoteExpression
|
||||||
import sexpression.Cons
|
import sexpression.Cons
|
||||||
import sexpression.LambdaExpression
|
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.UndefinedSymbolException
|
||||||
import function.builtin.Eval.UnmatchedAtSignException
|
import function.builtin.Eval.UnmatchedAtSignException
|
||||||
import function.builtin.Eval.UnmatchedCommaException
|
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.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.Assertions.assertThrows
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
import org.junit.jupiter.api.Test
|
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