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) {
|
||||
validateListNotDotted(argumentList);
|
||||
|
||||
if (containsTooFewArguments(argumentList))
|
||||
throw new TooFewArgumentsException(functionName, argumentList);
|
||||
else if (containsTooManyArguments(argumentList))
|
||||
throw new TooManyArgumentsException(functionName, argumentList);
|
||||
|
||||
validateListLength(argumentList);
|
||||
validateArgumentTypes(argumentList);
|
||||
}
|
||||
|
||||
private void validateListNotDotted(Cons argumentList) {
|
||||
Cons currentCons = argumentList;
|
||||
SExpression nextCons = argumentList.getCdr();
|
||||
SExpression next = argumentList.getCdr();
|
||||
|
||||
while (!nextCons.nullp()) {
|
||||
if (!nextCons.consp())
|
||||
for (Cons current = argumentList; next.consp(); next = current.getCdr())
|
||||
current = (Cons) next;
|
||||
|
||||
if (!next.nullp())
|
||||
throw new DottedArgumentListException(functionName, argumentList);
|
||||
|
||||
currentCons = (Cons) nextCons;
|
||||
nextCons = currentCons.getCdr();
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -101,6 +100,11 @@ public class ArgumentValidator {
|
|||
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 {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
|
|
@ -1,53 +1,63 @@
|
|||
package function.builtin;
|
||||
|
||||
import function.LispFunction;
|
||||
import function.*;
|
||||
import sexpression.*;
|
||||
|
||||
public class COND extends LispFunction {
|
||||
|
||||
public SExpression call(Cons argList) {
|
||||
if (argList.nullp()) {
|
||||
// return NIL if there are were no arguments passed to COND
|
||||
return Nil.getUniqueInstance();
|
||||
private ArgumentValidator argumentValidator;
|
||||
|
||||
public COND() {
|
||||
this.argumentValidator = new ArgumentValidator("COND");
|
||||
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
|
||||
}
|
||||
|
||||
SExpression argCar = argList.getCar(); // first clause
|
||||
Cons argCdr = (Cons) argList.getCdr(); // list of remaining clauses
|
||||
public SExpression call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
// make sure the first clause is a list and is not NIL
|
||||
if (argCar.consp()) {
|
||||
Cons clause = (Cons) argCar;
|
||||
return callTailRecursive(argumentList);
|
||||
}
|
||||
|
||||
private SExpression callTailRecursive(Cons argumentList) {
|
||||
if (argumentList.nullp())
|
||||
return Nil.getUniqueInstance();
|
||||
|
||||
Cons clause = getFirstClauseAndValidateNotNil(argumentList);
|
||||
Cons remainingClauses = (Cons) argumentList.getCdr();
|
||||
SExpression test = EVAL.eval(clause.getCar());
|
||||
|
||||
if (test != Nil.getUniqueInstance()) {
|
||||
// the car of this clause is true, so we evaluate its cdr
|
||||
if (isTestSuccessful(test))
|
||||
return evaluateResult(clause, test);
|
||||
|
||||
SExpression cdr = clause.getCdr();
|
||||
SExpression retval = test;
|
||||
|
||||
// evaluate all the S-expressions in the cdr of the clause
|
||||
while (cdr.consp()) {
|
||||
retval = EVAL.eval(((Cons) cdr).getCar());
|
||||
cdr = ((Cons) cdr).getCdr();
|
||||
return callTailRecursive(remainingClauses);
|
||||
}
|
||||
|
||||
// return the value of the last S-expression evaluated
|
||||
return retval;
|
||||
private Cons getFirstClauseAndValidateNotNil(Cons argumentList) {
|
||||
Cons firstClause = (Cons) argumentList.getCar();
|
||||
argumentValidator.validateListIsNotNil(firstClause);
|
||||
|
||||
return firstClause;
|
||||
}
|
||||
|
||||
// the car of this clause is false, so we test any remaining
|
||||
// clauses
|
||||
|
||||
// check if the list of remaining clauses is a list and is not NIL
|
||||
if (argCdr.consp()) {
|
||||
return call(argCdr);
|
||||
private boolean isTestSuccessful(SExpression test) {
|
||||
return test != Nil.getUniqueInstance();
|
||||
}
|
||||
|
||||
// there are no remaining clauses, so we return NIL
|
||||
return Nil.getUniqueInstance();
|
||||
private SExpression evaluateResult(Cons clause, SExpression test) {
|
||||
SExpression lastResultValue = test;
|
||||
|
||||
for (SExpression result = clause.getCdr(); result.consp(); result = advanceCons(result))
|
||||
lastResultValue = EVAL.eval(getCar(result));
|
||||
|
||||
return lastResultValue;
|
||||
}
|
||||
|
||||
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() {
|
||||
|
|
|
@ -165,7 +165,8 @@ public class ArgumentValidatorTester {
|
|||
try {
|
||||
validator.validate(argumentList);
|
||||
} catch (BadArgumentTypeException e) {
|
||||
e.getMessage();
|
||||
assertNotNull(e.getMessage());
|
||||
assertTrue(e.getMessage().length() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,6 +184,13 @@ public class ArgumentValidatorTester {
|
|||
validator.validate(argumentList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void DottedArgumentListException_HasCorrectSeverity() {
|
||||
DottedArgumentListException e = new DottedArgumentListException("TEST", Nil.getUniqueInstance());
|
||||
|
||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dottedArgumentListException_HasMessageText() {
|
||||
DottedArgumentListException e = new DottedArgumentListException("TEST", Nil.getUniqueInstance());
|
||||
|
@ -191,4 +199,14 @@ public class ArgumentValidatorTester {
|
|||
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