235 lines
8.5 KiB
Java
235 lines
8.5 KiB
Java
package function;
|
|
|
|
import error.LispException;
|
|
import sexpression.Cons;
|
|
import sexpression.DisplayName;
|
|
import sexpression.SExpression;
|
|
|
|
import java.math.BigInteger;
|
|
|
|
import static function.builtin.cons.LENGTH.getLength;
|
|
import static java.text.MessageFormat.format;
|
|
|
|
public class ArgumentValidator {
|
|
|
|
private Class<? extends SExpression> firstArgumentType;
|
|
private Class<? extends SExpression> trailingArgumentType;
|
|
private Class<? extends SExpression> excludedFirstArgumentType;
|
|
private Class<? extends SExpression> excludedTrailingArgumentType;
|
|
private String functionName;
|
|
private BigInteger maximumNumberOfArguments;
|
|
private BigInteger minimumNumberOfArguments;
|
|
|
|
public ArgumentValidator(String functionName) {
|
|
this.firstArgumentType = SExpression.class;
|
|
this.trailingArgumentType = SExpression.class;
|
|
this.excludedFirstArgumentType = null;
|
|
this.excludedTrailingArgumentType = null;
|
|
this.functionName = functionName;
|
|
this.minimumNumberOfArguments = null;
|
|
this.maximumNumberOfArguments = null;
|
|
}
|
|
|
|
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 setFirstArgumentExcludedType(Class<? extends SExpression> argumentType) {
|
|
this.excludedFirstArgumentType = argumentType;
|
|
}
|
|
|
|
public void setTrailingArgumentExcludedType(Class<? extends SExpression> argumentType) {
|
|
this.excludedTrailingArgumentType = argumentType;
|
|
}
|
|
|
|
public void setEveryArgumentExcludedType(Class<? extends SExpression> argumentType) {
|
|
this.excludedFirstArgumentType = argumentType;
|
|
this.excludedTrailingArgumentType = argumentType;
|
|
}
|
|
|
|
public void setMaximumNumberOfArguments(int maximumNumberOfArguments) {
|
|
this.maximumNumberOfArguments = BigInteger.valueOf(maximumNumberOfArguments);
|
|
}
|
|
|
|
public void setMinimumNumberOfArguments(int minimumNumberOfArguments) {
|
|
this.minimumNumberOfArguments = BigInteger.valueOf(minimumNumberOfArguments);
|
|
}
|
|
|
|
public void setExactNumberOfArguments(int exactNumberOfArguments) {
|
|
this.minimumNumberOfArguments = BigInteger.valueOf(exactNumberOfArguments);
|
|
this.maximumNumberOfArguments = BigInteger.valueOf(exactNumberOfArguments);
|
|
}
|
|
|
|
public void validate(Cons argumentList) {
|
|
validateListNotDotted(argumentList);
|
|
validateListLength(argumentList);
|
|
validateArgumentTypes(argumentList);
|
|
}
|
|
|
|
private void validateListNotDotted(Cons argumentList) {
|
|
SExpression next = argumentList.getRest();
|
|
|
|
for (Cons current = argumentList; next.isCons(); next = current.getRest())
|
|
current = (Cons) next;
|
|
|
|
if (!next.isNull())
|
|
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 isMinimum() && isLengthLessThanMinimum(argumentList);
|
|
}
|
|
|
|
private boolean isMinimum() {
|
|
return minimumNumberOfArguments != null;
|
|
}
|
|
|
|
private boolean isLengthLessThanMinimum(Cons argumentList) {
|
|
return getLength(argumentList).compareTo(minimumNumberOfArguments) < 0;
|
|
}
|
|
|
|
private boolean containsTooManyArguments(Cons argumentList) {
|
|
return isMaximum() && isLengthGreaterThanMaximum(argumentList);
|
|
}
|
|
|
|
private boolean isMaximum() {
|
|
return maximumNumberOfArguments != null;
|
|
}
|
|
|
|
private boolean isLengthGreaterThanMaximum(Cons argumentList) {
|
|
return getLength(argumentList).compareTo(maximumNumberOfArguments) > 0;
|
|
}
|
|
|
|
private void validateArgumentTypes(Cons argumentList) {
|
|
validateFirstArgument(argumentList);
|
|
validateTrailingArguments(argumentList);
|
|
}
|
|
|
|
private void validateFirstArgument(Cons argumentList) {
|
|
if (!isFirstArgumentValid(argumentList))
|
|
throw new BadArgumentTypeException(functionName, argumentList.getFirst(), firstArgumentType);
|
|
}
|
|
|
|
private boolean isFirstArgumentValid(Cons argumentList) {
|
|
return argumentList.isNull() || isExpectedFirstArgumentType(argumentList.getFirst());
|
|
}
|
|
|
|
private boolean isExpectedFirstArgumentType(SExpression firstArgument) {
|
|
return firstArgumentType.isInstance(firstArgument) && !isExcludedFirstArgumentType(firstArgument);
|
|
}
|
|
|
|
private boolean isExcludedFirstArgumentType(SExpression firstArgument) {
|
|
return excludedFirstArgumentType != null && excludedFirstArgumentType.isInstance(firstArgument);
|
|
}
|
|
|
|
private void validateTrailingArguments(Cons argumentList) {
|
|
for (Cons cons = (Cons) argumentList.getRest(); !cons.isNull(); cons = (Cons) cons.getRest())
|
|
if (!isExpectedTrailingArgumentType(cons.getFirst()))
|
|
throw new BadArgumentTypeException(functionName, cons.getFirst(), trailingArgumentType);
|
|
}
|
|
|
|
private boolean isExpectedTrailingArgumentType(SExpression trailingArgument) {
|
|
return trailingArgumentType.isInstance(trailingArgument) && !isExcludedTrailingArgumentType(trailingArgument);
|
|
}
|
|
|
|
private boolean isExcludedTrailingArgumentType(SExpression trailingArgument) {
|
|
return excludedTrailingArgumentType != null && excludedTrailingArgumentType.isInstance(trailingArgument);
|
|
}
|
|
|
|
public static class TooFewArgumentsException extends LispException {
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
private String functionName;
|
|
private Cons argumentList;
|
|
|
|
public TooFewArgumentsException(String functionName, Cons argumentList) {
|
|
this.functionName = functionName;
|
|
this.argumentList = argumentList;
|
|
}
|
|
|
|
@Override
|
|
public String getMessage() {
|
|
return format("too few arguments given to {0}: {1}", functionName, argumentList);
|
|
}
|
|
}
|
|
|
|
public static class TooManyArgumentsException extends LispException {
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
private String functionName;
|
|
private Cons argumentList;
|
|
|
|
public TooManyArgumentsException(String functionName, Cons argumentList) {
|
|
this.functionName = functionName;
|
|
this.argumentList = argumentList;
|
|
}
|
|
|
|
@Override
|
|
public String getMessage() {
|
|
return format("too many arguments given to {0}: {1}", functionName, argumentList);
|
|
}
|
|
}
|
|
|
|
public static class DottedArgumentListException extends LispException {
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
private String functionName;
|
|
private Cons argumentList;
|
|
|
|
public DottedArgumentListException(String functionName, Cons argumentList) {
|
|
this.functionName = functionName;
|
|
this.argumentList = argumentList;
|
|
}
|
|
|
|
@Override
|
|
public String getMessage() {
|
|
return format("dotted argument list given to {0}: {1}", functionName, argumentList);
|
|
}
|
|
}
|
|
|
|
public static class BadArgumentTypeException extends LispException {
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
private String functionName;
|
|
private String argument;
|
|
private Class<? extends SExpression> expectedType;
|
|
|
|
public BadArgumentTypeException(String functionName, SExpression argument,
|
|
Class<? extends SExpression> expectedType) {
|
|
this.functionName = functionName;
|
|
this.argument = argument.toString();
|
|
this.expectedType = expectedType;
|
|
}
|
|
|
|
@Override
|
|
public String getMessage() {
|
|
return format("{0}: {1} is not the expected type of ''{2}''",
|
|
functionName,
|
|
argument,
|
|
getExpectedTypeName());
|
|
}
|
|
|
|
private String getExpectedTypeName() {
|
|
DisplayName displayName = expectedType.getAnnotation(DisplayName.class);
|
|
|
|
return (displayName == null) ? "unknown" : displayName.value();
|
|
}
|
|
}
|
|
}
|