Convert Cond to kotlin
This commit is contained in:
parent
5d1606571a
commit
c6d25275bb
|
@ -1,67 +0,0 @@
|
||||||
package function.builtin.special;
|
|
||||||
|
|
||||||
import function.ArgumentValidator;
|
|
||||||
import function.FunctionNames;
|
|
||||||
import function.LispSpecialFunction;
|
|
||||||
import recursion.TailCall;
|
|
||||||
import sexpression.Cons;
|
|
||||||
import sexpression.Nil;
|
|
||||||
import sexpression.SExpression;
|
|
||||||
|
|
||||||
import static function.builtin.Eval.eval;
|
|
||||||
import static recursion.TailCalls.done;
|
|
||||||
import static recursion.TailCalls.tailCall;
|
|
||||||
|
|
||||||
@FunctionNames({ "COND" })
|
|
||||||
public class COND extends LispSpecialFunction {
|
|
||||||
|
|
||||||
private ArgumentValidator argumentValidator;
|
|
||||||
|
|
||||||
public COND(String name) {
|
|
||||||
this.argumentValidator = new ArgumentValidator(name);
|
|
||||||
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
|
|
||||||
this.argumentValidator.setEveryArgumentExcludedType(Nil.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SExpression call(Cons argumentList) {
|
|
||||||
argumentValidator.validate(argumentList);
|
|
||||||
|
|
||||||
return callTailRecursive(argumentList).invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TailCall<SExpression> callTailRecursive(Cons argumentList) {
|
|
||||||
if (argumentList.isNull())
|
|
||||||
return done(Nil.INSTANCE);
|
|
||||||
|
|
||||||
Cons clause = (Cons) argumentList.getFirst();
|
|
||||||
Cons remainingClauses = (Cons) argumentList.getRest();
|
|
||||||
SExpression test = eval(clause.getFirst());
|
|
||||||
|
|
||||||
if (isTestSuccessful(test))
|
|
||||||
return done(evaluateConsequents(clause.getRest(), test));
|
|
||||||
|
|
||||||
return tailCall(() -> callTailRecursive(remainingClauses));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isTestSuccessful(SExpression test) {
|
|
||||||
return test != Nil.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SExpression evaluateConsequents(SExpression consequentList, SExpression test) {
|
|
||||||
SExpression lastConsequentValue = test;
|
|
||||||
|
|
||||||
for (; consequentList.isCons(); consequentList = advanceCons(consequentList))
|
|
||||||
lastConsequentValue = eval(getFirst(consequentList));
|
|
||||||
|
|
||||||
return lastConsequentValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SExpression advanceCons(SExpression knownCons) {
|
|
||||||
return ((Cons) knownCons).getRest();
|
|
||||||
}
|
|
||||||
|
|
||||||
private SExpression getFirst(SExpression knownCons) {
|
|
||||||
return ((Cons) knownCons).getFirst();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -57,5 +57,5 @@ class Case(name: String) : LispSpecialFunction() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun evaluateConsequents(clause: Cons) =
|
private fun evaluateConsequents(clause: Cons) =
|
||||||
clause.drop(1).fold(Nil as SExpression) { _, it -> eval(it.first) }
|
clause.drop(1).fold(Nil as SExpression) { _, cons -> eval(cons.first) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
@FunctionNames("COND")
|
||||||
|
class Cond(name: String) : LispSpecialFunction() {
|
||||||
|
|
||||||
|
private val argumentValidator = ArgumentValidator(name).apply {
|
||||||
|
setEveryArgumentExpectedType(Cons::class.java)
|
||||||
|
setEveryArgumentExcludedType(Nil::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun call(argumentList: Cons): SExpression {
|
||||||
|
argumentValidator.validate(argumentList)
|
||||||
|
|
||||||
|
return callTailRecursive(argumentList)
|
||||||
|
}
|
||||||
|
|
||||||
|
private tailrec fun callTailRecursive(argumentList: Cons): SExpression {
|
||||||
|
if (argumentList.isNull)
|
||||||
|
return Nil
|
||||||
|
|
||||||
|
val clause = argumentList.first as Cons
|
||||||
|
val remainingClauses = argumentList.rest as Cons
|
||||||
|
val test = eval(clause.first)
|
||||||
|
|
||||||
|
return if (!test.isNull)
|
||||||
|
evaluateConsequents(clause, test)
|
||||||
|
else
|
||||||
|
callTailRecursive(remainingClauses)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateConsequents(clause: Cons, test: SExpression) =
|
||||||
|
clause.drop(1).fold(test) { _, cons -> eval(cons.first) }
|
||||||
|
}
|
|
@ -1,96 +0,0 @@
|
||||||
package function.builtin.special;
|
|
||||||
|
|
||||||
import function.ArgumentValidator.BadArgumentTypeException;
|
|
||||||
import function.ArgumentValidator.DottedArgumentListException;
|
|
||||||
import org.junit.Test;
|
|
||||||
import testutil.SymbolAndFunctionCleaner;
|
|
||||||
|
|
||||||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
|
||||||
import static testutil.TestUtilities.evaluateString;
|
|
||||||
import static testutil.TestUtilities.parseString;
|
|
||||||
|
|
||||||
public class CONDTest extends SymbolAndFunctionCleaner {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void condWithNoArguments() {
|
|
||||||
String input = "(cond)";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("nil"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void condWithTrue() {
|
|
||||||
String input = "(cond (T))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("T"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void condWithNumber() {
|
|
||||||
String input = "(cond ((+ 1 2)))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("3"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void condWithSingleClause() {
|
|
||||||
String input = "(cond (T \"true\"))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("\"true\""), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void condWithMultipleClauses() {
|
|
||||||
String input = "(cond ((= 1 2) 2) ((= 1 2) 2) ((= 1 1) 3))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("3"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void condWithMultipleTrueTests_ReturnsFirstOne() {
|
|
||||||
String input = "(cond ((= 1 1) 2) ((= 1 1) 3))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("2"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void condWithMultipleTrueTests_OnlyEvaluatesFirstOne() {
|
|
||||||
String input = "(cond ((= 1 1) 2) ((= 1 1) x))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("2"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void condWithMultipleValuesInConsequent_OnlyReturnsLast() {
|
|
||||||
String input = "(cond ((= 1 1) 2 3 4))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("4"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void condWithNoTrueTest_ReturnsNil() {
|
|
||||||
String input = "(cond ((= 1 2) T) ((= 1 3) T))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("nil"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
|
||||||
public void condWithEmptyListArgument_ThrowsException() {
|
|
||||||
evaluateString("(cond ())");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
|
||||||
public void condWithNilArgument_ThrowsException() {
|
|
||||||
evaluateString("(cond nil)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
|
||||||
public void condWithNonListArgument_ThrowsException() {
|
|
||||||
evaluateString("(cond o)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = DottedArgumentListException.class)
|
|
||||||
public void condWithDottedArgumentList_ThrowsException() {
|
|
||||||
evaluateString("(apply 'cond (cons '(nil T) 'b))");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
package function.builtin.special
|
||||||
|
|
||||||
|
import function.ArgumentValidator.BadArgumentTypeException
|
||||||
|
import function.ArgumentValidator.DottedArgumentListException
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import testutil.LispTestInstance
|
||||||
|
import testutil.SymbolAndFunctionCleaner
|
||||||
|
|
||||||
|
import testutil.TestUtilities.assertSExpressionsMatch
|
||||||
|
import testutil.TestUtilities.evaluateString
|
||||||
|
import testutil.TestUtilities.parseString
|
||||||
|
|
||||||
|
@LispTestInstance
|
||||||
|
class CondTest : SymbolAndFunctionCleaner() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithNoArguments() {
|
||||||
|
val input = "(cond)"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("nil"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithTrue() {
|
||||||
|
val input = "(cond (T))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("T"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithNumber() {
|
||||||
|
val input = "(cond ((+ 1 2)))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("3"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithSingleClause() {
|
||||||
|
val input = "(cond (T \"true\"))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("\"true\""), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithMultipleClauses() {
|
||||||
|
val input = "(cond ((= 1 2) 2) ((= 1 2) 2) ((= 1 1) 3))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("3"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithMultipleTrueTests_ReturnsFirstOne() {
|
||||||
|
val input = "(cond ((= 1 1) 2) ((= 1 1) 3))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("2"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithMultipleTrueTests_OnlyEvaluatesFirstOne() {
|
||||||
|
val input = "(cond ((= 1 1) 2) ((= 1 1) x))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("2"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithMultipleValuesInConsequent_OnlyReturnsLast() {
|
||||||
|
val input = "(cond ((= 1 1) 2 3 4))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("4"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithNoTrueTest_ReturnsNil() {
|
||||||
|
val input = "(cond ((= 1 2) T) ((= 1 3) T))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("nil"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithEmptyListArgument_ThrowsException() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(cond ())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithNilArgument_ThrowsException() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(cond nil)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithNonListArgument_ThrowsException() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(cond o)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithDottedArgumentList_ThrowsException() {
|
||||||
|
assertThrows(DottedArgumentListException::class.java) {
|
||||||
|
evaluateString("(apply 'cond (cons '(nil T) 'b))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condWithDottedClause() {
|
||||||
|
val input = "(eval `(cond ,(cons T 'pear)))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("T"), evaluateString(input))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue