diff --git a/pom.xml b/pom.xml index bd817f6..685e2d2 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ UTF-8 - 1.2.60 + 1.2.70 5.2.0 false diff --git a/src/main/kotlin/function/ArgumentValidator.java b/src/main/kotlin/function/ArgumentValidator.java deleted file mode 100644 index dc0db05..0000000 --- a/src/main/kotlin/function/ArgumentValidator.java +++ /dev/null @@ -1,234 +0,0 @@ -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 firstArgumentType; - private Class trailingArgumentType; - private Class excludedFirstArgumentType; - private Class 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 argumentType) { - this.firstArgumentType = argumentType; - } - - public void setTrailingArgumentExpectedType(Class argumentType) { - this.trailingArgumentType = argumentType; - } - - public void setEveryArgumentExpectedType(Class argumentType) { - this.firstArgumentType = argumentType; - this.trailingArgumentType = argumentType; - } - - public void setFirstArgumentExcludedType(Class argumentType) { - this.excludedFirstArgumentType = argumentType; - } - - public void setTrailingArgumentExcludedType(Class argumentType) { - this.excludedTrailingArgumentType = argumentType; - } - - public void setEveryArgumentExcludedType(Class 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 expectedType; - - public BadArgumentTypeException(String functionName, SExpression argument, - Class 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(); - } - } -} diff --git a/src/main/kotlin/function/ArgumentValidator.kt b/src/main/kotlin/function/ArgumentValidator.kt new file mode 100644 index 0000000..c22dcce --- /dev/null +++ b/src/main/kotlin/function/ArgumentValidator.kt @@ -0,0 +1,164 @@ +package function + +import error.LispException +import function.builtin.cons.LENGTH.getLength +import sexpression.Cons +import sexpression.DisplayName +import sexpression.SExpression +import java.math.BigInteger +import java.text.MessageFormat.format + +class ArgumentValidator(private val functionName: String) { + + private var firstArgumentType: Class = SExpression::class.java + private var trailingArgumentType: Class = SExpression::class.java + private var excludedFirstArgumentType: Class? = null + private var excludedTrailingArgumentType: Class? = null + private var maximumNumberOfArguments: BigInteger? = null + private var minimumNumberOfArguments: BigInteger? = null + + fun setFirstArgumentExpectedType(argumentType: Class) { + this.firstArgumentType = argumentType + } + + fun setTrailingArgumentExpectedType(argumentType: Class) { + this.trailingArgumentType = argumentType + } + + fun setEveryArgumentExpectedType(argumentType: Class) { + this.firstArgumentType = argumentType + this.trailingArgumentType = argumentType + } + + fun setFirstArgumentExcludedType(argumentType: Class) { + this.excludedFirstArgumentType = argumentType + } + + fun setTrailingArgumentExcludedType(argumentType: Class) { + this.excludedTrailingArgumentType = argumentType + } + + fun setEveryArgumentExcludedType(argumentType: Class) { + this.excludedFirstArgumentType = argumentType + this.excludedTrailingArgumentType = argumentType + } + + fun setMaximumNumberOfArguments(maximumNumberOfArguments: Int) { + this.maximumNumberOfArguments = BigInteger.valueOf(maximumNumberOfArguments.toLong()) + } + + fun setMinimumNumberOfArguments(minimumNumberOfArguments: Int) { + this.minimumNumberOfArguments = BigInteger.valueOf(minimumNumberOfArguments.toLong()) + } + + fun setExactNumberOfArguments(exactNumberOfArguments: Int) { + this.minimumNumberOfArguments = BigInteger.valueOf(exactNumberOfArguments.toLong()) + this.maximumNumberOfArguments = BigInteger.valueOf(exactNumberOfArguments.toLong()) + } + + fun validate(argumentList: Cons) { + validateListNotDotted(argumentList) + validateListLength(argumentList) + validateArgumentTypes(argumentList) + } + + private fun validateListNotDotted(argumentList: Cons) { + var current = argumentList + var next = current.rest + + while (next.isCons) { + current = next as Cons + next = current.rest + } + + if (!next.isNull) + throw DottedArgumentListException(functionName, argumentList) + } + + private fun validateListLength(argumentList: Cons) { + if (containsTooFewArguments(argumentList)) + throw TooFewArgumentsException(functionName, argumentList) + else if (containsTooManyArguments(argumentList)) + throw TooManyArgumentsException(functionName, argumentList) + } + + private fun containsTooFewArguments(argumentList: Cons) = isMinimum() && isLengthLessThanMinimum(argumentList) + private fun isMinimum() = minimumNumberOfArguments != null + private fun isLengthLessThanMinimum(argumentList: Cons) = getLength(argumentList) < minimumNumberOfArguments!! + + private fun containsTooManyArguments(argumentList: Cons) = isMaximum() && isLengthGreaterThanMaximum(argumentList) + private fun isMaximum() = maximumNumberOfArguments != null + private fun isLengthGreaterThanMaximum(argumentList: Cons) = getLength(argumentList) > maximumNumberOfArguments!! + + private fun validateArgumentTypes(argumentList: Cons) { + validateFirstArgument(argumentList) + validateTrailingArguments(argumentList) + } + + private fun validateFirstArgument(argumentList: Cons) { + if (!isFirstArgumentValid(argumentList)) + throw BadArgumentTypeException(functionName, argumentList.first, firstArgumentType) + } + + private fun isFirstArgumentValid(argumentList: Cons) = + argumentList.isNull || isExpectedFirstArgumentType(argumentList.first) + + private fun isExpectedFirstArgumentType(firstArgument: SExpression) = + firstArgumentType.isInstance(firstArgument) && !isExcludedFirstArgumentType(firstArgument) + + private fun isExcludedFirstArgumentType(firstArgument: SExpression) = + excludedFirstArgumentType != null && excludedFirstArgumentType!!.isInstance(firstArgument) + + private fun validateTrailingArguments(argumentList: Cons) { + var cons = argumentList.rest as Cons + + while (!cons.isNull) { + if (!isExpectedTrailingArgumentType(cons.first)) + throw BadArgumentTypeException(functionName, cons.first, trailingArgumentType) + + cons = cons.rest as Cons + } + } + + private fun isExpectedTrailingArgumentType(trailingArgument: SExpression) = + trailingArgumentType.isInstance(trailingArgument) && !isExcludedTrailingArgumentType(trailingArgument) + + private fun isExcludedTrailingArgumentType(trailingArgument: SExpression) = + excludedTrailingArgumentType != null && excludedTrailingArgumentType!!.isInstance(trailingArgument) + + class TooFewArgumentsException(functionName: String, argumentList: Cons) : LispException() { + + override val message: String by lazy { + format("too few arguments given to {0}: {1}", functionName, argumentList) + } + } + + class TooManyArgumentsException(functionName: String, argumentList: Cons) : LispException() { + + override val message: String by lazy { + format("too many arguments given to {0}: {1}", functionName, argumentList) + } + } + + class DottedArgumentListException(functionName: String, argumentList: Cons) : LispException() { + + override val message: String by lazy { + format("dotted argument list given to {0}: {1}", functionName, argumentList) + } + } + + class BadArgumentTypeException(functionName: String, + argument: SExpression, + expectedType: Class?) : LispException() { + + private val expectedTypeName: String by lazy { + val displayName = expectedType?.getAnnotation(DisplayName::class.java) + + displayName?.value ?: "unknown" + } + + override val message: String by lazy { + format("{0}: {1} is not the expected type of ''{2}''", functionName, argument.toString(), expectedTypeName) + } + } +}