diff --git a/src/main/java/function/UserDefinedFunction.java b/src/main/java/function/UserDefinedFunction.java index 6ed4569..bb481c6 100644 --- a/src/main/java/function/UserDefinedFunction.java +++ b/src/main/java/function/UserDefinedFunction.java @@ -34,7 +34,7 @@ public class UserDefinedFunction extends LispFunction { this.name = name; this.body = body; this.lambdaExpression = new Cons(new Symbol(name), new Cons(lambdaList, body)); - this.executionContext = ExecutionContext.getInstance(); + this.executionContext = ExecutionContext.INSTANCE; this.functionScope = executionContext.getScope(); this.keywordRestParameter = null; this.isKeywordRestPresent = false; diff --git a/src/main/java/function/builtin/EVAL.java b/src/main/java/function/builtin/EVAL.java index 0a2d576..d7cbd34 100644 --- a/src/main/java/function/builtin/EVAL.java +++ b/src/main/java/function/builtin/EVAL.java @@ -14,6 +14,7 @@ import sexpression.Symbol; import table.ExecutionContext; import static function.builtin.cons.LIST.makeList; +import static function.builtin.special.LAMBDA.Lambda; import static java.text.MessageFormat.format; import static sexpression.Nil.NIL; import static sexpression.Symbol.T; @@ -22,7 +23,7 @@ import static table.FunctionTable.lookupFunction; @FunctionNames({ "EVAL" }) public class EVAL extends LispFunction { - private static ExecutionContext executionContext = ExecutionContext.getInstance(); + private static ExecutionContext executionContext = ExecutionContext.INSTANCE; public static SExpression eval(SExpression sExpression) { Cons argumentList = makeList(sExpression); @@ -59,8 +60,8 @@ public class EVAL extends LispFunction { private static LispFunction createLambdaFunction(SExpression lambdaExpression) { if (lambdaExpression.isFunction()) return ((LambdaExpression) lambdaExpression).getFunction(); - else if (LAMBDA.Companion.isLambdaExpression(lambdaExpression)) - return LAMBDA.Companion.createFunction((Cons) lambdaExpression); + else if (Lambda.isLambdaExpression(lambdaExpression)) + return Lambda.createFunction((Cons) lambdaExpression); else throw new UndefinedFunctionException(lambdaExpression); } @@ -73,7 +74,7 @@ public class EVAL extends LispFunction { else if (symbolName.startsWith(":")) return new Symbol(symbolName); - return ExecutionContext.getInstance().lookupSymbolValue(symbolName); + return ExecutionContext.INSTANCE.lookupSymbolValue(symbolName); } private ArgumentValidator argumentValidator; diff --git a/src/main/java/function/builtin/SET.java b/src/main/java/function/builtin/SET.java index ee70249..1810449 100644 --- a/src/main/java/function/builtin/SET.java +++ b/src/main/java/function/builtin/SET.java @@ -25,7 +25,7 @@ public class SET extends LispFunction { this.argumentValidator = new ArgumentValidator(name); this.argumentValidator.setExactNumberOfArguments(2); this.argumentValidator.setFirstArgumentExpectedType(Symbol.class); - this.executionContext = ExecutionContext.getInstance(); + this.executionContext = ExecutionContext.INSTANCE; } @Override diff --git a/src/main/java/function/builtin/SYMBOLS.java b/src/main/java/function/builtin/SYMBOLS.java index e1058a6..834fb86 100644 --- a/src/main/java/function/builtin/SYMBOLS.java +++ b/src/main/java/function/builtin/SYMBOLS.java @@ -16,7 +16,7 @@ public class SYMBOLS extends LispFunction { public SYMBOLS(String name) { this.argumentValidator = new ArgumentValidator(name); this.argumentValidator.setExactNumberOfArguments(0); - this.executionContext = ExecutionContext.getInstance(); + this.executionContext = ExecutionContext.INSTANCE; } @Override diff --git a/src/main/java/function/builtin/special/LAMBDA.kt b/src/main/java/function/builtin/special/LAMBDA.kt index 580f5d4..36baeca 100644 --- a/src/main/java/function/builtin/special/LAMBDA.kt +++ b/src/main/java/function/builtin/special/LAMBDA.kt @@ -40,7 +40,7 @@ class LAMBDA(name: String) : LispSpecialFunction() { return Cons(Symbol("LAMBDA"), argumentList) } - companion object { + companion object Lambda { fun isLambdaExpression(sexpr: SExpression): Boolean { if (sexpr.isCons) { diff --git a/src/main/java/function/builtin/special/LET.java b/src/main/java/function/builtin/special/LET.java index 73112d5..012d95a 100644 --- a/src/main/java/function/builtin/special/LET.java +++ b/src/main/java/function/builtin/special/LET.java @@ -33,7 +33,7 @@ public class LET extends LispSpecialFunction { this.pairValidator.setMaximumNumberOfArguments(2); this.pairValidator.setFirstArgumentExpectedType(Symbol.class); - this.executionContext = ExecutionContext.getInstance(); + this.executionContext = ExecutionContext.INSTANCE; } @Override diff --git a/src/main/java/function/builtin/special/RECUR.java b/src/main/java/function/builtin/special/RECUR.java index f3006d4..faef519 100644 --- a/src/main/java/function/builtin/special/RECUR.java +++ b/src/main/java/function/builtin/special/RECUR.java @@ -18,7 +18,7 @@ public class RECUR extends LispSpecialFunction { public RECUR(String name) { this.argumentValidator = new ArgumentValidator(name); - this.executionContext = ExecutionContext.getInstance(); + this.executionContext = ExecutionContext.INSTANCE; } @Override diff --git a/src/main/java/table/ExecutionContext.java b/src/main/java/table/ExecutionContext.java deleted file mode 100644 index a20d3df..0000000 --- a/src/main/java/table/ExecutionContext.java +++ /dev/null @@ -1,129 +0,0 @@ -package table; - -import function.LispFunction; -import sexpression.Cons; -import sexpression.SExpression; - -import java.util.Stack; - -import static sexpression.Nil.NIL; - -public class ExecutionContext { - - private static ExecutionContext uniqueInstance = new ExecutionContext(); - - public static ExecutionContext getInstance() { - return uniqueInstance; - } - - private SymbolTable scope; - private Stack functionCalls; - private boolean recur; - - private ExecutionContext() { - clearContext(); - } - - public void clearContext() { - this.scope = new SymbolTable(); - this.functionCalls = new Stack<>(); - this.clearRecur(); - } - - public SymbolTable getScope() { - return scope; - } - - public void setScope(SymbolTable scope) { - this.scope = scope; - } - - public void restoreGlobalScope() { - while (!scope.isGlobal()) - scope = scope.getParent(); - } - - public SExpression lookupSymbolValue(String symbolName) { - for (SymbolTable t = scope; t != null; t = t.getParent()) - if (t.contains(symbolName)) - return t.get(symbolName); - - return null; - } - - public Cons toList() { - Cons symbols = NIL; - - for (SymbolTable t = scope; t != null; t = t.getParent()) - symbols = new Cons(t.toList(), symbols); - - return symbols; - } - - public void pushFunctionCall(LispFunction function) { - functionCalls.push(new LispFunctionRecurInfo(function)); - } - - public void popFunctionCall() { - functionCalls.pop(); - } - - public boolean isInFunctionCall() { - return !functionCalls.empty(); - } - - public LispFunction getCurrentFunction() { - return functionCalls.peek().getLispFunction(); - } - - public boolean isRecur() { - return recur; - } - - public void setRecur() { - recur = true; - } - - public void clearRecur() { - recur = false; - } - - public boolean isRecurInitializing() { - return functionCalls.peek().isRecurInitializing(); - } - - public void setRecurInitializing() { - functionCalls.peek().setRecurInitializing(); - } - - public void clearRecurInitializing() { - functionCalls.peek().clearRecurInitializing(); - } - - public static class LispFunctionRecurInfo { - - private LispFunction lispFunction; - private boolean recurInitializing; - - public LispFunctionRecurInfo(LispFunction lispFunction) { - this.lispFunction = lispFunction; - this.clearRecurInitializing(); - } - - public boolean isRecurInitializing() { - return recurInitializing; - } - - public void setRecurInitializing() { - this.recurInitializing = true; - } - - public void clearRecurInitializing() { - this.recurInitializing = false; - } - - public LispFunction getLispFunction() { - return lispFunction; - } - } -} diff --git a/src/main/java/table/ExecutionContext.kt b/src/main/java/table/ExecutionContext.kt new file mode 100644 index 0000000..7e6bdf0 --- /dev/null +++ b/src/main/java/table/ExecutionContext.kt @@ -0,0 +1,101 @@ +package table + +import function.LispFunction +import sexpression.Cons +import sexpression.SExpression + +import java.util.Stack + +import sexpression.Nil.NIL + +object ExecutionContext { + + private val functionCalls: Stack = Stack() + + var scope: SymbolTable = SymbolTable(NullSymbolTable) + var isRecur: Boolean = false + private set + + val isInFunctionCall: Boolean + get() = !functionCalls.empty() + + val currentFunction: LispFunction + get() = functionCalls.peek().lispFunction + + val isRecurInitializing: Boolean + get() = functionCalls.peek().isRecurInitializing + + fun clearContext() { + scope = SymbolTable(NullSymbolTable) + functionCalls.clear() + clearRecur() + } + + fun restoreGlobalScope() { + while (!scope.isGlobal) + scope = scope.parent + } + + fun lookupSymbolValue(symbolName: String): SExpression? { + var t = scope + while (t !== NullSymbolTable) { + if (t.contains(symbolName)) + return t.get(symbolName) + + t = t.parent + } + + return null + } + + fun toList(): Cons { + var symbols: Cons = NIL + + var t = scope + while (t !== NullSymbolTable) { + symbols = Cons(t.toList(), symbols) + t = t.parent + } + + return symbols + } + + fun pushFunctionCall(function: LispFunction) { + functionCalls.push(LispFunctionRecurInfo(function)) + } + + fun popFunctionCall() { + functionCalls.pop() + } + + fun setRecur() { + isRecur = true + } + + fun clearRecur() { + isRecur = false + } + + fun setRecurInitializing() { + functionCalls.peek().setRecurInitializing() + } + + fun clearRecurInitializing() { + functionCalls.peek().clearRecurInitializing() + } + + class LispFunctionRecurInfo(val lispFunction: LispFunction) { + var isRecurInitializing: Boolean = false + private set + + fun setRecurInitializing() { + isRecurInitializing = true + } + + fun clearRecurInitializing() { + isRecurInitializing = false + } + } + + object NullSymbolTable : SymbolTable() +} diff --git a/src/main/java/table/SymbolTable.java b/src/main/java/table/SymbolTable.java index b04636b..8e16395 100644 --- a/src/main/java/table/SymbolTable.java +++ b/src/main/java/table/SymbolTable.java @@ -3,6 +3,7 @@ package table; import sexpression.Cons; import sexpression.SExpression; import sexpression.Symbol; +import table.ExecutionContext.NullSymbolTable; import java.util.HashMap; import java.util.Map.Entry; @@ -19,7 +20,7 @@ public class SymbolTable { private SymbolTable parent; public SymbolTable() { - this(null); + this(NullSymbolTable.INSTANCE); } public SymbolTable(SymbolTable parent) { @@ -44,7 +45,7 @@ public class SymbolTable { } public boolean isGlobal() { - return parent == null; + return parent == NullSymbolTable.INSTANCE; } public Cons toList() { diff --git a/src/test/java/acceptance/fixture/LispInterpreterFixture.java b/src/test/java/acceptance/fixture/LispInterpreterFixture.java index 5c2597d..a2c6ab6 100644 --- a/src/test/java/acceptance/fixture/LispInterpreterFixture.java +++ b/src/test/java/acceptance/fixture/LispInterpreterFixture.java @@ -19,7 +19,7 @@ import static util.Path.getPathPrefix; public class LispInterpreterFixture { private static ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - private static ExecutionContext executionContext = ExecutionContext.getInstance(); + private static ExecutionContext executionContext = ExecutionContext.INSTANCE; private static RuntimeEnvironment environment = RuntimeEnvironment.getInstance(); private static LispInterpreter interpreter = null; diff --git a/src/test/java/function/builtin/special/LAMBDATest.java b/src/test/java/function/builtin/special/LAMBDATest.java index d36a56d..4bd54e8 100644 --- a/src/test/java/function/builtin/special/LAMBDATest.java +++ b/src/test/java/function/builtin/special/LAMBDATest.java @@ -11,6 +11,7 @@ import sexpression.LispNumber; import sexpression.Symbol; import testutil.SymbolAndFunctionCleaner; +import static function.builtin.special.LAMBDA.Lambda; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static sexpression.LispNumber.ONE; @@ -47,12 +48,12 @@ public class LAMBDATest extends SymbolAndFunctionCleaner { public void lambdaExpressionIsLambdaExpression() { Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), new Cons(NIL, new Cons(NIL, NIL))); - assertTrue(LAMBDA.Companion.isLambdaExpression(lambdaExpression)); + assertTrue(Lambda.isLambdaExpression(lambdaExpression)); } @Test public void somethingElseIsNotLambdaExpression() { - assertFalse(LAMBDA.Companion.isLambdaExpression(T)); + assertFalse(Lambda.isLambdaExpression(T)); } @Test @@ -60,7 +61,7 @@ public class LAMBDATest extends SymbolAndFunctionCleaner { Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), new Cons(NIL, new Cons(NIL, NIL))); assertSExpressionsMatch(parseString("(:LAMBDA () ())"), - LAMBDA.Companion.createFunction(lambdaExpression).getLambdaExpression()); + Lambda.createFunction(lambdaExpression).getLambdaExpression()); } @Test(expected = DottedArgumentListException.class) @@ -81,14 +82,14 @@ public class LAMBDATest extends SymbolAndFunctionCleaner { public void createFunctionWithDottedArgumentList() { Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), new Cons(NIL, ONE)); - LAMBDA.Companion.createFunction(lambdaExpression); + Lambda.createFunction(lambdaExpression); } @Test(expected = BadArgumentTypeException.class) public void createFunctionWithNonList() { Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), ONE); - LAMBDA.Companion.createFunction(lambdaExpression); + Lambda.createFunction(lambdaExpression); } @Test(expected = BadArgumentTypeException.class) diff --git a/src/test/java/table/ExecutionContextTest.kt b/src/test/java/table/ExecutionContextTest.kt index ea99313..9529516 100644 --- a/src/test/java/table/ExecutionContextTest.kt +++ b/src/test/java/table/ExecutionContextTest.kt @@ -8,11 +8,12 @@ import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS import sexpression.Nil.NIL import sexpression.Symbol.T +import table.ExecutionContext.NullSymbolTable @TestInstance(PER_CLASS) class ExecutionContextTest { - private val executionContext: ExecutionContext = ExecutionContext.getInstance() + private val executionContext: ExecutionContext = ExecutionContext @BeforeEach fun setUp() { @@ -40,7 +41,7 @@ class ExecutionContextTest { assertThat(executionContext.scope).isEqualTo(scope) executionContext.clearContext() assertThat(executionContext.scope).isNotEqualTo(scope) - assertThat(executionContext.scope.parent).isNull() + assertThat(executionContext.scope.parent).isEqualTo(NullSymbolTable) } @Test diff --git a/src/test/java/testutil/SymbolAndFunctionCleaner.java b/src/test/java/testutil/SymbolAndFunctionCleaner.java index cd96210..7d97484 100644 --- a/src/test/java/testutil/SymbolAndFunctionCleaner.java +++ b/src/test/java/testutil/SymbolAndFunctionCleaner.java @@ -11,7 +11,7 @@ public abstract class SymbolAndFunctionCleaner { protected ExecutionContext executionContext; public SymbolAndFunctionCleaner() { - this.executionContext = ExecutionContext.getInstance(); + this.executionContext = ExecutionContext.INSTANCE; } @Before