Added unit tests and refactored setf
This commit is contained in:
parent
6ed24e85fe
commit
919644d9c0
|
@ -20,7 +20,7 @@ public class UserDefinedFunction extends LispFunction {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
this.lambdaExpression = new Cons(new Symbol(name), new Cons(lambdaList, body));
|
this.lambdaExpression = new Cons(new Symbol(name), new Cons(lambdaList, body));
|
||||||
this.environment = SETF.getEnvironment();
|
this.environment = SETF.getSymbolTable();
|
||||||
|
|
||||||
for (this.formalParameters = new ArrayList<>(); lambdaList.consp(); lambdaList = (Cons) lambdaList.getCdr())
|
for (this.formalParameters = new ArrayList<>(); lambdaList.consp(); lambdaList = (Cons) lambdaList.getCdr())
|
||||||
this.formalParameters.add(lambdaList.getCar().toString());
|
this.formalParameters.add(lambdaList.getCar().toString());
|
||||||
|
@ -45,11 +45,11 @@ public class UserDefinedFunction extends LispFunction {
|
||||||
|
|
||||||
// store the environment of the S-expression that called this function
|
// store the environment of the S-expression that called this function
|
||||||
// (the current environment)
|
// (the current environment)
|
||||||
SymbolTable currentEnvironment = SETF.getEnvironment();
|
SymbolTable currentEnvironment = SETF.getSymbolTable();
|
||||||
|
|
||||||
// replace the current environment with the environment of this
|
// replace the current environment with the environment of this
|
||||||
// function
|
// function
|
||||||
SETF.setEnvironment(environment);
|
SETF.setSymbolTable(environment);
|
||||||
|
|
||||||
Cons currentSExpression = body;
|
Cons currentSExpression = body;
|
||||||
SExpression retval = null;
|
SExpression retval = null;
|
||||||
|
@ -62,7 +62,7 @@ public class UserDefinedFunction extends LispFunction {
|
||||||
|
|
||||||
// replace the environment of the S-expression that called this
|
// replace the environment of the S-expression that called this
|
||||||
// function
|
// function
|
||||||
SETF.setEnvironment(currentEnvironment);
|
SETF.setSymbolTable(currentEnvironment);
|
||||||
|
|
||||||
// remove the bindings of the arguments to the formal parameter names
|
// remove the bindings of the arguments to the formal parameter names
|
||||||
// in the environment of this function
|
// in the environment of this function
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class EVAL extends LispFunction {
|
||||||
return new Symbol(symbolName);
|
return new Symbol(symbolName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return SETF.lookup(symbolName);
|
return SETF.lookupSymbolValue(symbolName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isDotted(Cons list) {
|
public static boolean isDotted(Cons list) {
|
||||||
|
|
|
@ -18,13 +18,13 @@ public class LET extends LispFunction {
|
||||||
|
|
||||||
// create a new symbol table on top of the current environment to add
|
// create a new symbol table on top of the current environment to add
|
||||||
// all the local variables to
|
// all the local variables to
|
||||||
SymbolTable environment = new SymbolTable(SETF.getEnvironment());
|
SymbolTable environment = new SymbolTable(SETF.getSymbolTable());
|
||||||
|
|
||||||
SExpression car = argList.getCar();
|
SExpression car = argList.getCar();
|
||||||
Cons cdr = (Cons) argList.getCdr();
|
Cons cdr = (Cons) argList.getCdr();
|
||||||
|
|
||||||
addVariablesToTable(environment, car);
|
addVariablesToTable(environment, car);
|
||||||
SETF.setEnvironment(environment);
|
SETF.setSymbolTable(environment);
|
||||||
|
|
||||||
SExpression retval = Nil.getInstance();
|
SExpression retval = Nil.getInstance();
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ public class LET extends LispFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore the environment to its original value
|
// restore the environment to its original value
|
||||||
SETF.setEnvironment(environment.getParent());
|
SETF.setSymbolTable(environment.getParent());
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,108 +1,68 @@
|
||||||
package function.builtin.special;
|
package function.builtin.special;
|
||||||
|
|
||||||
import function.*;
|
import function.*;
|
||||||
import function.builtin.*;
|
import function.builtin.EVAL;
|
||||||
import function.builtin.cons.LENGTH;
|
|
||||||
import sexpression.*;
|
import sexpression.*;
|
||||||
import table.SymbolTable;
|
import table.SymbolTable;
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>SETF</code> represents the SETF form in Lisp.
|
|
||||||
*/
|
|
||||||
public class SETF extends LispFunction {
|
public class SETF extends LispFunction {
|
||||||
|
|
||||||
private static SymbolTable environment = new SymbolTable();
|
private static SymbolTable symbolTable = new SymbolTable();
|
||||||
|
|
||||||
/**
|
public static SExpression lookupSymbolValue(String symbolName) {
|
||||||
* Look up the value of a symbol using its name.
|
for (SymbolTable t = symbolTable; t != null; t = t.getParent())
|
||||||
*
|
if (t.contains(symbolName))
|
||||||
* @param symbolName
|
return t.get(symbolName);
|
||||||
* the name of the symbol to look up
|
|
||||||
* @return
|
|
||||||
* the value of <code>symbolName</code> if it has one; null otherwise
|
|
||||||
*/
|
|
||||||
public static SExpression lookup(String symbolName) {
|
|
||||||
SymbolTable current = environment;
|
|
||||||
|
|
||||||
while (current != null) {
|
|
||||||
if (current.contains(symbolName)) {
|
|
||||||
return current.get(symbolName);
|
|
||||||
}
|
|
||||||
|
|
||||||
current = current.getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static void setSymbolTable(SymbolTable newSymbolTable) {
|
||||||
* Set the current environment to the specified value.
|
symbolTable = newSymbolTable;
|
||||||
*
|
|
||||||
* @param newEnvironment
|
|
||||||
* the value to set the environment to
|
|
||||||
*/
|
|
||||||
public static void setEnvironment(SymbolTable newEnvironment) {
|
|
||||||
environment = newEnvironment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static SymbolTable getSymbolTable() {
|
||||||
* Retrieve the current environment.
|
return symbolTable;
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the current environment
|
|
||||||
*/
|
|
||||||
public static SymbolTable getEnvironment() {
|
|
||||||
return environment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The number of arguments that SETF takes.
|
private ArgumentValidator argumentValidator;
|
||||||
private static final int NUM_ARGS = 2;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
public SETF() {
|
||||||
// retrieve the number of arguments passed to SETF
|
this.argumentValidator = new ArgumentValidator("SETF");
|
||||||
int argListLength = LENGTH.getLength(argList);
|
this.argumentValidator.setExactNumberOfArguments(2);
|
||||||
|
this.argumentValidator.setFirstArgumentExpectedType(Symbol.class);
|
||||||
|
}
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
public SExpression call(Cons argumentList) {
|
||||||
if (argListLength != NUM_ARGS) {
|
argumentValidator.validate(argumentList);
|
||||||
Cons originalSExpr = new Cons(new Symbol("SETF"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to SETF: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
Cons cdr = (Cons) argumentList.getCdr();
|
||||||
}
|
SExpression symbol = argumentList.getCar();
|
||||||
|
|
||||||
SExpression symbol = argList.getCar();
|
|
||||||
|
|
||||||
// make sure the first argument is a symbol
|
|
||||||
if (! symbol.symbolp()) {
|
|
||||||
throw new RuntimeException("SETF: " + symbol + " is not a symbol");
|
|
||||||
}
|
|
||||||
|
|
||||||
Cons cdr = (Cons) argList.getCdr();
|
|
||||||
SExpression value = EVAL.eval(cdr.getCar());
|
SExpression value = EVAL.eval(cdr.getCar());
|
||||||
|
|
||||||
SymbolTable current = environment;
|
SymbolTable table = findTableForSymbol(symbol);
|
||||||
|
table.put(symbol.toString(), value);
|
||||||
// set 'current' to the symbol table that contains 'symbol' or the
|
|
||||||
// global symbol table if 'symbol' is not in the environment
|
|
||||||
while ((! current.contains(symbol.toString())) &&
|
|
||||||
(current.getParent() != null)) {
|
|
||||||
current = current.getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
current.put(symbol.toString(), value);
|
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private SymbolTable findTableForSymbol(SExpression symbol) {
|
||||||
* Determine if the arguments passed to this Lisp function should be
|
SymbolTable table = symbolTable;
|
||||||
* evaluated.
|
|
||||||
*
|
while (!isSymbolInTable(symbol, table) && !isGlobalTable(table))
|
||||||
* @return
|
table = table.getParent();
|
||||||
* <code>false</code>
|
|
||||||
*/
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSymbolInTable(SExpression symbol, SymbolTable table) {
|
||||||
|
return table.contains(symbol.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isGlobalTable(SymbolTable table) {
|
||||||
|
return table.getParent() == null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean evaluateArguments() {
|
public boolean evaluateArguments() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
package function.builtin.special;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static testutil.TestUtilities.*;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import function.ArgumentValidator.*;
|
||||||
|
import function.builtin.EVAL.UndefinedSymbolException;
|
||||||
|
import sexpression.LispNumber;
|
||||||
|
import table.SymbolTable;
|
||||||
|
|
||||||
|
public class SETFTester {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
SETF.setSymbolTable(new SymbolTable());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetf() {
|
||||||
|
evaluateString("(setf a 23)");
|
||||||
|
assertSExpressionsMatch(evaluateString("23"), evaluateString("a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lookupDefinedSymbol() {
|
||||||
|
evaluateString("(setf a 23)");
|
||||||
|
assertSExpressionsMatch(evaluateString("23"), SETF.lookupSymbolValue("A"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lookupUndefinedSymbol() {
|
||||||
|
assertNull(SETF.lookupSymbolValue("A"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setfGlobalVariable() {
|
||||||
|
evaluateString("(setf a 23)");
|
||||||
|
SymbolTable global = SETF.getSymbolTable();
|
||||||
|
SETF.setSymbolTable(new SymbolTable(global));
|
||||||
|
|
||||||
|
evaluateString("(setf a 94)");
|
||||||
|
SETF.setSymbolTable(global);
|
||||||
|
assertSExpressionsMatch(evaluateString("94"), evaluateString("a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = UndefinedSymbolException.class)
|
||||||
|
public void setfLocalVariableDefined_DoesNotSetGlobal() {
|
||||||
|
SymbolTable global = SETF.getSymbolTable();
|
||||||
|
SymbolTable local = new SymbolTable(global);
|
||||||
|
local.put("A", new LispNumber("99"));
|
||||||
|
SETF.setSymbolTable(local);
|
||||||
|
|
||||||
|
evaluateString("(setf a 94)");
|
||||||
|
SETF.setSymbolTable(global);
|
||||||
|
evaluateString("a");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setfLocalVariableUndefined_SetsGlobal() {
|
||||||
|
SymbolTable global = SETF.getSymbolTable();
|
||||||
|
SymbolTable local = new SymbolTable(global);
|
||||||
|
SETF.setSymbolTable(local);
|
||||||
|
|
||||||
|
evaluateString("(setf a 94)");
|
||||||
|
SETF.setSymbolTable(global);
|
||||||
|
assertSExpressionsMatch(evaluateString("94"), evaluateString("a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = BadArgumentTypeException.class)
|
||||||
|
public void testSetfWithNonSymbol() {
|
||||||
|
evaluateString("(setf 1 2)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = TooFewArgumentsException.class)
|
||||||
|
public void testSetfWithTooFewArguments() {
|
||||||
|
evaluateString("(setf x)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = TooManyArgumentsException.class)
|
||||||
|
public void testSetfWithTooManyArguments() {
|
||||||
|
evaluateString("(setf a b c)");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue