Refactored the symbol table code
This commit is contained in:
parent
d3fc8f9812
commit
db2817f7be
|
@ -3,15 +3,15 @@ package function;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import function.builtin.EVAL;
|
import function.builtin.EVAL;
|
||||||
import function.builtin.special.SETF;
|
|
||||||
import sexpression.*;
|
import sexpression.*;
|
||||||
import table.SymbolTable;
|
import table.*;
|
||||||
|
|
||||||
public class UserDefinedFunction extends LispFunction {
|
public class UserDefinedFunction extends LispFunction {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private Cons body;
|
private Cons body;
|
||||||
private Cons lambdaExpression;
|
private Cons lambdaExpression;
|
||||||
|
private ExecutionContext executionContext;
|
||||||
private SymbolTable scope;
|
private SymbolTable scope;
|
||||||
private ArrayList<String> formalParameters;
|
private ArrayList<String> formalParameters;
|
||||||
private ArgumentValidator argumentValidator;
|
private ArgumentValidator argumentValidator;
|
||||||
|
@ -20,7 +20,8 @@ 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.scope = SETF.getSymbolTable();
|
this.executionContext = ExecutionContext.getInstance();
|
||||||
|
this.scope = executionContext.getScope();
|
||||||
|
|
||||||
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());
|
||||||
|
@ -37,11 +38,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 callingScope = SETF.getSymbolTable();
|
SymbolTable callingScope = executionContext.getScope();
|
||||||
|
|
||||||
// replace the current environment with the environment of this
|
// replace the current environment with the environment of this
|
||||||
// function
|
// function
|
||||||
SETF.setSymbolTable(scope);
|
executionContext.setScope(scope);
|
||||||
|
|
||||||
Cons currentSExpression = body;
|
Cons currentSExpression = body;
|
||||||
SExpression lastValue = null;
|
SExpression lastValue = null;
|
||||||
|
@ -54,7 +55,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.setSymbolTable(callingScope);
|
executionContext.setScope(callingScope);
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
@ -5,9 +5,9 @@ import java.text.MessageFormat;
|
||||||
import error.LispException;
|
import error.LispException;
|
||||||
import function.*;
|
import function.*;
|
||||||
import function.builtin.cons.LIST;
|
import function.builtin.cons.LIST;
|
||||||
import function.builtin.special.*;
|
import function.builtin.special.LAMBDA;
|
||||||
import sexpression.*;
|
import sexpression.*;
|
||||||
import table.FunctionTable;
|
import table.*;
|
||||||
|
|
||||||
public class EVAL extends LispFunction {
|
public class EVAL extends LispFunction {
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ public class EVAL extends LispFunction {
|
||||||
else if (symbolName.startsWith(":"))
|
else if (symbolName.startsWith(":"))
|
||||||
return new Symbol(symbolName);
|
return new Symbol(symbolName);
|
||||||
|
|
||||||
return SETF.lookupSymbolValue(symbolName);
|
return ExecutionContext.getInstance().lookupSymbolValue(symbolName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SExpression eval(SExpression sExpression) {
|
public static SExpression eval(SExpression sExpression) {
|
||||||
|
|
|
@ -3,10 +3,16 @@ package function.builtin.special;
|
||||||
import function.*;
|
import function.*;
|
||||||
import function.builtin.EVAL;
|
import function.builtin.EVAL;
|
||||||
import sexpression.*;
|
import sexpression.*;
|
||||||
import table.SymbolTable;
|
import table.*;
|
||||||
|
|
||||||
public class LET extends LispFunction {
|
public class LET extends LispFunction {
|
||||||
|
|
||||||
|
private ExecutionContext executionContext;
|
||||||
|
|
||||||
|
public LET() {
|
||||||
|
this.executionContext = ExecutionContext.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
public SExpression call(Cons argList) {
|
||||||
// make sure we have received at least one argument
|
// make sure we have received at least one argument
|
||||||
if (argList.nullp()) {
|
if (argList.nullp()) {
|
||||||
|
@ -15,13 +21,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.getSymbolTable());
|
SymbolTable environment = new SymbolTable(executionContext.getScope());
|
||||||
|
|
||||||
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.setSymbolTable(environment);
|
executionContext.setScope(environment);
|
||||||
|
|
||||||
SExpression retval = Nil.getInstance();
|
SExpression retval = Nil.getInstance();
|
||||||
|
|
||||||
|
@ -32,7 +38,7 @@ public class LET extends LispFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore the environment to its original value
|
// restore the environment to its original value
|
||||||
SETF.setSymbolTable(environment.getParent());
|
executionContext.setScope(environment.getParent());
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,34 +3,18 @@ package function.builtin.special;
|
||||||
import function.*;
|
import function.*;
|
||||||
import function.builtin.EVAL;
|
import function.builtin.EVAL;
|
||||||
import sexpression.*;
|
import sexpression.*;
|
||||||
import table.SymbolTable;
|
import table.*;
|
||||||
|
|
||||||
public class SETF extends LispFunction {
|
public class SETF extends LispFunction {
|
||||||
|
|
||||||
private static SymbolTable symbolTable = new SymbolTable();
|
|
||||||
|
|
||||||
public static SExpression lookupSymbolValue(String symbolName) {
|
|
||||||
for (SymbolTable t = symbolTable; t != null; t = t.getParent())
|
|
||||||
if (t.contains(symbolName))
|
|
||||||
return t.get(symbolName);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setSymbolTable(SymbolTable newSymbolTable) {
|
|
||||||
symbolTable = newSymbolTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SymbolTable getSymbolTable() {
|
|
||||||
return symbolTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArgumentValidator argumentValidator;
|
private ArgumentValidator argumentValidator;
|
||||||
|
private ExecutionContext executionContext;
|
||||||
|
|
||||||
public SETF() {
|
public SETF() {
|
||||||
this.argumentValidator = new ArgumentValidator("SETF");
|
this.argumentValidator = new ArgumentValidator("SETF");
|
||||||
this.argumentValidator.setExactNumberOfArguments(2);
|
this.argumentValidator.setExactNumberOfArguments(2);
|
||||||
this.argumentValidator.setFirstArgumentExpectedType(Symbol.class);
|
this.argumentValidator.setFirstArgumentExpectedType(Symbol.class);
|
||||||
|
this.executionContext = ExecutionContext.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SExpression call(Cons argumentList) {
|
public SExpression call(Cons argumentList) {
|
||||||
|
@ -40,14 +24,14 @@ public class SETF extends LispFunction {
|
||||||
SExpression symbol = argumentList.getCar();
|
SExpression symbol = argumentList.getCar();
|
||||||
SExpression value = EVAL.eval(cdr.getCar());
|
SExpression value = EVAL.eval(cdr.getCar());
|
||||||
|
|
||||||
SymbolTable table = findTableForSymbol(symbol);
|
SymbolTable table = findScopeOfSymbol(symbol);
|
||||||
table.put(symbol.toString(), value);
|
table.put(symbol.toString(), value);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SymbolTable findTableForSymbol(SExpression symbol) {
|
private SymbolTable findScopeOfSymbol(SExpression symbol) {
|
||||||
SymbolTable table = symbolTable;
|
SymbolTable table = executionContext.getScope();
|
||||||
|
|
||||||
while (!isSymbolInTable(symbol, table) && !isGlobalTable(table))
|
while (!isSymbolInTable(symbol, table) && !isGlobalTable(table))
|
||||||
table = table.getParent();
|
table = table.getParent();
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package table;
|
||||||
|
|
||||||
|
import sexpression.SExpression;
|
||||||
|
|
||||||
|
public class ExecutionContext {
|
||||||
|
|
||||||
|
private static ExecutionContext uniqueInstance = new ExecutionContext();
|
||||||
|
|
||||||
|
public static ExecutionContext getInstance() {
|
||||||
|
return uniqueInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SymbolTable scope;
|
||||||
|
|
||||||
|
private ExecutionContext() {
|
||||||
|
this.scope = new SymbolTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SymbolTable getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScope(SymbolTable scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearContext() {
|
||||||
|
this.scope = new SymbolTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SExpression lookupSymbolValue(String symbolName) {
|
||||||
|
for (SymbolTable t = scope; t != null; t = t.getParent())
|
||||||
|
if (t.contains(symbolName))
|
||||||
|
return t.get(symbolName);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,13 +8,19 @@ import org.junit.*;
|
||||||
import function.ArgumentValidator.*;
|
import function.ArgumentValidator.*;
|
||||||
import function.builtin.EVAL.UndefinedSymbolException;
|
import function.builtin.EVAL.UndefinedSymbolException;
|
||||||
import sexpression.LispNumber;
|
import sexpression.LispNumber;
|
||||||
import table.SymbolTable;
|
import table.*;
|
||||||
|
|
||||||
public class SETFTester {
|
public class SETFTester {
|
||||||
|
|
||||||
|
private ExecutionContext executionContext;
|
||||||
|
|
||||||
|
public SETFTester() {
|
||||||
|
this.executionContext = ExecutionContext.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
SETF.setSymbolTable(new SymbolTable());
|
executionContext.clearContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -26,45 +32,45 @@ public class SETFTester {
|
||||||
@Test
|
@Test
|
||||||
public void lookupDefinedSymbol() {
|
public void lookupDefinedSymbol() {
|
||||||
evaluateString("(setf a 23)");
|
evaluateString("(setf a 23)");
|
||||||
assertSExpressionsMatch(new LispNumber("23"), SETF.lookupSymbolValue("A"));
|
assertSExpressionsMatch(new LispNumber("23"), executionContext.lookupSymbolValue("A"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void lookupUndefinedSymbol() {
|
public void lookupUndefinedSymbol() {
|
||||||
assertNull(SETF.lookupSymbolValue("A"));
|
assertNull(executionContext.lookupSymbolValue("A"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setfGlobalVariable() {
|
public void setfGlobalVariable() {
|
||||||
evaluateString("(setf a 23)");
|
evaluateString("(setf a 23)");
|
||||||
SymbolTable global = SETF.getSymbolTable();
|
SymbolTable global = executionContext.getScope();
|
||||||
SETF.setSymbolTable(new SymbolTable(global));
|
executionContext.setScope(new SymbolTable(global));
|
||||||
|
|
||||||
evaluateString("(setf a 94)");
|
evaluateString("(setf a 94)");
|
||||||
SETF.setSymbolTable(global);
|
executionContext.setScope(global);
|
||||||
assertSExpressionsMatch(new LispNumber("94"), evaluateString("a"));
|
assertSExpressionsMatch(new LispNumber("94"), evaluateString("a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = UndefinedSymbolException.class)
|
@Test(expected = UndefinedSymbolException.class)
|
||||||
public void setfLocalVariableDefined_DoesNotSetGlobal() {
|
public void setfLocalVariableDefined_DoesNotSetGlobal() {
|
||||||
SymbolTable global = SETF.getSymbolTable();
|
SymbolTable global = executionContext.getScope();
|
||||||
SymbolTable local = new SymbolTable(global);
|
SymbolTable local = new SymbolTable(global);
|
||||||
local.put("A", new LispNumber("99"));
|
local.put("A", new LispNumber("99"));
|
||||||
SETF.setSymbolTable(local);
|
executionContext.setScope(local);
|
||||||
|
|
||||||
evaluateString("(setf a 94)");
|
evaluateString("(setf a 94)");
|
||||||
SETF.setSymbolTable(global);
|
executionContext.setScope(global);
|
||||||
evaluateString("a");
|
evaluateString("a");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setfLocalVariableUndefined_SetsGlobal() {
|
public void setfLocalVariableUndefined_SetsGlobal() {
|
||||||
SymbolTable global = SETF.getSymbolTable();
|
SymbolTable global = executionContext.getScope();
|
||||||
SymbolTable local = new SymbolTable(global);
|
SymbolTable local = new SymbolTable(global);
|
||||||
SETF.setSymbolTable(local);
|
executionContext.setScope(local);
|
||||||
|
|
||||||
evaluateString("(setf a 94)");
|
evaluateString("(setf a 94)");
|
||||||
SETF.setSymbolTable(global);
|
executionContext.setScope(global);
|
||||||
assertSExpressionsMatch(new LispNumber("94"), evaluateString("a"));
|
assertSExpressionsMatch(new LispNumber("94"), evaluateString("a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue