transcendental-lisp/src/function/ArgumentValidator.java

196 lines
7.2 KiB
Java

package function;
import java.text.MessageFormat;
import error.LispException;
import function.builtin.LENGTH;
import sexpression.*;
public class ArgumentValidator {
private Class<? extends SExpression> firstArgumentType;
private Class<? extends SExpression> trailingArgumentType;
private String functionName;
private Integer maximumNumberOfArguments;
private Integer minimumNumberOfArguments;
private boolean isNilAcceptable;
public ArgumentValidator(String functionName) {
this.firstArgumentType = SExpression.class;
this.trailingArgumentType = SExpression.class;
this.functionName = functionName;
this.minimumNumberOfArguments = null;
this.maximumNumberOfArguments = null;
this.isNilAcceptable = true;
}
public void setFirstArgumentExpectedType(Class<? extends SExpression> argumentType) {
this.firstArgumentType = argumentType;
}
public void setTrailingArgumentExpectedType(Class<? extends SExpression> argumentType) {
this.trailingArgumentType = argumentType;
}
public void setEveryArgumentExpectedType(Class<? extends SExpression> argumentType) {
this.firstArgumentType = argumentType;
this.trailingArgumentType = argumentType;
}
public void setMaximumNumberOfArguments(int maximumNumberOfArguments) {
this.maximumNumberOfArguments = maximumNumberOfArguments;
}
public void setMinimumNumberOfArguments(int minimumNumberOfArguments) {
this.minimumNumberOfArguments = minimumNumberOfArguments;
}
public void setExactNumberOfArguments(int exactNumberOfArguments) {
this.minimumNumberOfArguments = exactNumberOfArguments;
this.maximumNumberOfArguments = exactNumberOfArguments;
}
public void doNotAcceptNil() {
this.isNilAcceptable = false;
}
public void validate(Cons argumentList) {
validateListNotDotted(argumentList);
validateListLength(argumentList);
validateArgumentTypes(argumentList);
}
private void validateListNotDotted(Cons argumentList) {
SExpression next = argumentList.getCdr();
for (Cons current = argumentList; next.consp(); next = current.getCdr())
current = (Cons) next;
if (!next.nullp())
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) {
return (minimumNumberOfArguments != null) && (LENGTH.getLength(argumentList) < minimumNumberOfArguments);
}
private boolean containsTooManyArguments(Cons argumentList) {
return (maximumNumberOfArguments != null) && (LENGTH.getLength(argumentList) > maximumNumberOfArguments);
}
private void validateArgumentTypes(Cons argumentList) {
validateFirstArgument(argumentList);
validateTrailingArguments(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) {
return firstArgumentType.isInstance(firstArgument) && !isDisallowedNil(firstArgument);
}
private boolean isDisallowedNil(SExpression argument) {
return !isNilAcceptable && argument.nullp();
}
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);
}
private boolean isExpectedTrailingArgumentType(SExpression trailingArgument) {
return trailingArgumentType.isInstance(trailingArgument) && !isDisallowedNil(trailingArgument);
}
public static class TooFewArgumentsException extends LispException {
private static final long serialVersionUID = 1L;
private String functionName;
private Cons originalSExpression;
public TooFewArgumentsException(String functionName, Cons argumentList) {
this.functionName = functionName;
this.originalSExpression = new Cons(new Symbol(this.functionName), argumentList);
}
@Override
public String getMessage() {
return MessageFormat.format("too few arguments given to {0}: {1}", functionName, originalSExpression);
}
}
public static class TooManyArgumentsException extends LispException {
private static final long serialVersionUID = 1L;
private String functionName;
private Cons originalSExpression;
public TooManyArgumentsException(String functionName, Cons argumentList) {
this.functionName = functionName;
this.originalSExpression = new Cons(new Symbol(this.functionName), argumentList);
}
@Override
public String getMessage() {
return MessageFormat.format("too many arguments given to {0}: {1}", functionName, originalSExpression);
}
}
public static class DottedArgumentListException extends LispException {
private static final long serialVersionUID = 1L;
private String functionName;
private Cons originalSExpression;
public DottedArgumentListException(String functionName, Cons argumentList) {
this.functionName = functionName;
this.originalSExpression = new Cons(new Symbol(this.functionName), argumentList);
}
@Override
public String getMessage() {
return MessageFormat.format("dotted argument list given to {0}: {1}", functionName, originalSExpression);
}
}
public static class BadArgumentTypeException extends LispException {
private static final long serialVersionUID = 1L;
private String functionName;
private String argument;
private Class<? extends SExpression> expected;
public BadArgumentTypeException(String functionName, SExpression argument,
Class<? extends SExpression> expected) {
this.functionName = functionName;
this.argument = argument.toString();
this.expected = expected;
}
@Override
public String getMessage() {
DisplayName displayName = expected.getAnnotation(DisplayName.class);
String expectedType = (displayName == null) ? "unknown" : displayName.value();
return MessageFormat.format("{0}: {1} is not the expected type of ''{2}''", functionName, argument,
expectedType);
}
}
}