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())
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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