Create FunctionSignatureParser
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package io.smnp.callable.function
|
||||
|
||||
import io.smnp.callable.signature.Signature
|
||||
import io.smnp.dsl.ast.model.node.*
|
||||
import io.smnp.error.ShouldNeverReachThisLineException
|
||||
import io.smnp.type.enumeration.DataType
|
||||
import io.smnp.type.matcher.Matcher
|
||||
import io.smnp.type.matcher.Matcher.Companion.allTypes
|
||||
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 RuntimeException("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 RuntimeException("Optional arguments and vararg cannot be mixed in same signature")
|
||||
}
|
||||
|
||||
if (metadata.hasRegular && metadata.hasOptional && firstOptional < lastRegular) {
|
||||
throw RuntimeException("Optional arguments should be at the very end of arguments list")
|
||||
}
|
||||
|
||||
if (metadata.hasVararg && vararg != signature.items.size - 1) {
|
||||
throw RuntimeException("Vararg arguments should be at the very end of arguments list")
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
||||
private fun matcherForUnionTypeNode(unionTypeNode: UnionTypeNode): Matcher {
|
||||
if (unionTypeNode.items.isEmpty()) {
|
||||
return allTypes()
|
||||
}
|
||||
|
||||
if (unionTypeNode.items.size == 1) {
|
||||
return matcherForSingleTypeNode(unionTypeNode.items[0] as SingleTypeNode)
|
||||
}
|
||||
|
||||
return oneOf(*unionTypeNode.items.map { matcherForSingleTypeNode(it as SingleTypeNode) }.toTypedArray())
|
||||
}
|
||||
|
||||
private 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 RuntimeException("Unknown type")
|
||||
}
|
||||
|
||||
private fun listSpecifier(listSpecifierNode: TypeSpecifierNode): Matcher {
|
||||
val types = mutableListOf<Matcher>()
|
||||
|
||||
if (listSpecifierNode.items.isEmpty()) {
|
||||
types.add(allTypes())
|
||||
}
|
||||
|
||||
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(allTypes())
|
||||
}
|
||||
|
||||
if (valueSpecifierNode.items.isEmpty()) {
|
||||
values.add(allTypes())
|
||||
}
|
||||
|
||||
keySpecifierNode.items.forEach { keys.add(matcherForSingleTypeNode(it as SingleTypeNode)) }
|
||||
valueSpecifierNode.items.forEach { values.add(matcherForSingleTypeNode(it as SingleTypeNode)) }
|
||||
|
||||
return mapOfMatchers(keys, values)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.smnp.dsl.ast.model.node
|
||||
|
||||
class TypeNode(type: Node, specifiers: Node) : Node(2, type.position) {
|
||||
class SingleTypeNode(type: Node, specifiers: Node) : Node(2, type.position) {
|
||||
val type: Node
|
||||
get() = children[0]
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package io.smnp.dsl.ast.model.node
|
||||
|
||||
import io.smnp.dsl.token.model.entity.TokenPosition
|
||||
|
||||
class UnionTypeNode(items: List<Node>, position: TokenPosition) : AbstractIterableNode(items, position)
|
||||
@@ -11,7 +11,7 @@ class ExtendParser : Parser() {
|
||||
override fun tryToParse(input: TokenList): ParserOutput {
|
||||
val simpleExtendParser = allOf(
|
||||
terminal(TokenType.EXTEND),
|
||||
assert(TypeParser(), "type to be extended"),
|
||||
assert(SingleTypeParser(), "type to be extended"),
|
||||
assert(terminal(TokenType.AS), "'as' keyword with identifier"),
|
||||
assert(SimpleIdentifierParser(), "identifier"),
|
||||
terminal(TokenType.WITH),
|
||||
@@ -22,7 +22,7 @@ class ExtendParser : Parser() {
|
||||
|
||||
val complexExtendParser = allOf(
|
||||
terminal(TokenType.EXTEND),
|
||||
assert(TypeParser(), "type to be extended"),
|
||||
assert(SingleTypeParser(), "type to be extended"),
|
||||
assert(terminal(TokenType.AS), "'as' keyword with identifier"),
|
||||
assert(SimpleIdentifierParser(), "identifier"),
|
||||
assert(loop(terminal(TokenType.OPEN_CURLY), assert(FunctionDefinitionParser(), "method definition or }"), terminal(TokenType.CLOSE_CURLY)) {
|
||||
|
||||
@@ -2,7 +2,9 @@ package io.smnp.dsl.ast.parser
|
||||
|
||||
import io.smnp.dsl.ast.model.entity.ParserOutput
|
||||
import io.smnp.dsl.ast.model.node.OptionalFunctionDefinitionArgumentNode
|
||||
import io.smnp.dsl.ast.model.node.UnionTypeNode
|
||||
import io.smnp.dsl.token.model.entity.TokenList
|
||||
import io.smnp.dsl.token.model.entity.TokenPosition
|
||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||
|
||||
class OptionalFunctionDefinitionArgumentParser : Parser() {
|
||||
@@ -12,7 +14,7 @@ class OptionalFunctionDefinitionArgumentParser : Parser() {
|
||||
optional(allOf(
|
||||
terminal(TokenType.COLON),
|
||||
TypeParser()
|
||||
) { it[1] }),
|
||||
) { it[1] }) { UnionTypeNode(emptyList(), TokenPosition.NONE) },
|
||||
terminal(TokenType.ASSIGN),
|
||||
assert(ExpressionParser(), "expression")
|
||||
) {
|
||||
|
||||
@@ -187,11 +187,11 @@ abstract class Parser {
|
||||
}
|
||||
|
||||
// optional -> a?
|
||||
fun optional(parser: Parser): Parser {
|
||||
fun optional(parser: Parser, createFallbackNode: () -> Node = { Node.NONE }): Parser {
|
||||
return object : Parser() {
|
||||
override fun tryToParse(input: TokenList): ParserOutput {
|
||||
val output = parser.parse(input)
|
||||
return if (output.result == ParsingResult.OK) output else ParserOutput.ok(Node.NONE)
|
||||
return if (output.result == ParsingResult.OK) output else ParserOutput.ok(createFallbackNode())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package io.smnp.dsl.ast.parser
|
||||
|
||||
import io.smnp.dsl.ast.model.entity.ParserOutput
|
||||
import io.smnp.dsl.ast.model.node.RegularFunctionDefinitionArgumentNode
|
||||
import io.smnp.dsl.ast.model.node.UnionTypeNode
|
||||
import io.smnp.dsl.token.model.entity.TokenList
|
||||
import io.smnp.dsl.token.model.entity.TokenPosition
|
||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||
|
||||
class RegularFunctionDefinitionArgumentParser : Parser() {
|
||||
@@ -13,9 +15,9 @@ class RegularFunctionDefinitionArgumentParser : Parser() {
|
||||
optional(allOf(
|
||||
terminal(TokenType.COLON),
|
||||
TypeParser()
|
||||
) { it[1] })
|
||||
) {
|
||||
RegularFunctionDefinitionArgumentNode(it[1], it[2], it[0])
|
||||
) { it[1] }) { UnionTypeNode(emptyList(), TokenPosition.NONE) }
|
||||
) { (vararg, identifier, type) ->
|
||||
RegularFunctionDefinitionArgumentNode(identifier, type, vararg)
|
||||
}.parse(input)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.smnp.dsl.ast.parser
|
||||
|
||||
import io.smnp.dsl.ast.model.entity.ParserOutput
|
||||
import io.smnp.dsl.ast.model.node.SingleTypeNode
|
||||
import io.smnp.dsl.ast.model.node.TypeSpecifiersNode
|
||||
import io.smnp.dsl.token.model.entity.TokenList
|
||||
|
||||
class SingleTypeParser : Parser() {
|
||||
override fun tryToParse(input: TokenList): ParserOutput {
|
||||
return allOf(
|
||||
SimpleIdentifierParser(),
|
||||
optional(repeat(TypeSpecifierParser()) { list, tokenPosition -> TypeSpecifiersNode(list, tokenPosition) })
|
||||
) {
|
||||
SingleTypeNode(it[0], it[1])
|
||||
}.parse(input)
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
package io.smnp.dsl.ast.parser
|
||||
|
||||
import io.smnp.dsl.ast.model.entity.ParserOutput
|
||||
import io.smnp.dsl.ast.model.node.TypeNode
|
||||
import io.smnp.dsl.ast.model.node.TypeSpecifiersNode
|
||||
import io.smnp.dsl.ast.model.node.SingleTypeNode
|
||||
import io.smnp.dsl.ast.model.node.UnionTypeNode
|
||||
import io.smnp.dsl.token.model.entity.TokenList
|
||||
|
||||
class TypeParser : Parser() {
|
||||
override fun tryToParse(input: TokenList): ParserOutput {
|
||||
return allOf(
|
||||
SimpleIdentifierParser(),
|
||||
optional(repeat(TypeSpecifierParser()) { list, tokenPosition -> TypeSpecifiersNode(list, tokenPosition) })
|
||||
) {
|
||||
TypeNode(it[0], it[1])
|
||||
return mapNode(oneOf(
|
||||
SingleTypeParser(),
|
||||
UnionTypeParser()
|
||||
)) {
|
||||
node -> if(node is SingleTypeNode) UnionTypeNode(listOf(node), node.position) else node
|
||||
}.parse(input)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,6 @@ import io.smnp.dsl.ast.model.node.TypeSpecifierNode
|
||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||
|
||||
class TypeSpecifierParser :
|
||||
AbstractIterableParser(TokenType.OPEN_ANGLE, TypeParser(), TokenType.CLOSE_ANGLE, { list, tokenPosition ->
|
||||
AbstractIterableParser(TokenType.OPEN_ANGLE, SingleTypeParser(), TokenType.CLOSE_ANGLE, { list, tokenPosition ->
|
||||
TypeSpecifierNode(list, tokenPosition)
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.smnp.dsl.ast.parser
|
||||
|
||||
import io.smnp.dsl.ast.model.node.UnionTypeNode
|
||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||
|
||||
class UnionTypeParser : AbstractIterableParser(TokenType.OPEN_ANGLE, SingleTypeParser(), TokenType.CLOSE_ANGLE, { list, tokenPosition ->
|
||||
UnionTypeNode(list, tokenPosition)
|
||||
})
|
||||
Reference in New Issue
Block a user