diff --git a/src/function/UserDefinedFunction.java b/src/function/UserDefinedFunction.java index a0ce867..9623c45 100644 --- a/src/function/UserDefinedFunction.java +++ b/src/function/UserDefinedFunction.java @@ -3,15 +3,15 @@ package function; import java.util.ArrayList; import function.builtin.EVAL; -import function.builtin.special.SETF; import sexpression.*; -import table.SymbolTable; +import table.*; public class UserDefinedFunction extends LispFunction { private String name; private Cons body; private Cons lambdaExpression; + private ExecutionContext executionContext; private SymbolTable scope; private ArrayList formalParameters; private ArgumentValidator argumentValidator; @@ -20,7 +20,8 @@ public class UserDefinedFunction extends LispFunction { this.name = name; this.body = 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()) 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 // (the current environment) - SymbolTable callingScope = SETF.getSymbolTable(); + SymbolTable callingScope = executionContext.getScope(); // replace the current environment with the environment of this // function - SETF.setSymbolTable(scope); + executionContext.setScope(scope); Cons currentSExpression = body; SExpression lastValue = null; @@ -54,7 +55,7 @@ public class UserDefinedFunction extends LispFunction { // replace the environment of the S-expression that called this // function - SETF.setSymbolTable(callingScope); + executionContext.setScope(callingScope); // remove the bindings of the arguments to the formal parameter names // in the environment of this function diff --git a/src/function/builtin/EVAL.java b/src/function/builtin/EVAL.java index 3b63d9e..85df142 100644 --- a/src/function/builtin/EVAL.java +++ b/src/function/builtin/EVAL.java @@ -5,9 +5,9 @@ import java.text.MessageFormat; import error.LispException; import function.*; import function.builtin.cons.LIST; -import function.builtin.special.*; +import function.builtin.special.LAMBDA; import sexpression.*; -import table.FunctionTable; +import table.*; public class EVAL extends LispFunction { @@ -37,7 +37,7 @@ public class EVAL extends LispFunction { else if (symbolName.startsWith(":")) return new Symbol(symbolName); - return SETF.lookupSymbolValue(symbolName); + return ExecutionContext.getInstance().lookupSymbolValue(symbolName); } public static SExpression eval(SExpression sExpression) { diff --git a/src/function/builtin/special/LET.java b/src/function/builtin/special/LET.java index 6a8b04b..26c6195 100644 --- a/src/function/builtin/special/LET.java +++ b/src/function/builtin/special/LET.java @@ -3,9 +3,15 @@ package function.builtin.special; import function.*; import function.builtin.EVAL; import sexpression.*; -import table.SymbolTable; +import table.*; public class LET extends LispFunction { + + private ExecutionContext executionContext; + + public LET() { + this.executionContext = ExecutionContext.getInstance(); + } public SExpression call(Cons argList) { // make sure we have received at least one argument @@ -15,13 +21,13 @@ public class LET extends LispFunction { // create a new symbol table on top of the current environment to add // all the local variables to - SymbolTable environment = new SymbolTable(SETF.getSymbolTable()); + SymbolTable environment = new SymbolTable(executionContext.getScope()); SExpression car = argList.getCar(); Cons cdr = (Cons) argList.getCdr(); addVariablesToTable(environment, car); - SETF.setSymbolTable(environment); + executionContext.setScope(environment); SExpression retval = Nil.getInstance(); @@ -32,7 +38,7 @@ public class LET extends LispFunction { } // restore the environment to its original value - SETF.setSymbolTable(environment.getParent()); + executionContext.setScope(environment.getParent()); return retval; } diff --git a/src/function/builtin/special/SETF.java b/src/function/builtin/special/SETF.java index 202149d..b9d5d25 100644 --- a/src/function/builtin/special/SETF.java +++ b/src/function/builtin/special/SETF.java @@ -3,34 +3,18 @@ package function.builtin.special; import function.*; import function.builtin.EVAL; import sexpression.*; -import table.SymbolTable; +import table.*; 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 ExecutionContext executionContext; public SETF() { this.argumentValidator = new ArgumentValidator("SETF"); this.argumentValidator.setExactNumberOfArguments(2); this.argumentValidator.setFirstArgumentExpectedType(Symbol.class); + this.executionContext = ExecutionContext.getInstance(); } public SExpression call(Cons argumentList) { @@ -40,14 +24,14 @@ public class SETF extends LispFunction { SExpression symbol = argumentList.getCar(); SExpression value = EVAL.eval(cdr.getCar()); - SymbolTable table = findTableForSymbol(symbol); + SymbolTable table = findScopeOfSymbol(symbol); table.put(symbol.toString(), value); return value; } - private SymbolTable findTableForSymbol(SExpression symbol) { - SymbolTable table = symbolTable; + private SymbolTable findScopeOfSymbol(SExpression symbol) { + SymbolTable table = executionContext.getScope(); while (!isSymbolInTable(symbol, table) && !isGlobalTable(table)) table = table.getParent(); diff --git a/src/table/ExecutionContext.java b/src/table/ExecutionContext.java new file mode 100644 index 0000000..7d94ad0 --- /dev/null +++ b/src/table/ExecutionContext.java @@ -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; + } +} diff --git a/test/function/builtin/special/SETFTester.java b/test/function/builtin/special/SETFTester.java index ca0fde6..c481cf3 100644 --- a/test/function/builtin/special/SETFTester.java +++ b/test/function/builtin/special/SETFTester.java @@ -8,13 +8,19 @@ import org.junit.*; import function.ArgumentValidator.*; import function.builtin.EVAL.UndefinedSymbolException; import sexpression.LispNumber; -import table.SymbolTable; +import table.*; public class SETFTester { + + private ExecutionContext executionContext; + + public SETFTester() { + this.executionContext = ExecutionContext.getInstance(); + } @Before public void setUp() { - SETF.setSymbolTable(new SymbolTable()); + executionContext.clearContext(); } @Test @@ -26,45 +32,45 @@ public class SETFTester { @Test public void lookupDefinedSymbol() { evaluateString("(setf a 23)"); - assertSExpressionsMatch(new LispNumber("23"), SETF.lookupSymbolValue("A")); + assertSExpressionsMatch(new LispNumber("23"), executionContext.lookupSymbolValue("A")); } @Test public void lookupUndefinedSymbol() { - assertNull(SETF.lookupSymbolValue("A")); + assertNull(executionContext.lookupSymbolValue("A")); } @Test public void setfGlobalVariable() { evaluateString("(setf a 23)"); - SymbolTable global = SETF.getSymbolTable(); - SETF.setSymbolTable(new SymbolTable(global)); + SymbolTable global = executionContext.getScope(); + executionContext.setScope(new SymbolTable(global)); evaluateString("(setf a 94)"); - SETF.setSymbolTable(global); + executionContext.setScope(global); assertSExpressionsMatch(new LispNumber("94"), evaluateString("a")); } @Test(expected = UndefinedSymbolException.class) public void setfLocalVariableDefined_DoesNotSetGlobal() { - SymbolTable global = SETF.getSymbolTable(); + SymbolTable global = executionContext.getScope(); SymbolTable local = new SymbolTable(global); local.put("A", new LispNumber("99")); - SETF.setSymbolTable(local); + executionContext.setScope(local); evaluateString("(setf a 94)"); - SETF.setSymbolTable(global); + executionContext.setScope(global); evaluateString("a"); } @Test public void setfLocalVariableUndefined_SetsGlobal() { - SymbolTable global = SETF.getSymbolTable(); + SymbolTable global = executionContext.getScope(); SymbolTable local = new SymbolTable(global); - SETF.setSymbolTable(local); + executionContext.setScope(local); evaluateString("(setf a 94)"); - SETF.setSymbolTable(global); + executionContext.setScope(global); assertSExpressionsMatch(new LispNumber("94"), evaluateString("a")); }