Improved the argument list validation for COND
This commit is contained in:
parent
eb9f224c60
commit
37bc303fe8
|
@ -13,6 +13,7 @@ public class ArgumentValidator {
|
||||||
private String functionName;
|
private String functionName;
|
||||||
private Integer maximumNumberOfArguments;
|
private Integer maximumNumberOfArguments;
|
||||||
private Integer minimumNumberOfArguments;
|
private Integer minimumNumberOfArguments;
|
||||||
|
private boolean isNilAcceptable;
|
||||||
|
|
||||||
public ArgumentValidator(String functionName) {
|
public ArgumentValidator(String functionName) {
|
||||||
this.firstArgumentType = SExpression.class;
|
this.firstArgumentType = SExpression.class;
|
||||||
|
@ -20,6 +21,7 @@ public class ArgumentValidator {
|
||||||
this.functionName = functionName;
|
this.functionName = functionName;
|
||||||
this.minimumNumberOfArguments = null;
|
this.minimumNumberOfArguments = null;
|
||||||
this.maximumNumberOfArguments = null;
|
this.maximumNumberOfArguments = null;
|
||||||
|
this.isNilAcceptable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFirstArgumentExpectedType(Class<? extends SExpression> argumentType) {
|
public void setFirstArgumentExpectedType(Class<? extends SExpression> argumentType) {
|
||||||
|
@ -48,6 +50,10 @@ public class ArgumentValidator {
|
||||||
this.maximumNumberOfArguments = exactNumberOfArguments;
|
this.maximumNumberOfArguments = exactNumberOfArguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void doNotAcceptNil() {
|
||||||
|
this.isNilAcceptable = false;
|
||||||
|
}
|
||||||
|
|
||||||
public void validate(Cons argumentList) {
|
public void validate(Cons argumentList) {
|
||||||
validateListNotDotted(argumentList);
|
validateListNotDotted(argumentList);
|
||||||
validateListLength(argumentList);
|
validateListLength(argumentList);
|
||||||
|
@ -80,29 +86,35 @@ public class ArgumentValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateArgumentTypes(Cons argumentList) {
|
private void validateArgumentTypes(Cons argumentList) {
|
||||||
if (!isExpectedFirstArgumentType(argumentList.getCar()))
|
validateFirstArgument(argumentList);
|
||||||
throw new BadArgumentTypeException(functionName, argumentList.getCar(), firstArgumentType);
|
validateTrailingArguments(argumentList);
|
||||||
|
}
|
||||||
|
|
||||||
validateRemainingArguments(argumentList);
|
private void validateFirstArgument(Cons argumentList) {
|
||||||
|
if (!isFirstArgumentValid(argumentList))
|
||||||
|
throw new BadArgumentTypeException(functionName, argumentList.getCar(), firstArgumentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFirstArgumentValid(Cons argumentList) {
|
||||||
|
return argumentList.nullp() || isExpectedFirstArgumentType(argumentList.getCar());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isExpectedFirstArgumentType(SExpression firstArgument) {
|
private boolean isExpectedFirstArgumentType(SExpression firstArgument) {
|
||||||
return firstArgumentType.isInstance(firstArgument);
|
return firstArgumentType.isInstance(firstArgument) && !isDisallowedNil(firstArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateRemainingArguments(Cons cons) {
|
private boolean isDisallowedNil(SExpression argument) {
|
||||||
for (cons = (Cons) cons.getCdr(); !cons.nullp(); cons = (Cons) cons.getCdr())
|
return !isNilAcceptable && argument.nullp();
|
||||||
if (!isExpectedRemainingArgumentType(cons.getCar()))
|
}
|
||||||
|
|
||||||
|
private void validateTrailingArguments(Cons argumentList) {
|
||||||
|
for (Cons cons = (Cons) argumentList.getCdr(); !cons.nullp(); cons = (Cons) cons.getCdr())
|
||||||
|
if (!isExpectedTrailingArgumentType(cons.getCar()))
|
||||||
throw new BadArgumentTypeException(functionName, cons.getCar(), trailingArgumentType);
|
throw new BadArgumentTypeException(functionName, cons.getCar(), trailingArgumentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isExpectedRemainingArgumentType(SExpression remainingArgument) {
|
private boolean isExpectedTrailingArgumentType(SExpression trailingArgument) {
|
||||||
return trailingArgumentType.isInstance(remainingArgument);
|
return trailingArgumentType.isInstance(trailingArgument) && !isDisallowedNil(trailingArgument);
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
|
|
|
@ -10,6 +10,7 @@ public class COND extends LispFunction {
|
||||||
public COND() {
|
public COND() {
|
||||||
this.argumentValidator = new ArgumentValidator("COND");
|
this.argumentValidator = new ArgumentValidator("COND");
|
||||||
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
|
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
|
||||||
|
this.argumentValidator.doNotAcceptNil();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SExpression call(Cons argumentList) {
|
public SExpression call(Cons argumentList) {
|
||||||
|
@ -22,7 +23,7 @@ public class COND extends LispFunction {
|
||||||
if (argumentList.nullp())
|
if (argumentList.nullp())
|
||||||
return Nil.getUniqueInstance();
|
return Nil.getUniqueInstance();
|
||||||
|
|
||||||
Cons clause = getFirstClauseAndValidateNotNil(argumentList);
|
Cons clause = (Cons) argumentList.getCar();
|
||||||
Cons remainingClauses = (Cons) argumentList.getCdr();
|
Cons remainingClauses = (Cons) argumentList.getCdr();
|
||||||
SExpression test = EVAL.eval(clause.getCar());
|
SExpression test = EVAL.eval(clause.getCar());
|
||||||
|
|
||||||
|
@ -32,13 +33,6 @@ public class COND extends LispFunction {
|
||||||
return callTailRecursive(remainingClauses);
|
return callTailRecursive(remainingClauses);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Cons getFirstClauseAndValidateNotNil(Cons argumentList) {
|
|
||||||
Cons firstClause = (Cons) argumentList.getCar();
|
|
||||||
argumentValidator.validateListIsNotNil(firstClause);
|
|
||||||
|
|
||||||
return firstClause;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isTestSuccessful(SExpression test) {
|
private boolean isTestSuccessful(SExpression test) {
|
||||||
return test != Nil.getUniqueInstance();
|
return test != Nil.getUniqueInstance();
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,13 +200,21 @@ public class ArgumentValidatorTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
@Test(expected = BadArgumentTypeException.class)
|
||||||
public void validateListNotNil_ThrowsExceptionWithNilValue() {
|
public void doNotAcceptNil_ThrowsExceptionOnNilArgument() {
|
||||||
validator.validateListIsNotNil(Nil.getUniqueInstance());
|
validator.doNotAcceptNil();
|
||||||
|
validator.validate(new Cons(Symbol.T, new Cons(Nil.getUniqueInstance(), Nil.getUniqueInstance())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void validateListNotNil_DoesNotThrowExceptionWithNonNilValue() {
|
public void doNotAcceptNil_AllowsEmptyArgumentList() {
|
||||||
validator.validateListIsNotNil(new Cons(Nil.getUniqueInstance(), Nil.getUniqueInstance()));
|
validator.doNotAcceptNil();
|
||||||
|
validator.validate(Nil.getUniqueInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doNotAcceptNil_AllowsProperList() {
|
||||||
|
validator.doNotAcceptNil();
|
||||||
|
validator.validate(new Cons(Symbol.T, new Cons(Symbol.T, Nil.getUniqueInstance())));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,4 +58,9 @@ public class APPLYTester {
|
||||||
evaluateString("(apply '1)");
|
evaluateString("(apply '1)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = DottedArgumentListException.class)
|
||||||
|
public void testCondWithDottedArgumentList_ThrowsException() {
|
||||||
|
evaluateString("(apply 'apply (cons 'T 'T))");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import static testutil.TestUtilities.*;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import function.ArgumentValidator.BadArgumentTypeException;
|
import function.ArgumentValidator.*;
|
||||||
|
|
||||||
public class CONDTester {
|
public class CONDTester {
|
||||||
|
|
||||||
|
@ -74,4 +74,9 @@ public class CONDTester {
|
||||||
evaluateString("(cond o)");
|
evaluateString("(cond o)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = DottedArgumentListException.class)
|
||||||
|
public void testCondWithDottedArgumentList_ThrowsException() {
|
||||||
|
evaluateString("(apply 'cond (cons '(nil T) 'b))");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue