Add LET* special function
This commit is contained in:
		
							parent
							
								
									d1060a8aad
								
							
						
					
					
						commit
						736e230de9
					
				@ -13,7 +13,7 @@ public class LET extends LispSpecialFunction {
 | 
				
			|||||||
    private ArgumentValidator argumentValidator;
 | 
					    private ArgumentValidator argumentValidator;
 | 
				
			||||||
    private ArgumentValidator variableDefinitionListValidator;
 | 
					    private ArgumentValidator variableDefinitionListValidator;
 | 
				
			||||||
    private ArgumentValidator pairValidator;
 | 
					    private ArgumentValidator pairValidator;
 | 
				
			||||||
    private ExecutionContext executionContext;
 | 
					    protected ExecutionContext executionContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public LET(String name) {
 | 
					    public LET(String name) {
 | 
				
			||||||
        this.argumentValidator = new ArgumentValidator(name);
 | 
					        this.argumentValidator = new ArgumentValidator(name);
 | 
				
			||||||
@ -47,7 +47,7 @@ public class LET extends LispSpecialFunction {
 | 
				
			|||||||
        return lastEvaluation;
 | 
					        return lastEvaluation;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private SymbolTable createLocalScope(Cons variableDefinitions) {
 | 
					    protected SymbolTable createLocalScope(Cons variableDefinitions) {
 | 
				
			||||||
        SymbolTable localScope = new SymbolTable(executionContext.getScope());
 | 
					        SymbolTable localScope = new SymbolTable(executionContext.getScope());
 | 
				
			||||||
        addVariablesToScope(localScope, variableDefinitions);
 | 
					        addVariablesToScope(localScope, variableDefinitions);
 | 
				
			||||||
        executionContext.setScope(localScope);
 | 
					        executionContext.setScope(localScope);
 | 
				
			||||||
@ -55,7 +55,7 @@ public class LET extends LispSpecialFunction {
 | 
				
			|||||||
        return localScope;
 | 
					        return localScope;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void addVariablesToScope(SymbolTable scope, Cons variableDefinitions) {
 | 
					    protected void addVariablesToScope(SymbolTable scope, Cons variableDefinitions) {
 | 
				
			||||||
        variableDefinitionListValidator.validate(variableDefinitions);
 | 
					        variableDefinitionListValidator.validate(variableDefinitions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (; variableDefinitions.isCons(); variableDefinitions = (Cons) variableDefinitions.getRest())
 | 
					        for (; variableDefinitions.isCons(); variableDefinitions = (Cons) variableDefinitions.getRest())
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										22
									
								
								src/function/builtin/special/LET_STAR.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/function/builtin/special/LET_STAR.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					package function.builtin.special;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import function.FunctionNames;
 | 
				
			||||||
 | 
					import sexpression.Cons;
 | 
				
			||||||
 | 
					import table.SymbolTable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@FunctionNames({ "LET*" })
 | 
				
			||||||
 | 
					public class LET_STAR extends LET {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public LET_STAR(String name) {
 | 
				
			||||||
 | 
					        super(name);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected SymbolTable createLocalScope(Cons variableDefinitions) {
 | 
				
			||||||
 | 
					        SymbolTable localScope = new SymbolTable(executionContext.getScope());
 | 
				
			||||||
 | 
					        executionContext.setScope(localScope);
 | 
				
			||||||
 | 
					        addVariablesToScope(localScope, variableDefinitions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return localScope;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -38,6 +38,7 @@ public class FunctionTable {
 | 
				
			|||||||
        allBuiltIns.add(LENGTH.class);
 | 
					        allBuiltIns.add(LENGTH.class);
 | 
				
			||||||
        allBuiltIns.add(NUMERIC_LESS.class);
 | 
					        allBuiltIns.add(NUMERIC_LESS.class);
 | 
				
			||||||
        allBuiltIns.add(LET.class);
 | 
					        allBuiltIns.add(LET.class);
 | 
				
			||||||
 | 
					        allBuiltIns.add(LET_STAR.class);
 | 
				
			||||||
        allBuiltIns.add(LIST.class);
 | 
					        allBuiltIns.add(LIST.class);
 | 
				
			||||||
        allBuiltIns.add(LISTP.class);
 | 
					        allBuiltIns.add(LISTP.class);
 | 
				
			||||||
        allBuiltIns.add(LOAD.class);
 | 
					        allBuiltIns.add(LOAD.class);
 | 
				
			||||||
 | 
				
			|||||||
@ -23,7 +23,6 @@ public class EQTester {
 | 
				
			|||||||
        assertT(evaluateString(input));
 | 
					        assertT(evaluateString(input));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    public void eqWithUnequalAtoms() {
 | 
					    public void eqWithUnequalAtoms() {
 | 
				
			||||||
        String input = "(eq 1 2)";
 | 
					        String input = "(eq 1 2)";
 | 
				
			||||||
 | 
				
			|||||||
@ -162,4 +162,11 @@ public class LETTester {
 | 
				
			|||||||
        evaluateString("(apply 'let (cons (cons (cons 'a 'b) nil) nil))");
 | 
					        evaluateString("(apply 'let (cons (cons (cons 'a 'b) nil) nil))");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = UndefinedSymbolException.class)
 | 
				
			||||||
 | 
					    public void letEvaluatesSymbolsInParallel() {
 | 
				
			||||||
 | 
					        String input = "(let ((x 1) (y (+ x 1))) (+ x y))";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("2"), evaluateString(input));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										172
									
								
								test/function/builtin/special/LET_STARTester.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								test/function/builtin/special/LET_STARTester.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,172 @@
 | 
				
			|||||||
 | 
					package function.builtin.special;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static sexpression.Nil.NIL;
 | 
				
			||||||
 | 
					import static testutil.TestUtilities.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import function.ArgumentValidator.*;
 | 
				
			||||||
 | 
					import function.builtin.EVAL.UndefinedSymbolException;
 | 
				
			||||||
 | 
					import sexpression.*;
 | 
				
			||||||
 | 
					import table.ExecutionContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class LET_STARTester {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ExecutionContext executionContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public LET_STARTester() {
 | 
				
			||||||
 | 
					        this.executionContext = ExecutionContext.getInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Before
 | 
				
			||||||
 | 
					    public void setUp() {
 | 
				
			||||||
 | 
					        executionContext.clearContext();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @After
 | 
				
			||||||
 | 
					    public void tearDown() {
 | 
				
			||||||
 | 
					        executionContext.clearContext();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void simpleLet() {
 | 
				
			||||||
 | 
					        String input = "(let* ((x 1)) x)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("1"), evaluateString(input));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void emptyLet_ReturnsNil() {
 | 
				
			||||||
 | 
					        String input = "(let* ())";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(NIL, evaluateString(input));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void letStarWithSymbolsOnly_SetsValuesToNil() {
 | 
				
			||||||
 | 
					        String input = "(let* ((x) (y)) (list x y))";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new Cons(NIL, new Cons(NIL, NIL)), evaluateString(input));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void letStarWithSetf_DoesNotAlterGlobalVariable() {
 | 
				
			||||||
 | 
					        String before = "(setf x 22)";
 | 
				
			||||||
 | 
					        String input = "(let* ((x 1)) x)";
 | 
				
			||||||
 | 
					        String after = "x";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("22"), evaluateString(before));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("1"), evaluateString(input));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("22"), evaluateString(after));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void letStarWithNestedSetf_DoesNotAlterGlobalVariable() {
 | 
				
			||||||
 | 
					        String before = "(setf x 22)";
 | 
				
			||||||
 | 
					        String input = "(let* ((x 33)) (setf x 44) x)";
 | 
				
			||||||
 | 
					        String after = "x";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("22"), evaluateString(before));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("44"), evaluateString(input));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("22"), evaluateString(after));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void nestedLet() {
 | 
				
			||||||
 | 
					        String input = "(let* ((x 1)) (let* ((y (+ 1 x))) y))";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("2"), evaluateString(input));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void nestedLetWithGlobals() {
 | 
				
			||||||
 | 
					        String before = "(setf x 92)";
 | 
				
			||||||
 | 
					        String input = "(let* ((x 1)) (let* ((y (+ 1 x))) y))";
 | 
				
			||||||
 | 
					        String after = "x";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("92"), evaluateString(before));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("2"), evaluateString(input));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("92"), evaluateString(after));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void alterGlobalVariableFromLet() {
 | 
				
			||||||
 | 
					        String before = "(setf x 1)";
 | 
				
			||||||
 | 
					        String input = "(let* ((y 1)) (setf x 2))";
 | 
				
			||||||
 | 
					        String after = "x";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("1"), evaluateString(before));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("2"), evaluateString(input));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("2"), evaluateString(after));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void accessGlobalVariableFromLet() {
 | 
				
			||||||
 | 
					        String before = "(setf x 1)";
 | 
				
			||||||
 | 
					        String input = "(let* () x)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("1"), evaluateString(before));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("1"), evaluateString(input));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = UndefinedSymbolException.class)
 | 
				
			||||||
 | 
					    public void letStarDoesNotSetGlobalVariable() {
 | 
				
			||||||
 | 
					        String input = "(let* ((x 1)) nil)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        evaluateString(input);
 | 
				
			||||||
 | 
					        evaluateString("x");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BadArgumentTypeException.class)
 | 
				
			||||||
 | 
					    public void letStarWithNonList() {
 | 
				
			||||||
 | 
					        evaluateString("(let* a)");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BadArgumentTypeException.class)
 | 
				
			||||||
 | 
					    public void letStarWithNoPairs() {
 | 
				
			||||||
 | 
					        evaluateString("(let* (a))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = TooFewArgumentsException.class)
 | 
				
			||||||
 | 
					    public void letStarWithTooFewItemsInPair() {
 | 
				
			||||||
 | 
					        evaluateString("(let* (()))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = TooManyArgumentsException.class)
 | 
				
			||||||
 | 
					    public void letStarWithTooManyItemsInPair() {
 | 
				
			||||||
 | 
					        evaluateString("(let* ((a b c)))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BadArgumentTypeException.class)
 | 
				
			||||||
 | 
					    public void letStarWithNonSymbolInPair() {
 | 
				
			||||||
 | 
					        evaluateString("(let* ((1 b)))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = TooFewArgumentsException.class)
 | 
				
			||||||
 | 
					    public void letStarWithTooFewArguments() {
 | 
				
			||||||
 | 
					        evaluateString("(let*)");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = DottedArgumentListException.class)
 | 
				
			||||||
 | 
					    public void letStarWithDottedArgumentList() {
 | 
				
			||||||
 | 
					        evaluateString("(apply 'let* (cons 'a 'b))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = DottedArgumentListException.class)
 | 
				
			||||||
 | 
					    public void letStarWithDottedPairList() {
 | 
				
			||||||
 | 
					        evaluateString("(apply 'let* (cons (cons 'a 'b) nil))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = DottedArgumentListException.class)
 | 
				
			||||||
 | 
					    public void letStarWithDottedPair() {
 | 
				
			||||||
 | 
					        evaluateString("(apply 'let* (cons (cons (cons 'a 'b) nil) nil))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void letStarEvaluatesSymbolsInSequence() {
 | 
				
			||||||
 | 
					        String input = "(let* ((x 1) (y (+ x 1))) (+ x y))";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("3"), evaluateString(input));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -27,7 +27,6 @@ public class PROGNTester {
 | 
				
			|||||||
        assertSExpressionsMatch(parseString("5"), evaluateString("(begin 1 2 3 4 5)"));
 | 
					        assertSExpressionsMatch(parseString("5"), evaluateString("(begin 1 2 3 4 5)"));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    public void prognEvaluatesArgument() {
 | 
					    public void prognEvaluatesArgument() {
 | 
				
			||||||
        assertSExpressionsMatch(parseString("1"), evaluateString("(progn (car '(1 2 3)))"));
 | 
					        assertSExpressionsMatch(parseString("1"), evaluateString("(progn (car '(1 2 3)))"));
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user