Convert Cond to kotlin

This commit is contained in:
Mike Cifelli 2018-10-28 13:10:39 -04:00
parent 5d1606571a
commit c6d25275bb
5 changed files with 156 additions and 164 deletions

View File

@ -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();
}
}

View File

@ -57,5 +57,5 @@ class Case(name: String) : LispSpecialFunction() {
}
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) }
}

View File

@ -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) }
}

View File

@ -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))");
}
}

View File

@ -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))
}
}