transcendental-lisp/src/main/kotlin/function/ArgumentValidator.kt
Mike Cifelli 347857fbb5 Restore main function
- Remove MessageFormat usages in kotlin where possible
 - Code cleanup
2018-10-20 10:09:53 -04:00

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'"
}
}