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 | |  | ||||||
							
								
								
									
										38
									
								
								fitnesse/FitNesseRoot/LispInterpreter/LexicalClosures.wiki
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								fitnesse/FitNesseRoot/LispInterpreter/LexicalClosures.wiki
									
									
									
									
									
										Normal file
									
								
							| @ -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
	
	Block a user