diff --git a/src/main/kotlin/function/builtin/special/LET.java b/src/main/kotlin/function/builtin/special/LET.java deleted file mode 100644 index 6dd55ba..0000000 --- a/src/main/kotlin/function/builtin/special/LET.java +++ /dev/null @@ -1,93 +0,0 @@ -package function.builtin.special; - -import function.ArgumentValidator; -import function.FunctionNames; -import function.LispSpecialFunction; -import sexpression.Cons; -import sexpression.Nil; -import sexpression.SExpression; -import sexpression.Symbol; -import table.ExecutionContext; -import table.SymbolTable; - -import static function.builtin.Eval.eval; - -@FunctionNames({ "LET" }) -public class LET extends LispSpecialFunction { - - private ArgumentValidator argumentValidator; - private ArgumentValidator variableDefinitionListValidator; - private ArgumentValidator pairValidator; - protected ExecutionContext executionContext; - - public LET(String name) { - this.argumentValidator = new ArgumentValidator(name); - this.argumentValidator.setMinimumNumberOfArguments(1); - this.argumentValidator.setFirstArgumentExpectedType(Cons.class); - - this.variableDefinitionListValidator = new ArgumentValidator(name + "|pair-list|"); - this.variableDefinitionListValidator.setEveryArgumentExpectedType(Cons.class); - - this.pairValidator = new ArgumentValidator(name + "|pair|"); - this.pairValidator.setMinimumNumberOfArguments(1); - this.pairValidator.setMaximumNumberOfArguments(2); - this.pairValidator.setFirstArgumentExpectedType(Symbol.class); - - this.executionContext = ExecutionContext.INSTANCE; - } - - @Override - public SExpression call(Cons argumentList) { - argumentValidator.validate(argumentList); - Cons variableDefinitions = (Cons) argumentList.getFirst(); - Cons body = (Cons) argumentList.getRest(); - - return evaluateInScope(variableDefinitions, body); - } - - private SExpression evaluateInScope(Cons variableDefinitions, Cons body) { - SymbolTable localScope = createLocalScope(variableDefinitions); - SExpression lastEvaluation = evaluateBody(body); - restorePreviousScope(localScope); - - return lastEvaluation; - } - - protected SymbolTable createLocalScope(Cons variableDefinitions) { - SymbolTable localScope = new SymbolTable(executionContext.getScope()); - addVariablesToScope(localScope, variableDefinitions); - executionContext.setScope(localScope); - - return localScope; - } - - protected void addVariablesToScope(SymbolTable scope, Cons variableDefinitions) { - variableDefinitionListValidator.validate(variableDefinitions); - - for (; variableDefinitions.isCons(); variableDefinitions = (Cons) variableDefinitions.getRest()) - addPairToScope((Cons) variableDefinitions.getFirst(), scope); - } - - private void addPairToScope(Cons symbolValuePair, SymbolTable scope) { - pairValidator.validate(symbolValuePair); - - Cons restOfPair = (Cons) symbolValuePair.getRest(); - SExpression symbol = symbolValuePair.getFirst(); - SExpression value = restOfPair.getFirst(); - - scope.set(symbol.toString(), eval(value)); - } - - private SExpression evaluateBody(Cons body) { - SExpression lastEvaluation = Nil.INSTANCE; - - for (; body.isCons(); body = (Cons) body.getRest()) - lastEvaluation = eval(body.getFirst()); - - return lastEvaluation; - } - - private void restorePreviousScope(SymbolTable localScope) { - executionContext.setScope(localScope.getParent()); - } -} diff --git a/src/main/kotlin/function/builtin/special/LET_STAR.java b/src/main/kotlin/function/builtin/special/LET_STAR.java deleted file mode 100644 index 41373c5..0000000 --- a/src/main/kotlin/function/builtin/special/LET_STAR.java +++ /dev/null @@ -1,22 +0,0 @@ -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); - } - - @Override - protected SymbolTable createLocalScope(Cons variableDefinitions) { - SymbolTable localScope = new SymbolTable(executionContext.getScope()); - executionContext.setScope(localScope); - addVariablesToScope(localScope, variableDefinitions); - - return localScope; - } -} diff --git a/src/main/kotlin/function/builtin/special/Let.kt b/src/main/kotlin/function/builtin/special/Let.kt new file mode 100644 index 0000000..e89e7bf --- /dev/null +++ b/src/main/kotlin/function/builtin/special/Let.kt @@ -0,0 +1,77 @@ +package function.builtin.special + +import function.ArgumentValidator +import function.FunctionNames +import function.LispSpecialFunction +import function.builtin.Eval.Companion.eval +import sexpression.Cons +import sexpression.Nil +import sexpression.SExpression +import sexpression.Symbol +import table.ExecutionContext +import table.SymbolTable + +@FunctionNames("LET") +open class Let(name: String) : LispSpecialFunction() { + + private val argumentValidator = ArgumentValidator(name).apply { + setMinimumNumberOfArguments(1) + setFirstArgumentExpectedType(Cons::class.java) + + } + + private val variableDefinitionListValidator = ArgumentValidator("$name|pair-list|").apply { + setEveryArgumentExpectedType(Cons::class.java) + } + + private val pairValidator: ArgumentValidator = ArgumentValidator("$name|pair|").apply { + setMinimumNumberOfArguments(1) + setMaximumNumberOfArguments(2) + setFirstArgumentExpectedType(Symbol::class.java) + } + + override fun call(argumentList: Cons): SExpression { + argumentValidator.validate(argumentList) + + val variableDefinitions = argumentList.first as Cons + val body = argumentList.rest as Cons + + return evaluateInScope(variableDefinitions, body) + } + + private fun evaluateInScope(variableDefinitions: Cons, body: Cons): SExpression { + val localScope = createLocalScope(variableDefinitions) + val lastEvaluation = evaluateBody(body) + restorePreviousScope(localScope) + + return lastEvaluation + } + + protected open fun createLocalScope(variableDefinitions: Cons) = + SymbolTable(ExecutionContext.scope).also { + addVariablesToScope(it, variableDefinitions) + ExecutionContext.scope = it + } + + protected fun addVariablesToScope(scope: SymbolTable, variableDefinitions: Cons) { + variableDefinitionListValidator.validate(variableDefinitions) + variableDefinitions.forEach { addPairToScope(it.first as Cons, scope) } + } + + private fun addPairToScope(symbolValuePair: Cons, scope: SymbolTable) { + pairValidator.validate(symbolValuePair) + + val restOfPair = symbolValuePair.rest as Cons + val symbol = symbolValuePair.first.toString() + val value = eval(restOfPair.first) + + scope[symbol] = value + } + + private fun evaluateBody(body: Cons) = + body.fold(Nil as SExpression) { _, cons -> eval(cons.first) } + + private fun restorePreviousScope(localScope: SymbolTable) { + ExecutionContext.scope = localScope.parent!! + } +} diff --git a/src/main/kotlin/function/builtin/special/LetStar.kt b/src/main/kotlin/function/builtin/special/LetStar.kt new file mode 100644 index 0000000..198575c --- /dev/null +++ b/src/main/kotlin/function/builtin/special/LetStar.kt @@ -0,0 +1,16 @@ +package function.builtin.special + +import function.FunctionNames +import sexpression.Cons +import table.ExecutionContext +import table.SymbolTable + +@FunctionNames("LET*") +class LetStar(name: String) : Let(name) { + + override fun createLocalScope(variableDefinitions: Cons) = + SymbolTable(ExecutionContext.scope).also { + ExecutionContext.scope = it + addVariablesToScope(it, variableDefinitions) + } +} diff --git a/src/test/kotlin/function/builtin/special/LET_STARTest.java b/src/test/kotlin/function/builtin/special/LetStarTest.java similarity index 98% rename from src/test/kotlin/function/builtin/special/LET_STARTest.java rename to src/test/kotlin/function/builtin/special/LetStarTest.java index ba6ad70..54f6521 100644 --- a/src/test/kotlin/function/builtin/special/LET_STARTest.java +++ b/src/test/kotlin/function/builtin/special/LetStarTest.java @@ -14,7 +14,7 @@ import testutil.SymbolAndFunctionCleaner; import static testutil.TestUtilities.assertSExpressionsMatch; import static testutil.TestUtilities.evaluateString; -public class LET_STARTest extends SymbolAndFunctionCleaner { +public class LetStarTest extends SymbolAndFunctionCleaner { @Test public void simpleLet() { diff --git a/src/test/kotlin/function/builtin/special/LETTest.java b/src/test/kotlin/function/builtin/special/LetTest.java similarity index 98% rename from src/test/kotlin/function/builtin/special/LETTest.java rename to src/test/kotlin/function/builtin/special/LetTest.java index 4187300..3110f7c 100644 --- a/src/test/kotlin/function/builtin/special/LETTest.java +++ b/src/test/kotlin/function/builtin/special/LetTest.java @@ -14,7 +14,7 @@ import testutil.SymbolAndFunctionCleaner; import static testutil.TestUtilities.assertSExpressionsMatch; import static testutil.TestUtilities.evaluateString; -public class LETTest extends SymbolAndFunctionCleaner { +public class LetTest extends SymbolAndFunctionCleaner { @Test public void simpleLet() {