Refactored the COND built-in function
This commit is contained in:
parent
a0583f808c
commit
eb9f224c60
|
@ -50,26 +50,25 @@ public class ArgumentValidator {
|
||||||
|
|
||||||
public void validate(Cons argumentList) {
|
public void validate(Cons argumentList) {
|
||||||
validateListNotDotted(argumentList);
|
validateListNotDotted(argumentList);
|
||||||
|
validateListLength(argumentList);
|
||||||
if (containsTooFewArguments(argumentList))
|
|
||||||
throw new TooFewArgumentsException(functionName, argumentList);
|
|
||||||
else if (containsTooManyArguments(argumentList))
|
|
||||||
throw new TooManyArgumentsException(functionName, argumentList);
|
|
||||||
|
|
||||||
validateArgumentTypes(argumentList);
|
validateArgumentTypes(argumentList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateListNotDotted(Cons argumentList) {
|
private void validateListNotDotted(Cons argumentList) {
|
||||||
Cons currentCons = argumentList;
|
SExpression next = argumentList.getCdr();
|
||||||
SExpression nextCons = argumentList.getCdr();
|
|
||||||
|
|
||||||
while (!nextCons.nullp()) {
|
for (Cons current = argumentList; next.consp(); next = current.getCdr())
|
||||||
if (!nextCons.consp())
|
current = (Cons) next;
|
||||||
throw new DottedArgumentListException(functionName, argumentList);
|
|
||||||
|
|
||||||
currentCons = (Cons) nextCons;
|
if (!next.nullp())
|
||||||
nextCons = currentCons.getCdr();
|
throw new DottedArgumentListException(functionName, argumentList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateListLength(Cons argumentList) {
|
||||||
|
if (containsTooFewArguments(argumentList))
|
||||||
|
throw new TooFewArgumentsException(functionName, argumentList);
|
||||||
|
else if (containsTooManyArguments(argumentList))
|
||||||
|
throw new TooManyArgumentsException(functionName, argumentList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean containsTooFewArguments(Cons argumentList) {
|
private boolean containsTooFewArguments(Cons argumentList) {
|
||||||
|
@ -101,6 +100,11 @@ public class ArgumentValidator {
|
||||||
return trailingArgumentType.isInstance(remainingArgument);
|
return trailingArgumentType.isInstance(remainingArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void validateListIsNotNil(Cons list) {
|
||||||
|
if (list.nullp())
|
||||||
|
throw new BadArgumentTypeException(functionName, Nil.getUniqueInstance(), Cons.class);
|
||||||
|
}
|
||||||
|
|
||||||
public static class TooFewArgumentsException extends LispException {
|
public static class TooFewArgumentsException extends LispException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
|
@ -1,53 +1,63 @@
|
||||||
package function.builtin;
|
package function.builtin;
|
||||||
|
|
||||||
import function.LispFunction;
|
import function.*;
|
||||||
import sexpression.*;
|
import sexpression.*;
|
||||||
|
|
||||||
public class COND extends LispFunction {
|
public class COND extends LispFunction {
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
private ArgumentValidator argumentValidator;
|
||||||
if (argList.nullp()) {
|
|
||||||
// return NIL if there are were no arguments passed to COND
|
public COND() {
|
||||||
|
this.argumentValidator = new ArgumentValidator("COND");
|
||||||
|
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SExpression call(Cons argumentList) {
|
||||||
|
argumentValidator.validate(argumentList);
|
||||||
|
|
||||||
|
return callTailRecursive(argumentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SExpression callTailRecursive(Cons argumentList) {
|
||||||
|
if (argumentList.nullp())
|
||||||
return Nil.getUniqueInstance();
|
return Nil.getUniqueInstance();
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argCar = argList.getCar(); // first clause
|
Cons clause = getFirstClauseAndValidateNotNil(argumentList);
|
||||||
Cons argCdr = (Cons) argList.getCdr(); // list of remaining clauses
|
Cons remainingClauses = (Cons) argumentList.getCdr();
|
||||||
|
SExpression test = EVAL.eval(clause.getCar());
|
||||||
|
|
||||||
// make sure the first clause is a list and is not NIL
|
if (isTestSuccessful(test))
|
||||||
if (argCar.consp()) {
|
return evaluateResult(clause, test);
|
||||||
Cons clause = (Cons) argCar;
|
|
||||||
SExpression test = EVAL.eval(clause.getCar());
|
|
||||||
|
|
||||||
if (test != Nil.getUniqueInstance()) {
|
return callTailRecursive(remainingClauses);
|
||||||
// the car of this clause is true, so we evaluate its cdr
|
}
|
||||||
|
|
||||||
SExpression cdr = clause.getCdr();
|
private Cons getFirstClauseAndValidateNotNil(Cons argumentList) {
|
||||||
SExpression retval = test;
|
Cons firstClause = (Cons) argumentList.getCar();
|
||||||
|
argumentValidator.validateListIsNotNil(firstClause);
|
||||||
|
|
||||||
// evaluate all the S-expressions in the cdr of the clause
|
return firstClause;
|
||||||
while (cdr.consp()) {
|
}
|
||||||
retval = EVAL.eval(((Cons) cdr).getCar());
|
|
||||||
cdr = ((Cons) cdr).getCdr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the value of the last S-expression evaluated
|
private boolean isTestSuccessful(SExpression test) {
|
||||||
return retval;
|
return test != Nil.getUniqueInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
// the car of this clause is false, so we test any remaining
|
private SExpression evaluateResult(Cons clause, SExpression test) {
|
||||||
// clauses
|
SExpression lastResultValue = test;
|
||||||
|
|
||||||
// check if the list of remaining clauses is a list and is not NIL
|
for (SExpression result = clause.getCdr(); result.consp(); result = advanceCons(result))
|
||||||
if (argCdr.consp()) {
|
lastResultValue = EVAL.eval(getCar(result));
|
||||||
return call(argCdr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// there are no remaining clauses, so we return NIL
|
return lastResultValue;
|
||||||
return Nil.getUniqueInstance();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("COND: clause " + argCar + " should be a list");
|
private SExpression advanceCons(SExpression knownCons) {
|
||||||
|
return ((Cons) knownCons).getCdr();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SExpression getCar(SExpression knownCons) {
|
||||||
|
return ((Cons) knownCons).getCar();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean evaluateArguments() {
|
public boolean evaluateArguments() {
|
||||||
|
|
|
@ -165,7 +165,8 @@ public class ArgumentValidatorTester {
|
||||||
try {
|
try {
|
||||||
validator.validate(argumentList);
|
validator.validate(argumentList);
|
||||||
} catch (BadArgumentTypeException e) {
|
} catch (BadArgumentTypeException e) {
|
||||||
e.getMessage();
|
assertNotNull(e.getMessage());
|
||||||
|
assertTrue(e.getMessage().length() > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,6 +184,13 @@ public class ArgumentValidatorTester {
|
||||||
validator.validate(argumentList);
|
validator.validate(argumentList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void DottedArgumentListException_HasCorrectSeverity() {
|
||||||
|
DottedArgumentListException e = new DottedArgumentListException("TEST", Nil.getUniqueInstance());
|
||||||
|
|
||||||
|
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dottedArgumentListException_HasMessageText() {
|
public void dottedArgumentListException_HasMessageText() {
|
||||||
DottedArgumentListException e = new DottedArgumentListException("TEST", Nil.getUniqueInstance());
|
DottedArgumentListException e = new DottedArgumentListException("TEST", Nil.getUniqueInstance());
|
||||||
|
@ -191,4 +199,14 @@ public class ArgumentValidatorTester {
|
||||||
assertTrue(e.getMessage().length() > 0);
|
assertTrue(e.getMessage().length() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = BadArgumentTypeException.class)
|
||||||
|
public void validateListNotNil_ThrowsExceptionWithNilValue() {
|
||||||
|
validator.validateListIsNotNil(Nil.getUniqueInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validateListNotNil_DoesNotThrowExceptionWithNonNilValue() {
|
||||||
|
validator.validateListIsNotNil(new Cons(Nil.getUniqueInstance(), Nil.getUniqueInstance()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
package function.builtin;
|
||||||
|
|
||||||
|
import static testutil.TestUtilities.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import function.ArgumentValidator.BadArgumentTypeException;
|
||||||
|
|
||||||
|
public class CONDTester {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCondWithNoArguments() {
|
||||||
|
String input = "(cond)";
|
||||||
|
|
||||||
|
assertSExpressionsMatch(evaluateString(input), parseString("nil"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCondWithTrue() {
|
||||||
|
String input = "(cond (T))";
|
||||||
|
|
||||||
|
assertSExpressionsMatch(evaluateString(input), parseString("T"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCondWithSingleExpression() {
|
||||||
|
String input = "(cond (T \"true\"))";
|
||||||
|
|
||||||
|
assertSExpressionsMatch(evaluateString(input), parseString("\"true\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCondWithMultipleExpressions() {
|
||||||
|
String input = "(cond ((= 1 2) 2) ((= 1 2) 2) ((= 1 1) 3))";
|
||||||
|
|
||||||
|
assertSExpressionsMatch(evaluateString(input), parseString("3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCondWithMultipleConditionsMatching_ReturnFirstOne() {
|
||||||
|
String input = "(cond ((= 1 1) 2) ((= 1 1) 3))";
|
||||||
|
|
||||||
|
assertSExpressionsMatch(evaluateString(input), parseString("2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCondWithMultipleConditionsMatching_OnlyEvaluatesFirstOne() {
|
||||||
|
String input = "(cond ((= 1 1) 2) ((= 1 1) x))";
|
||||||
|
|
||||||
|
assertSExpressionsMatch(evaluateString(input), parseString("2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCondWithMultipleResultValues_OnlyReturnsLast() {
|
||||||
|
String input = "(cond ((= 1 1) 2 3 4))";
|
||||||
|
|
||||||
|
assertSExpressionsMatch(evaluateString(input), parseString("4"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCondWithNoConditionMatching_ReturnsNil() {
|
||||||
|
String input = "(cond ((= 1 2) T) ((= 1 3) T))";
|
||||||
|
|
||||||
|
assertSExpressionsMatch(evaluateString(input), parseString("nil"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = BadArgumentTypeException.class)
|
||||||
|
public void testCondWithNilArgument_ThrowsException() {
|
||||||
|
evaluateString("(cond ())");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = BadArgumentTypeException.class)
|
||||||
|
public void testCondWithNonListArgument_ThrowsException() {
|
||||||
|
evaluateString("(cond o)");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue