diff --git a/src/main/kotlin/function/builtin/special/AND.java b/src/main/kotlin/function/builtin/special/AND.java deleted file mode 100644 index 7f299f5..0000000 --- a/src/main/kotlin/function/builtin/special/AND.java +++ /dev/null @@ -1,43 +0,0 @@ -package function.builtin.special; - -import function.ArgumentValidator; -import function.FunctionNames; -import function.LispSpecialFunction; -import recursion.TailCall; -import sexpression.Cons; -import sexpression.SExpression; -import sexpression.Symbol; - -import static function.builtin.Eval.eval; -import static recursion.TailCalls.done; -import static recursion.TailCalls.tailCall; - -@FunctionNames({ "AND" }) -public class AND extends LispSpecialFunction { - - private ArgumentValidator argumentValidator; - - public AND(String name) { - this.argumentValidator = new ArgumentValidator(name); - } - - @Override - public SExpression call(Cons argumentList) { - argumentValidator.validate(argumentList); - - return callTailRecursive(argumentList, Symbol.Companion.getT()).invoke(); - } - - private TailCall callTailRecursive(Cons argumentList, SExpression lastValue) { - SExpression currentValue = eval(argumentList.getFirst()); - Cons remainingValues = (Cons) argumentList.getRest(); - - if (argumentList.isNull()) - return done(lastValue); - - if (currentValue.isNull()) - return done(currentValue); - - return tailCall(() -> callTailRecursive(remainingValues, currentValue)); - } -} diff --git a/src/main/kotlin/function/builtin/special/And.kt b/src/main/kotlin/function/builtin/special/And.kt new file mode 100644 index 0000000..9f173c6 --- /dev/null +++ b/src/main/kotlin/function/builtin/special/And.kt @@ -0,0 +1,32 @@ +package function.builtin.special + +import function.ArgumentValidator +import function.FunctionNames +import function.LispSpecialFunction +import function.builtin.Eval.Companion.eval +import sexpression.Cons +import sexpression.SExpression +import sexpression.Symbol.Companion.T + +@FunctionNames("AND") +class And(name: String) : LispSpecialFunction() { + + private val argumentValidator = ArgumentValidator(name) + + override fun call(argumentList: Cons): SExpression { + argumentValidator.validate(argumentList) + + return callTailRecursive(argumentList, T) + } + + private tailrec fun callTailRecursive(argumentList: Cons, lastValue: SExpression): SExpression { + val currentValue = eval(argumentList.first) + val remainingValues = argumentList.rest as Cons + + return when { + argumentList.isNull -> lastValue + currentValue.isNull -> currentValue + else -> callTailRecursive(remainingValues, currentValue) + } + } +} diff --git a/src/main/kotlin/function/builtin/special/OR.java b/src/main/kotlin/function/builtin/special/OR.java deleted file mode 100644 index ba310bc..0000000 --- a/src/main/kotlin/function/builtin/special/OR.java +++ /dev/null @@ -1,39 +0,0 @@ -package function.builtin.special; - -import function.ArgumentValidator; -import function.FunctionNames; -import function.LispSpecialFunction; -import recursion.TailCall; -import sexpression.Cons; -import sexpression.SExpression; - -import static function.builtin.Eval.eval; -import static recursion.TailCalls.done; -import static recursion.TailCalls.tailCall; - -@FunctionNames({ "OR" }) -public class OR extends LispSpecialFunction { - - private ArgumentValidator argumentValidator; - - public OR(String name) { - this.argumentValidator = new ArgumentValidator(name); - } - - @Override - public SExpression call(Cons argumentList) { - argumentValidator.validate(argumentList); - - return callTailRecursive(argumentList).invoke(); - } - - private TailCall callTailRecursive(Cons argumentList) { - SExpression currentValue = eval(argumentList.getFirst()); - Cons remainingValues = (Cons) argumentList.getRest(); - - if (remainingValues.isNull() || !currentValue.isNull()) - return done(currentValue); - - return tailCall(() -> callTailRecursive(remainingValues)); - } -} diff --git a/src/main/kotlin/function/builtin/special/Or.kt b/src/main/kotlin/function/builtin/special/Or.kt new file mode 100644 index 0000000..cca2eed --- /dev/null +++ b/src/main/kotlin/function/builtin/special/Or.kt @@ -0,0 +1,30 @@ +package function.builtin.special + +import function.ArgumentValidator +import function.FunctionNames +import function.LispSpecialFunction +import function.builtin.Eval.Companion.eval +import sexpression.Cons +import sexpression.SExpression + +@FunctionNames("OR") +class Or(name: String) : LispSpecialFunction() { + + private val argumentValidator = ArgumentValidator(name) + + override fun call(argumentList: Cons): SExpression { + argumentValidator.validate(argumentList) + + return callTailRecursive(argumentList) + } + + private tailrec fun callTailRecursive(argumentList: Cons): SExpression { + val currentValue = eval(argumentList.first) + val remainingValues = argumentList.rest as Cons + + return if (remainingValues.isNull || !currentValue.isNull) + currentValue + else + callTailRecursive(remainingValues) + } +} diff --git a/src/test/kotlin/function/builtin/special/ANDTest.java b/src/test/kotlin/function/builtin/special/ANDTest.java deleted file mode 100644 index 0ec6c14..0000000 --- a/src/test/kotlin/function/builtin/special/ANDTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package function.builtin.special; - -import function.builtin.Eval.UndefinedSymbolException; -import org.junit.Test; -import sexpression.LispNumber; -import testutil.SymbolAndFunctionCleaner; - -import static testutil.TestUtilities.assertSExpressionsMatch; -import static testutil.TestUtilities.evaluateString; -import static testutil.TypeAssertions.assertNil; -import static testutil.TypeAssertions.assertT; - -public class ANDTest extends SymbolAndFunctionCleaner { - - @Test - public void andByItself() { - String input = "(and)"; - - assertT(evaluateString(input)); - } - - @Test - public void andWithNil() { - String input = "(and nil)"; - - assertNil(evaluateString(input)); - } - - @Test - public void andWithT() { - String input = "(and t)"; - - assertT(evaluateString(input)); - } - - @Test - public void andWithNumber() { - String input = "(and 7)"; - - assertSExpressionsMatch(new LispNumber("7"), evaluateString(input)); - } - - @Test - public void andWithSeveralValues() { - String input = "(and t t nil t t)"; - - assertNil(evaluateString(input)); - } - - @Test - public void andWithSeveralNumbers() { - String input = "(and 1 2 3)"; - - assertSExpressionsMatch(new LispNumber("3"), evaluateString(input)); - } - - @Test(expected = UndefinedSymbolException.class) - public void andShortCircuits() { - String input = "(and nil (setq x 22))"; - - assertNil(evaluateString(input)); - evaluateString("x"); - } -} diff --git a/src/test/kotlin/function/builtin/special/AndTest.kt b/src/test/kotlin/function/builtin/special/AndTest.kt new file mode 100644 index 0000000..1909c43 --- /dev/null +++ b/src/test/kotlin/function/builtin/special/AndTest.kt @@ -0,0 +1,69 @@ +package function.builtin.special + +import function.builtin.Eval.UndefinedSymbolException +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Test +import sexpression.LispNumber +import testutil.LispTestInstance +import testutil.SymbolAndFunctionCleaner +import testutil.TestUtilities.assertSExpressionsMatch +import testutil.TestUtilities.evaluateString +import testutil.TypeAssertions.assertNil +import testutil.TypeAssertions.assertT + +@LispTestInstance +class AndTest : SymbolAndFunctionCleaner() { + + @Test + fun andByItself() { + val input = "(and)" + + assertT(evaluateString(input)) + } + + @Test + fun andWithNil() { + val input = "(and nil)" + + assertNil(evaluateString(input)) + } + + @Test + fun andWithT() { + val input = "(and t)" + + assertT(evaluateString(input)) + } + + @Test + fun andWithNumber() { + val input = "(and 7)" + + assertSExpressionsMatch(LispNumber("7"), evaluateString(input)) + } + + @Test + fun andWithSeveralValues() { + val input = "(and t t nil t t)" + + assertNil(evaluateString(input)) + } + + @Test + fun andWithSeveralNumbers() { + val input = "(and 1 2 3)" + + assertSExpressionsMatch(LispNumber("3"), evaluateString(input)) + } + + @Test + fun andShortCircuits() { + val input = "(and nil (setq x 22))" + + assertNil(evaluateString(input)) + + assertThrows(UndefinedSymbolException::class.java) { + evaluateString("x") + } + } +} diff --git a/src/test/kotlin/function/builtin/special/LambdaTest.kt b/src/test/kotlin/function/builtin/special/LambdaTest.kt index 6409dbe..244b6a3 100644 --- a/src/test/kotlin/function/builtin/special/LambdaTest.kt +++ b/src/test/kotlin/function/builtin/special/LambdaTest.kt @@ -16,11 +16,13 @@ import sexpression.LispNumber.Companion.ONE import sexpression.Nil import sexpression.Symbol import sexpression.Symbol.Companion.T +import testutil.LispTestInstance import testutil.SymbolAndFunctionCleaner import testutil.TestUtilities.assertSExpressionsMatch import testutil.TestUtilities.evaluateString import testutil.TestUtilities.parseString +@LispTestInstance class LambdaTest : SymbolAndFunctionCleaner() { @Test diff --git a/src/test/kotlin/function/builtin/special/ORTest.java b/src/test/kotlin/function/builtin/special/ORTest.java deleted file mode 100644 index cc9006d..0000000 --- a/src/test/kotlin/function/builtin/special/ORTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package function.builtin.special; - -import function.builtin.Eval.UndefinedSymbolException; -import org.junit.Test; -import sexpression.LispNumber; -import testutil.SymbolAndFunctionCleaner; - -import static testutil.TestUtilities.assertSExpressionsMatch; -import static testutil.TestUtilities.evaluateString; -import static testutil.TypeAssertions.assertNil; -import static testutil.TypeAssertions.assertT; - -public class ORTest extends SymbolAndFunctionCleaner { - - @Test - public void orByItself() { - String input = "(or)"; - - assertNil(evaluateString(input)); - } - - @Test - public void orWithNil() { - String input = "(or nil)"; - - assertNil(evaluateString(input)); - } - - @Test - public void orWithT() { - String input = "(or t)"; - - assertT(evaluateString(input)); - } - - @Test - public void orWithNumber() { - String input = "(or 7)"; - - assertSExpressionsMatch(new LispNumber("7"), evaluateString(input)); - } - - @Test - public void orWithSeveralValues() { - String input = "(or nil nil nil t nil)"; - - assertT(evaluateString(input)); - } - - @Test - public void orWithSeveralNumbers() { - String input = "(or 1 2 3)"; - - assertSExpressionsMatch(new LispNumber("1"), evaluateString(input)); - } - - @Test(expected = UndefinedSymbolException.class) - public void orShortCircuits() { - String input = "(or t (setq x 22))"; - - assertT(evaluateString(input)); - evaluateString("x"); - } -} diff --git a/src/test/kotlin/function/builtin/special/OrTest.kt b/src/test/kotlin/function/builtin/special/OrTest.kt new file mode 100644 index 0000000..2cd48e2 --- /dev/null +++ b/src/test/kotlin/function/builtin/special/OrTest.kt @@ -0,0 +1,69 @@ +package function.builtin.special + +import function.builtin.Eval.UndefinedSymbolException +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Test +import sexpression.LispNumber +import testutil.LispTestInstance +import testutil.SymbolAndFunctionCleaner +import testutil.TestUtilities.assertSExpressionsMatch +import testutil.TestUtilities.evaluateString +import testutil.TypeAssertions.assertNil +import testutil.TypeAssertions.assertT + +@LispTestInstance +class OrTest : SymbolAndFunctionCleaner() { + + @Test + fun orByItself() { + val input = "(or)" + + assertNil(evaluateString(input)) + } + + @Test + fun orWithNil() { + val input = "(or nil)" + + assertNil(evaluateString(input)) + } + + @Test + fun orWithT() { + val input = "(or t)" + + assertT(evaluateString(input)) + } + + @Test + fun orWithNumber() { + val input = "(or 7)" + + assertSExpressionsMatch(LispNumber("7"), evaluateString(input)) + } + + @Test + fun orWithSeveralValues() { + val input = "(or nil nil nil t nil)" + + assertT(evaluateString(input)) + } + + @Test + fun orWithSeveralNumbers() { + val input = "(or 1 2 3)" + + assertSExpressionsMatch(LispNumber("1"), evaluateString(input)) + } + + @Test + fun orShortCircuits() { + val input = "(or t (setq x 22))" + + assertT(evaluateString(input)) + + assertThrows(UndefinedSymbolException::class.java) { + evaluateString("x") + } + } +} diff --git a/src/test/kotlin/function/builtin/special/SetqTest.java b/src/test/kotlin/function/builtin/special/SetqTest.java deleted file mode 100644 index 1e5a152..0000000 --- a/src/test/kotlin/function/builtin/special/SetqTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package function.builtin.special; - -import function.ArgumentValidator.BadArgumentTypeException; -import function.ArgumentValidator.TooFewArgumentsException; -import function.ArgumentValidator.TooManyArgumentsException; -import function.builtin.Eval.UndefinedSymbolException; -import org.junit.Test; -import sexpression.LispNumber; -import table.SymbolTable; -import testutil.SymbolAndFunctionCleaner; - -import static org.junit.Assert.assertNull; -import static testutil.TestUtilities.assertSExpressionsMatch; -import static testutil.TestUtilities.evaluateString; - -public class SetqTest extends SymbolAndFunctionCleaner { - - @Test - public void setq() { - evaluateString("(setq a 23)"); - assertSExpressionsMatch(new LispNumber("23"), evaluateString("a")); - } - - @Test - public void lookupDefinedSymbol() { - evaluateString("(setq a 23)"); - assertSExpressionsMatch(new LispNumber("23"), getExecutionContext().lookupSymbolValue("A")); - } - - @Test - public void lookupUndefinedSymbol() { - assertNull(getExecutionContext().lookupSymbolValue("A")); - } - - @Test - public void setqGlobalVariable() { - evaluateString("(setq a 23)"); - SymbolTable global = getExecutionContext().getScope(); - getExecutionContext().setScope(new SymbolTable(global)); - - evaluateString("(setq a 94)"); - getExecutionContext().setScope(global); - assertSExpressionsMatch(new LispNumber("94"), evaluateString("a")); - } - - @Test - public void setqLocalVariable() { - SymbolTable global = getExecutionContext().getScope(); - SymbolTable local = new SymbolTable(global); - local.set("A", new LispNumber("99")); - getExecutionContext().setScope(local); - - evaluateString("(setq a 94)"); - assertSExpressionsMatch(new LispNumber("94"), evaluateString("a")); - } - - @Test(expected = UndefinedSymbolException.class) - public void setqLocalVariableDefined_DoesNotSetGlobal() { - SymbolTable global = getExecutionContext().getScope(); - SymbolTable local = new SymbolTable(global); - local.set("A", new LispNumber("99")); - getExecutionContext().setScope(local); - - evaluateString("(setq a 94)"); - getExecutionContext().setScope(global); - evaluateString("a"); - } - - @Test - public void setqLocalVariableUndefined_SetsGlobal() { - SymbolTable global = getExecutionContext().getScope(); - SymbolTable local = new SymbolTable(global); - getExecutionContext().setScope(local); - - evaluateString("(setq a 94)"); - getExecutionContext().setScope(global); - assertSExpressionsMatch(new LispNumber("94"), evaluateString("a")); - } - - @Test(expected = BadArgumentTypeException.class) - public void setqWithNonSymbol() { - evaluateString("(setq 1 2)"); - } - - @Test(expected = TooFewArgumentsException.class) - public void setqWithTooFewArguments() { - evaluateString("(setq x)"); - } - - @Test(expected = TooManyArgumentsException.class) - public void setqWithTooManyArguments() { - evaluateString("(setq a b c)"); - } -} diff --git a/src/test/kotlin/function/builtin/special/SetqTest.kt b/src/test/kotlin/function/builtin/special/SetqTest.kt new file mode 100644 index 0000000..d590efc --- /dev/null +++ b/src/test/kotlin/function/builtin/special/SetqTest.kt @@ -0,0 +1,105 @@ +package function.builtin.special + +import function.ArgumentValidator.BadArgumentTypeException +import function.ArgumentValidator.TooFewArgumentsException +import function.ArgumentValidator.TooManyArgumentsException +import function.builtin.Eval.UndefinedSymbolException +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Test +import sexpression.LispNumber +import table.SymbolTable +import testutil.LispTestInstance +import testutil.SymbolAndFunctionCleaner +import testutil.TestUtilities.assertSExpressionsMatch +import testutil.TestUtilities.evaluateString + +@LispTestInstance +class SetqTest : SymbolAndFunctionCleaner() { + + @Test + fun setq() { + evaluateString("(setq a 23)") + assertSExpressionsMatch(LispNumber("23"), evaluateString("a")) + } + + @Test + fun lookupDefinedSymbol() { + evaluateString("(setq a 23)") + assertSExpressionsMatch(LispNumber("23"), executionContext.lookupSymbolValue("A")!!) + } + + @Test + fun lookupUndefinedSymbol() { + assertThat(executionContext.lookupSymbolValue("A")).isNull() + } + + @Test + fun setqGlobalVariable() { + evaluateString("(setq a 23)") + val global = executionContext.scope + executionContext.scope = SymbolTable(global) + + evaluateString("(setq a 94)") + executionContext.scope = global + assertSExpressionsMatch(LispNumber("94"), evaluateString("a")) + } + + @Test + fun setqLocalVariable() { + val global = executionContext.scope + val local = SymbolTable(global) + local["A"] = LispNumber("99") + executionContext.scope = local + + evaluateString("(setq a 94)") + assertSExpressionsMatch(LispNumber("94"), evaluateString("a")) + } + + @Test + fun setqLocalVariableDefined_DoesNotSetGlobal() { + val global = executionContext.scope + val local = SymbolTable(global) + local["A"] = LispNumber("99") + executionContext.scope = local + + evaluateString("(setq a 94)") + executionContext.scope = global + + assertThrows(UndefinedSymbolException::class.java) { + evaluateString("a") + } + } + + @Test + fun setqLocalVariableUndefined_SetsGlobal() { + val global = executionContext.scope + val local = SymbolTable(global) + executionContext.scope = local + + evaluateString("(setq a 94)") + executionContext.scope = global + assertSExpressionsMatch(LispNumber("94"), evaluateString("a")) + } + + @Test + fun setqWithNonSymbol() { + assertThrows(BadArgumentTypeException::class.java) { + evaluateString("(setq 1 2)") + } + } + + @Test + fun setqWithTooFewArguments() { + assertThrows(TooFewArgumentsException::class.java) { + evaluateString("(setq x)") + } + } + + @Test + fun setqWithTooManyArguments() { + assertThrows(TooManyArgumentsException::class.java) { + evaluateString("(setq a b c)") + } + } +}