Narrow the behavior of RECUR
This commit is contained in:
parent
b4229c6ac1
commit
6cf017734f
|
@ -17,7 +17,7 @@
|
|||
|
||||
(defun many-years-with-no-interest-rate ()
|
||||
(setq compounder (interest-compounder 1000 0))
|
||||
(call compounder :move-forward-years 83)
|
||||
(call compounder :move-forward-years 1803)
|
||||
(call assertions :assert= 1000 (call compounder :get-principal)))
|
||||
|
||||
(defun no-years-with-positive-interest-rate ()
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
(setq principal
|
||||
(+ principal
|
||||
(call static :percent-of-number principal interest-rate)))
|
||||
(call private :compound-interest (- years 1)))))))
|
||||
(recur (- years 1)))))))
|
||||
|
||||
(setq public
|
||||
(dlambda
|
||||
|
|
|
@ -89,17 +89,16 @@ public class UserDefinedFunction extends LispFunction {
|
|||
|
||||
@Override
|
||||
public SExpression call(Cons argumentList) {
|
||||
return callTailRecursive(argumentList).invoke();
|
||||
executionContext.pushFunctionCall(this);
|
||||
SExpression result = callTailRecursive(argumentList).invoke();
|
||||
executionContext.popFunctionCall();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private TailCall<SExpression> callTailRecursive(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
// if recur
|
||||
// clear recur indicator
|
||||
// validate function list
|
||||
// do not evaluate arguments - will have been done already if necessary
|
||||
// tailCall(evaluatedArguments)
|
||||
SExpression result = evaluateInFunctionScope(argumentList);
|
||||
|
||||
if (executionContext.isRecur()) {
|
||||
|
|
|
@ -12,7 +12,6 @@ import error.LispException;
|
|||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import function.UserDefinedFunction;
|
||||
import sexpression.BackquoteExpression;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LambdaExpression;
|
||||
|
@ -29,7 +28,7 @@ public class EVAL extends LispFunction {
|
|||
return lookupFunction("EVAL").call(argumentList);
|
||||
}
|
||||
|
||||
public static Cons evalArgumentList(Cons argumentList) {
|
||||
public static Cons evalRecurArgumentList(Cons argumentList) {
|
||||
return ((EVAL) lookupFunction("EVAL")).evaluateArgumentList(argumentList);
|
||||
}
|
||||
|
||||
|
@ -116,8 +115,11 @@ public class EVAL extends LispFunction {
|
|||
}
|
||||
|
||||
private SExpression callFunction(LispFunction function, Cons argumentList) {
|
||||
if (function instanceof UserDefinedFunction)
|
||||
executionContext.pushFunctionCall(function);
|
||||
|
||||
// if (executionContext.isRecur()) {
|
||||
// executionContext.popFunctionCall(); // FIXME - clear this in repl?
|
||||
// throw new RecurNotInTailPositionException();
|
||||
// }
|
||||
|
||||
if (function.isArgumentListEvaluated())
|
||||
argumentList = evaluateArgumentList(argumentList);
|
||||
|
@ -127,9 +129,6 @@ public class EVAL extends LispFunction {
|
|||
if (function.isMacro())
|
||||
result = eval(result);
|
||||
|
||||
if (function instanceof UserDefinedFunction)
|
||||
executionContext.popFunctionCall();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -208,4 +207,14 @@ public class EVAL extends LispFunction {
|
|||
}
|
||||
}
|
||||
|
||||
public static class RecurNotInTailPositionException extends LispException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "recur not in tail position";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package function.builtin.special;
|
||||
|
||||
import static function.builtin.EVAL.evalArgumentList;
|
||||
import static function.builtin.EVAL.evalRecurArgumentList;
|
||||
|
||||
import error.LispException;
|
||||
import function.ArgumentValidator;
|
||||
|
@ -24,19 +24,20 @@ public class RECUR extends LispSpecialFunction {
|
|||
|
||||
@Override
|
||||
public SExpression call(Cons argumentList) {
|
||||
if (executionContext.isRecur())
|
||||
throw new NestedRecurException();
|
||||
|
||||
if (!executionContext.isInFunctionCall())
|
||||
throw new RecurOutsideOfFunctionException();
|
||||
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
if (!executionContext.isInFunctionCall()) {
|
||||
throw new RecurOutsideOfFunctionException();
|
||||
}
|
||||
|
||||
executionContext.setRecur();
|
||||
LispFunction currentFunction = executionContext.getCurrentFunction();
|
||||
Cons recurArguments = argumentList;
|
||||
|
||||
if (currentFunction.isArgumentListEvaluated())
|
||||
recurArguments = evalArgumentList(argumentList);
|
||||
|
||||
executionContext.setRecur();
|
||||
recurArguments = evalRecurArgumentList(argumentList);
|
||||
|
||||
return recurArguments;
|
||||
}
|
||||
|
@ -51,4 +52,14 @@ public class RECUR extends LispSpecialFunction {
|
|||
}
|
||||
}
|
||||
|
||||
public static class NestedRecurException extends LispException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "nested call to recur";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ 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;
|
||||
|
||||
|
@ -24,16 +27,57 @@ public class RECURTest extends SymbolAndFunctionCleaner {
|
|||
evaluateString("(recur)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recurCallsCurrentFunction() {
|
||||
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
|
||||
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue