diff --git a/pom.xml b/pom.xml
index 2eee15b..cefebdc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,18 +8,9 @@
transcendental-lisp
1.2.1
-
-
-
- kotlin-eap
- Kotlin EAP
- https://dl.bintray.com/kotlin/kotlin-eap
-
-
-
UTF-8
- 1.3.0-rc-190
+ 1.3.0
5.3.1
false
diff --git a/src/main/kotlin/function/builtin/Eval.kt b/src/main/kotlin/function/builtin/Eval.kt
index fce2880..0034853 100644
--- a/src/main/kotlin/function/builtin/Eval.kt
+++ b/src/main/kotlin/function/builtin/Eval.kt
@@ -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
diff --git a/src/main/kotlin/function/builtin/special/RECUR.java b/src/main/kotlin/function/builtin/special/RECUR.java
deleted file mode 100644
index 6a362a0..0000000
--- a/src/main/kotlin/function/builtin/special/RECUR.java
+++ /dev/null
@@ -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";
- }
- }
-}
diff --git a/src/main/kotlin/function/builtin/special/Recur.kt b/src/main/kotlin/function/builtin/special/Recur.kt
new file mode 100644
index 0000000..eecf84a
--- /dev/null
+++ b/src/main/kotlin/function/builtin/special/Recur.kt
@@ -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"
+ }
+}
diff --git a/src/test/kotlin/function/builtin/EvalTest.kt b/src/test/kotlin/function/builtin/EvalTest.kt
index 198200a..e5b6384 100644
--- a/src/test/kotlin/function/builtin/EvalTest.kt
+++ b/src/test/kotlin/function/builtin/EvalTest.kt
@@ -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
diff --git a/src/test/kotlin/function/builtin/special/RECURTest.java b/src/test/kotlin/function/builtin/special/RECURTest.java
deleted file mode 100644
index 56bb983..0000000
--- a/src/test/kotlin/function/builtin/special/RECURTest.java
+++ /dev/null
@@ -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());
- }
-}
diff --git a/src/test/kotlin/function/builtin/special/RecurTest.kt b/src/test/kotlin/function/builtin/special/RecurTest.kt
new file mode 100644
index 0000000..ae5a2e0
--- /dev/null
+++ b/src/test/kotlin/function/builtin/special/RecurTest.kt
@@ -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())
+ }
+}