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