Create FunctionSignatureParser

This commit is contained in:
2020-03-11 18:17:52 +01:00
parent a354a5b96e
commit eb4fb1e980
11 changed files with 183 additions and 17 deletions

View File

@@ -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)
}
}

View File

@@ -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]

View File

@@ -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)

View File

@@ -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)) {

View File

@@ -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")
) {

View File

@@ -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())
}
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
})

View File

@@ -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)
})