144 lines
6.0 KiB
Kotlin
144 lines
6.0 KiB
Kotlin
package function
|
|
|
|
import error.LispException
|
|
import function.builtin.cons.LENGTH.getLength
|
|
import sexpression.Cons
|
|
import sexpression.DisplayName
|
|
import sexpression.SExpression
|
|
import java.math.BigInteger
|
|
|
|
class ArgumentValidator(private val functionName: String) {
|
|
|
|
private var firstArgumentType: Class<out SExpression> = SExpression::class.java
|
|
private var trailingArgumentType: Class<out SExpression> = SExpression::class.java
|
|
private var excludedFirstArgumentType: Class<out SExpression>? = null
|
|
private var excludedTrailingArgumentType: Class<out SExpression>? = null
|
|
private var maximumNumberOfArguments: BigInteger? = null
|
|
private var minimumNumberOfArguments: BigInteger? = null
|
|
|
|
fun setFirstArgumentExpectedType(argumentType: Class<out SExpression>) {
|
|
this.firstArgumentType = argumentType
|
|
}
|
|
|
|
fun setTrailingArgumentExpectedType(argumentType: Class<out SExpression>) {
|
|
this.trailingArgumentType = argumentType
|
|
}
|
|
|
|
fun setEveryArgumentExpectedType(argumentType: Class<out SExpression>) {
|
|
this.firstArgumentType = argumentType
|
|
this.trailingArgumentType = argumentType
|
|
}
|
|
|
|
fun setFirstArgumentExcludedType(argumentType: Class<out SExpression>) {
|
|
this.excludedFirstArgumentType = argumentType
|
|
}
|
|
|
|
fun setTrailingArgumentExcludedType(argumentType: Class<out SExpression>) {
|
|
this.excludedTrailingArgumentType = argumentType
|
|
}
|
|
|
|
fun setEveryArgumentExcludedType(argumentType: Class<out SExpression>) {
|
|
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) {
|
|
if (isArgumentListDotted(argumentList))
|
|
throw DottedArgumentListException(functionName, argumentList)
|
|
}
|
|
|
|
private fun isArgumentListDotted(argumentList: Cons) = argumentList.isCons && !argumentList.last().rest.isNull
|
|
|
|
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) {
|
|
val cons = argumentList.rest as Cons
|
|
|
|
cons.forEach {
|
|
if (!isExpectedTrailingArgumentType(it.first))
|
|
throw BadArgumentTypeException(functionName, it.first, trailingArgumentType)
|
|
}
|
|
}
|
|
|
|
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 = "too few arguments given to $functionName: $argumentList"
|
|
}
|
|
|
|
class TooManyArgumentsException(functionName: String, argumentList: Cons) : LispException() {
|
|
|
|
override val message = "too many arguments given to $functionName: $argumentList"
|
|
}
|
|
|
|
class DottedArgumentListException(functionName: String, argumentList: Cons) : LispException() {
|
|
|
|
override val message = "dotted argument list given to $functionName: $argumentList"
|
|
}
|
|
|
|
class BadArgumentTypeException(functionName: String,
|
|
argument: SExpression,
|
|
expectedType: Class<out SExpression>) : LispException() {
|
|
|
|
private val expectedTypeName = expectedType.getAnnotation(DisplayName::class.java)?.value ?: "unknown"
|
|
|
|
override val message = "$functionName: $argument is not the expected type of '$expectedTypeName'"
|
|
}
|
|
}
|