2016-12-19 13:05:53 -05:00
|
|
|
package function;
|
2016-12-19 11:38:14 -05:00
|
|
|
|
|
|
|
import java.text.MessageFormat;
|
|
|
|
|
|
|
|
import error.LispException;
|
2016-12-19 13:05:53 -05:00
|
|
|
import function.builtin.LENGTH;
|
2016-12-19 11:38:14 -05:00
|
|
|
import sexpression.*;
|
|
|
|
|
|
|
|
public class ArgumentValidator {
|
|
|
|
|
2016-12-22 16:55:25 -05:00
|
|
|
private Class<? extends SExpression> firstArgumentType;
|
|
|
|
private Class<? extends SExpression> trailingArgumentType;
|
2016-12-19 11:38:14 -05:00
|
|
|
private String functionName;
|
|
|
|
private Integer maximumNumberOfArguments;
|
|
|
|
private Integer minimumNumberOfArguments;
|
|
|
|
|
|
|
|
public ArgumentValidator(String functionName) {
|
2016-12-22 16:55:25 -05:00
|
|
|
this.firstArgumentType = SExpression.class;
|
|
|
|
this.trailingArgumentType = SExpression.class;
|
2016-12-19 11:38:14 -05:00
|
|
|
this.functionName = functionName;
|
|
|
|
this.minimumNumberOfArguments = null;
|
|
|
|
this.maximumNumberOfArguments = null;
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:55:25 -05:00
|
|
|
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;
|
2016-12-19 11:38:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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 validate(Cons argumentList) {
|
2016-12-22 16:55:25 -05:00
|
|
|
validateListNotDotted(argumentList);
|
|
|
|
|
2016-12-19 11:38:14 -05:00
|
|
|
if (containsTooFewArguments(argumentList))
|
|
|
|
throw new TooFewArgumentsException(functionName, argumentList);
|
|
|
|
else if (containsTooManyArguments(argumentList))
|
|
|
|
throw new TooManyArgumentsException(functionName, argumentList);
|
2016-12-22 16:55:25 -05:00
|
|
|
|
|
|
|
validateArgumentTypes(argumentList);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void validateListNotDotted(Cons argumentList) {
|
|
|
|
Cons currentCons = argumentList;
|
|
|
|
SExpression nextCons = argumentList.getCdr();
|
|
|
|
|
|
|
|
while (!nextCons.nullp()) {
|
|
|
|
if (!nextCons.consp())
|
|
|
|
throw new DottedArgumentListException(functionName, argumentList);
|
|
|
|
|
|
|
|
currentCons = (Cons) nextCons;
|
|
|
|
nextCons = currentCons.getCdr();
|
|
|
|
}
|
2016-12-19 11:38:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
private boolean containsTooFewArguments(Cons argumentList) {
|
|
|
|
return (minimumNumberOfArguments != null) && (LENGTH.getLength(argumentList) < minimumNumberOfArguments);
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean containsTooManyArguments(Cons argumentList) {
|
|
|
|
return (maximumNumberOfArguments != null) && (LENGTH.getLength(argumentList) > maximumNumberOfArguments);
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:55:25 -05:00
|
|
|
private void validateArgumentTypes(Cons argumentList) {
|
|
|
|
if (!isExpectedFirstArgumentType(argumentList.getCar()))
|
|
|
|
throw new BadArgumentTypeException(functionName, argumentList.getCar(), firstArgumentType);
|
|
|
|
|
|
|
|
validateRemainingArguments(argumentList);
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isExpectedFirstArgumentType(SExpression firstArgument) {
|
|
|
|
return firstArgumentType.isInstance(firstArgument);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void validateRemainingArguments(Cons cons) {
|
|
|
|
for (cons = (Cons) cons.getCdr(); !cons.nullp(); cons = (Cons) cons.getCdr())
|
|
|
|
if (!isExpectedRemainingArgumentType(cons.getCar()))
|
|
|
|
throw new BadArgumentTypeException(functionName, cons.getCar(), trailingArgumentType);
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isExpectedRemainingArgumentType(SExpression remainingArgument) {
|
|
|
|
return trailingArgumentType.isInstance(remainingArgument);
|
2016-12-19 11:38:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:55:25 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-19 11:38:14 -05:00
|
|
|
public static class BadArgumentTypeException extends LispException {
|
|
|
|
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
private String functionName;
|
|
|
|
private String argument;
|
2016-12-22 16:55:25 -05:00
|
|
|
private Class<? extends SExpression> expected;
|
2016-12-19 11:38:14 -05:00
|
|
|
|
2016-12-22 16:55:25 -05:00
|
|
|
public BadArgumentTypeException(String functionName, SExpression argument,
|
|
|
|
Class<? extends SExpression> expected) {
|
2016-12-19 11:38:14 -05:00
|
|
|
this.functionName = functionName;
|
|
|
|
this.argument = argument.toString();
|
2016-12-22 16:55:25 -05:00
|
|
|
this.expected = expected;
|
2016-12-19 11:38:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getMessage() {
|
2016-12-22 16:55:25 -05:00
|
|
|
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);
|
2016-12-19 11:38:14 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|