User defined functions now set up their scope correctly
This commit is contained in:
parent
462e5ea15e
commit
bf40feadec
|
@ -2,14 +2,13 @@
|
|||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="test"/>
|
||||
<classpathentry kind="src" path="acceptance"/>
|
||||
<classpathentry kind="src" path="acctest"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||
<attributes>
|
||||
<attribute name="owner.project.facets" value="java"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||
<classpathentry kind="lib" path="fitnesse/fitnesse-standalone.jar"/>
|
||||
<classpathentry kind="lib" path="lib/recursion.jar"/>
|
||||
<classpathentry kind="output" path="build/classes"/>
|
||||
</classpath>
|
||||
|
|
|
@ -6,23 +6,18 @@ import interpreter.*;
|
|||
|
||||
public class LispInterpreterFixture {
|
||||
|
||||
private ByteArrayOutputStream outputStream;
|
||||
private LispInterpreter interpreter;
|
||||
private static ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
private static LispInterpreter interpreter = null;
|
||||
|
||||
public LispInterpreterFixture() throws IOException {
|
||||
this.outputStream = new ByteArrayOutputStream();
|
||||
this.interpreter = buildInterpreter();
|
||||
}
|
||||
|
||||
private LispInterpreter buildInterpreter() {
|
||||
static {
|
||||
LispInterpreterBuilder builder = LispInterpreterBuilderImpl.getInstance();
|
||||
builder.setOutput(new PrintStream(outputStream));
|
||||
builder.setErrorOutput(new PrintStream(outputStream));
|
||||
builder.setNotInteractive();
|
||||
builder.setTerminationFunction(() -> System.exit(0));
|
||||
builder.setErrorTerminationFunction(() -> System.exit(1));
|
||||
builder.setTerminationFunction(() -> {});
|
||||
builder.setErrorTerminationFunction(() -> {});
|
||||
|
||||
return builder.build();
|
||||
interpreter = builder.build();
|
||||
}
|
||||
|
||||
public String evaluate(String input) throws IOException {
|
|
@ -1,7 +0,0 @@
|
|||
---
|
||||
Test
|
||||
---
|
||||
| script | lisp interpreter fixture |
|
||||
| evaluate | (defun adderx (x) (lambda (y) (+ x y))) |
|
||||
| evaluate | (setf adder20 (adderx 20)) |
|
||||
| check | evaluate | (funcall adder20 2) | 22 |
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
Test
|
||||
---
|
||||
| script | lisp interpreter fixture |
|
||||
| evaluate | (defun adderx (x) (lambda (y) (+ x y))) |
|
||||
| evaluate | (setf adder20 (adderx 20)) |
|
||||
| check | evaluate | (funcall adder20 2) | 22 |
|
||||
|
||||
|
||||
| script | lisp interpreter fixture |
|
||||
| evaluate |{{{!-
|
||||
|
||||
(let ((direction 'up))
|
||||
(defun toggle-counter-direction ()
|
||||
(setq direction
|
||||
(if (eq direction 'up)
|
||||
'down
|
||||
'up)))
|
||||
|
||||
(defun counter-class ()
|
||||
(let ((counter 0))
|
||||
(lambda ()
|
||||
(if (eq direction 'up)
|
||||
(setq counter (+ counter 1))
|
||||
(setq counter (- counter 1)))))))
|
||||
-!}}}|
|
||||
| evaluate | (setq my-counter (counter-class)) |
|
||||
| check | evaluate | (funcall my-counter) | 1 |
|
||||
| check | evaluate | (funcall my-counter) | 2 |
|
||||
| check | evaluate | (funcall my-counter) | 3 |
|
||||
| evaluate | (toggle-counter-direction) |
|
||||
| check | evaluate | (funcall my-counter) | 2 |
|
||||
| check | evaluate | (funcall my-counter) | 1 |
|
||||
| check | evaluate | (funcall my-counter) | 0 |
|
||||
| evaluate | (toggle-counter-direction) |
|
||||
| check | evaluate | (funcall my-counter) | 1 |
|
||||
| check | evaluate | (funcall my-counter) | 2 |
|
||||
| check | evaluate | (funcall my-counter) | 3 |
|
|
@ -1,3 +1,5 @@
|
|||
|LispInterpreter.LexicalClosures||11:52:29 Mon, Feb 27, 2017|
|
||||
|LispInterpreter.TestClosure||11:24:27 Mon, Feb 27, 2017|
|
||||
|LispInterpreter.TestOne||09:26:08 Fri, Feb 24, 2017|
|
||||
|LispInterpreter.SuiteSetUp||14:27:52 Wed, Feb 22, 2017|
|
||||
|LispInterpreter||14:24:41 Wed, Feb 22, 2017|
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
(defun fact (x)
|
||||
(cond ((> 2 x) 1)
|
||||
(t (* x (fact (- x 1))))))
|
||||
(if (< x 2) 1
|
||||
(* x (fact (- x 1)))
|
||||
)
|
||||
)
|
||||
|
|
|
@ -34,26 +34,30 @@ public class UserDefinedFunction extends LispFunction {
|
|||
public SExpression call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
bindParameterValues(argumentList);
|
||||
return evaluateInFunctionScope(argumentList);
|
||||
}
|
||||
|
||||
private SExpression evaluateInFunctionScope(Cons argumentList) {
|
||||
SymbolTable callingScope = executionContext.getScope();
|
||||
executionContext.setScope(functionScope);
|
||||
SymbolTable executionScope = bindParameterValuesToFunctionScope(argumentList);
|
||||
|
||||
executionContext.setScope(executionScope);
|
||||
SExpression lastEvaluation = evaluateBody();
|
||||
|
||||
executionContext.setScope(callingScope);
|
||||
releaseParameterValues();
|
||||
|
||||
return lastEvaluation;
|
||||
}
|
||||
|
||||
private void bindParameterValues(Cons argumentList) {
|
||||
functionScope = new SymbolTable(functionScope);
|
||||
private SymbolTable bindParameterValuesToFunctionScope(Cons argumentList) {
|
||||
SymbolTable executionScope = new SymbolTable(functionScope);
|
||||
|
||||
for (String parameter : formalParameters) {
|
||||
SExpression currentArg = argumentList.getFirst();
|
||||
functionScope.put(parameter, currentArg);
|
||||
executionScope.put(parameter, currentArg);
|
||||
argumentList = (Cons) argumentList.getRest();
|
||||
}
|
||||
|
||||
return executionScope;
|
||||
}
|
||||
|
||||
private SExpression evaluateBody() {
|
||||
|
@ -65,10 +69,6 @@ public class UserDefinedFunction extends LispFunction {
|
|||
return lastEvaluation;
|
||||
}
|
||||
|
||||
private void releaseParameterValues() {
|
||||
functionScope = new SymbolTable(functionScope.getParent());
|
||||
}
|
||||
|
||||
public Cons getLambdaExpression() {
|
||||
return lambdaExpression;
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public class DEFUNTester {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testDefun() {
|
||||
public void defun() {
|
||||
String input = "(defun f () t)";
|
||||
|
||||
assertSExpressionsMatch(parseString("f"), evaluateString(input));
|
||||
|
@ -50,7 +50,7 @@ public class DEFUNTester {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testDefunWithEmptyBody() {
|
||||
public void defunWithEmptyBody() {
|
||||
String input = "(defun f ())";
|
||||
|
||||
assertSExpressionsMatch(parseString("f"), evaluateString(input));
|
||||
|
@ -58,11 +58,34 @@ public class DEFUNTester {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testDefunEvaluatesArguments() {
|
||||
public void defunEvaluatesArguments() {
|
||||
evaluateString("(defun f (x) (car x))");
|
||||
assertSExpressionsMatch(parseString("1"), evaluateString("(f '(1 2 3))"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defunRecursiveFunction() {
|
||||
evaluateString("(defun fact (x) (if (< x 2) 1 (* x (fact (- x 1)))))");
|
||||
assertSExpressionsMatch(parseString("120"), evaluateString("(fact 5)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defunTailRecursiveFunction() {
|
||||
evaluateString("(defun fact-tail (x acc) (if (< x 2) acc (fact-tail (- x 1) (* x acc))))");
|
||||
assertSExpressionsMatch(parseString("120"), evaluateString("(fact-tail 5 1)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defunSimpleClass() {
|
||||
evaluateString("(defun counter-class () (let ((counter 0)) (lambda () (setf counter (+ 1 counter)))))");
|
||||
evaluateString("(setf my-counter (counter-class))");
|
||||
|
||||
assertSExpressionsMatch(parseString("1"), evaluateString("(funcall my-counter)"));
|
||||
assertSExpressionsMatch(parseString("2"), evaluateString("(funcall my-counter)"));
|
||||
assertSExpressionsMatch(parseString("3"), evaluateString("(funcall my-counter)"));
|
||||
assertSExpressionsMatch(parseString("4"), evaluateString("(funcall my-counter)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void redefineFunction_DisplaysWarning() {
|
||||
String input = "(defun myFunction () nil)";
|
||||
|
@ -82,22 +105,22 @@ public class DEFUNTester {
|
|||
}
|
||||
|
||||
@Test(expected = DottedArgumentListException.class)
|
||||
public void testDefunWithDottedLambdaList() {
|
||||
public void defunWithDottedLambdaList() {
|
||||
evaluateString("(funcall 'defun 'x (cons 'a 'b) ())");
|
||||
}
|
||||
|
||||
@Test(expected = BadArgumentTypeException.class)
|
||||
public void testDefunWithNonSymbolName() {
|
||||
public void defunWithNonSymbolName() {
|
||||
evaluateString("(defun 1 () ())");
|
||||
}
|
||||
|
||||
@Test(expected = BadArgumentTypeException.class)
|
||||
public void testDefunWithBadLambdaList() {
|
||||
public void defunWithBadLambdaList() {
|
||||
evaluateString("(defun x a ())");
|
||||
}
|
||||
|
||||
@Test(expected = TooFewArgumentsException.class)
|
||||
public void testDefunWithTooFewArguments() {
|
||||
public void defunWithTooFewArguments() {
|
||||
evaluateString("(defun x)");
|
||||
}
|
||||
|
||||
|
|
|
@ -11,14 +11,14 @@ import sexpression.*;
|
|||
public class LAMBDATester {
|
||||
|
||||
@Test
|
||||
public void testLambda() {
|
||||
public void lambda() {
|
||||
String input = "(lambda (x) x)";
|
||||
|
||||
assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLambdaWithNoBody() {
|
||||
public void lambdaWithNoBody() {
|
||||
String input = "(lambda ())";
|
||||
|
||||
assertSExpressionsMatch(parseString("(LAMBDA ())"), evaluateString(input));
|
||||
|
@ -38,7 +38,7 @@ public class LAMBDATester {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCreateLambdaExpression() {
|
||||
public void createLambdaExpression() {
|
||||
Cons lambdaExpression = new Cons(new Symbol("LAMBDA"),
|
||||
new Cons(Nil.getInstance(), new Cons(Nil.getInstance(), Nil.getInstance())));
|
||||
|
||||
|
@ -47,40 +47,40 @@ public class LAMBDATester {
|
|||
}
|
||||
|
||||
@Test(expected = DottedArgumentListException.class)
|
||||
public void testLambdaWithDottedArgumentList() {
|
||||
public void lambdaWithDottedArgumentList() {
|
||||
String input = "(apply 'lambda (cons '(x) 1))";
|
||||
|
||||
evaluateString(input);
|
||||
}
|
||||
|
||||
@Test(expected = DottedArgumentListException.class)
|
||||
public void testLambdaWithDottedLambdaList() {
|
||||
public void lambdaWithDottedLambdaList() {
|
||||
String input = "(funcall 'lambda (cons 'a 'b) ())";
|
||||
|
||||
evaluateString(input);
|
||||
}
|
||||
|
||||
@Test(expected = DottedArgumentListException.class)
|
||||
public void testCreateFunctionWithDottedArgumentList() {
|
||||
public void createFunctionWithDottedArgumentList() {
|
||||
Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), new Cons(Nil.getInstance(), LispNumber.ONE));
|
||||
|
||||
LAMBDA.createFunction(lambdaExpression);
|
||||
}
|
||||
|
||||
@Test(expected = BadArgumentTypeException.class)
|
||||
public void testCreateFunctionWithNonList() {
|
||||
public void createFunctionWithNonList() {
|
||||
Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), LispNumber.ONE);
|
||||
|
||||
LAMBDA.createFunction(lambdaExpression);
|
||||
}
|
||||
|
||||
@Test(expected = BadArgumentTypeException.class)
|
||||
public void testLambdaWithNonSymbolParameter() {
|
||||
public void lambdaWithNonSymbolParameter() {
|
||||
evaluateString("(lambda (1) ())");
|
||||
}
|
||||
|
||||
@Test(expected = TooFewArgumentsException.class)
|
||||
public void testLambdaWithTooFewArguments() {
|
||||
public void lambdaWithTooFewArguments() {
|
||||
evaluateString("(lambda)");
|
||||
}
|
||||
|
||||
|
@ -108,4 +108,14 @@ public class LAMBDATester {
|
|||
evaluateString("((lambda (x y) x) 1 2 3)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lexicalClosure() {
|
||||
evaluateString("(setf increment-count (let ((counter 0)) (lambda () (setf counter (+ 1 counter)))))");
|
||||
|
||||
assertSExpressionsMatch(parseString("1"), evaluateString("(funcall increment-count)"));
|
||||
assertSExpressionsMatch(parseString("2"), evaluateString("(funcall increment-count)"));
|
||||
assertSExpressionsMatch(parseString("3"), evaluateString("(funcall increment-count)"));
|
||||
assertSExpressionsMatch(parseString("4"), evaluateString("(funcall increment-count)"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue