Files
smnp-kt/app/src/main/kotlin/io/smnp/callable/util/FunctionSignatureParser.kt

155 lines
5.8 KiB
Kotlin

package io.smnp.callable.util
import io.smnp.callable.signature.Signature
import io.smnp.dsl.ast.model.node.*
import io.smnp.error.InvalidSignatureException
import io.smnp.error.ShouldNeverReachThisLineException
import io.smnp.type.enumeration.DataType
import io.smnp.type.matcher.Matcher
import io.smnp.type.matcher.Matcher.Companion.anyType
import io.smnp.type.matcher.Matcher.Companion.listOfMatchers
import io.smnp.type.matcher.Matcher.Companion.mapOfMatchers
import io.smnp.type.matcher.Matcher.Companion.ofType
import io.smnp.type.matcher.Matcher.Companion.oneOf
import io.smnp.type.matcher.Matcher.Companion.optional
object FunctionSignatureParser {
private data class SignatureMetadata(val hasRegular: Boolean, val hasOptional: Boolean, val hasVararg: Boolean)
fun parseSignature(signature: FunctionDefinitionArgumentsNode): Signature {
val (_, _, hasVararg) = assertArgumentsPositions(signature)
assertArgumentsNames(signature)
val matchers = signature.items.map {
when (it) {
is RegularFunctionDefinitionArgumentNode -> matcherForUnionTypeNode(it.type as UnionTypeNode)
is OptionalFunctionDefinitionArgumentNode -> optional(matcherForUnionTypeNode(it.type as UnionTypeNode))
else -> throw ShouldNeverReachThisLineException()
}
}
return if (hasVararg) Signature.vararg(
matchers.last(),
*matchers.dropLast(1).toTypedArray()
) else Signature.simple(*matchers.toTypedArray())
}
private fun assertArgumentsNames(signature: FunctionDefinitionArgumentsNode) {
signature.items.groupingBy {
when (it) {
is RegularFunctionDefinitionArgumentNode -> (it.identifier as IdentifierNode).token.rawValue
is OptionalFunctionDefinitionArgumentNode -> (it.identifier as IdentifierNode).token.rawValue
else -> throw ShouldNeverReachThisLineException()
}
}.eachCount().forEach {
if (it.value > 1) {
throw InvalidSignatureException("Duplicated argument name of '${it.key}'")
}
}
}
private fun assertArgumentsPositions(signature: FunctionDefinitionArgumentsNode): SignatureMetadata {
val firstOptional = signature.items.indexOfFirst { it is OptionalFunctionDefinitionArgumentNode }
val lastRegular = signature.items.indexOfLast { it is RegularFunctionDefinitionArgumentNode }
val vararg =
signature.items.indexOfFirst { it is RegularFunctionDefinitionArgumentNode && it.vararg != Node.NONE }
val metadata = SignatureMetadata(
lastRegular != -1,
firstOptional != -1,
vararg != -1
)
if (metadata.hasVararg && metadata.hasOptional) {
throw InvalidSignatureException("Optional arguments and vararg cannot be mixed in same signature")
}
if (metadata.hasRegular && metadata.hasOptional && firstOptional < lastRegular) {
throw InvalidSignatureException("Optional arguments should be at the very end of arguments list")
}
if (metadata.hasVararg && vararg != signature.items.size - 1) {
throw InvalidSignatureException("Vararg arguments should be at the very end of arguments list")
}
return metadata
}
private fun matcherForUnionTypeNode(unionTypeNode: UnionTypeNode): Matcher {
if (unionTypeNode.items.isEmpty()) {
return anyType()
}
if (unionTypeNode.items.size == 1) {
return matcherForSingleTypeNode(
unionTypeNode.items[0] as SingleTypeNode
)
}
return oneOf(*unionTypeNode.items.map {
matcherForSingleTypeNode(
it as SingleTypeNode
)
}.toTypedArray())
}
fun matcherForSingleTypeNode(singleTypeNode: SingleTypeNode): Matcher {
// TODO
val type = DataType.valueOf((singleTypeNode.type as IdentifierNode).token.rawValue.toUpperCase())
if (singleTypeNode.specifiers == Node.NONE) {
return ofType(type)
} else if (type == DataType.LIST && singleTypeNode.specifiers.children.size == 1) {
return listSpecifier(singleTypeNode.specifiers.children[0] as TypeSpecifierNode)
} else if (type == DataType.MAP && singleTypeNode.specifiers.children.size == 2) {
return mapSpecifier(
singleTypeNode.specifiers.children[0] as TypeSpecifierNode,
singleTypeNode.specifiers.children[1] as TypeSpecifierNode
)
}
throw ShouldNeverReachThisLineException()
}
private fun listSpecifier(listSpecifierNode: TypeSpecifierNode): Matcher {
val types = mutableListOf<Matcher>()
if (listSpecifierNode.items.isEmpty()) {
types.add(anyType())
}
listSpecifierNode.items.forEach { types.add(
matcherForSingleTypeNode(
it as SingleTypeNode
)
) }
return listOfMatchers(*types.toTypedArray())
}
private fun mapSpecifier(keySpecifierNode: TypeSpecifierNode, valueSpecifierNode: TypeSpecifierNode): Matcher {
val keys = mutableListOf<Matcher>()
val values = mutableListOf<Matcher>()
if (keySpecifierNode.items.isEmpty()) {
keys.add(anyType())
}
if (valueSpecifierNode.items.isEmpty()) {
values.add(anyType())
}
keySpecifierNode.items.forEach { keys.add(
matcherForSingleTypeNode(
it as SingleTypeNode
)
) }
valueSpecifierNode.items.forEach { values.add(
matcherForSingleTypeNode(
it as SingleTypeNode
)
) }
return mapOfMatchers(keys, values)
}
}