Add support for defining custom methods

This commit is contained in:
2020-03-12 21:46:11 +01:00
parent e7268bf18a
commit 26d072d46f
10 changed files with 106 additions and 12 deletions

View File

@@ -12,7 +12,7 @@ class MethodDefinitionTool {
}
inner class MethodDefinitionToolStage2(private val signature: Signature) {
infix fun define(body: (Environment, Value, List<Value>) -> Value) {
infix fun body(body: (Environment, Value, List<Value>) -> Value) {
definitions.add(MethodDefinition(signature, body))
}
}

View File

@@ -1,5 +1,7 @@
package io.smnp.callable.function
import io.smnp.callable.util.FunctionEnvironmentProvider
import io.smnp.callable.util.FunctionSignatureParser
import io.smnp.dsl.ast.model.node.FunctionDefinitionArgumentsNode
import io.smnp.dsl.ast.model.node.FunctionDefinitionNode
import io.smnp.dsl.ast.model.node.IdentifierNode

View File

@@ -0,0 +1,42 @@
package io.smnp.callable.method
import io.smnp.callable.util.FunctionEnvironmentProvider
import io.smnp.callable.util.FunctionSignatureParser
import io.smnp.dsl.ast.model.node.FunctionDefinitionArgumentsNode
import io.smnp.dsl.ast.model.node.FunctionDefinitionNode
import io.smnp.dsl.ast.model.node.IdentifierNode
import io.smnp.evaluation.evaluator.BlockEvaluator
import io.smnp.evaluation.model.exception.Return
import io.smnp.type.matcher.Matcher
import io.smnp.type.model.Value
object CustomMethod {
fun create(type: Matcher, objectIdentifier: String, node: FunctionDefinitionNode): Method {
val identifier = (node.identifier as IdentifierNode).token.rawValue
return object : Method(type, identifier) {
override fun define(new: MethodDefinitionTool) {
val (_, argumentsNode, bodyNode) = node
val signature = FunctionSignatureParser.parseSignature(argumentsNode as FunctionDefinitionArgumentsNode)
val evaluator = BlockEvaluator(dedicatedScope = false)
new method signature body { env, obj, args ->
val boundArguments =
FunctionEnvironmentProvider.provideEnvironment(argumentsNode, args, env).toMutableMap()
boundArguments[objectIdentifier] = obj
try {
env.pushScope(boundArguments)
evaluator.evaluate(bodyNode, env)
} catch (value: Return) {
return@body value.value
} finally {
env.popScope()
}
Value.void()
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
package io.smnp.callable.function
package io.smnp.callable.util
import io.smnp.dsl.ast.model.node.FunctionDefinitionArgumentsNode
import io.smnp.dsl.ast.model.node.IdentifierNode

View File

@@ -1,4 +1,4 @@
package io.smnp.callable.function
package io.smnp.callable.util
import io.smnp.callable.signature.Signature
import io.smnp.dsl.ast.model.node.*
@@ -54,7 +54,11 @@ object FunctionSignatureParser {
val vararg =
signature.items.indexOfFirst { it is RegularFunctionDefinitionArgumentNode && it.vararg != Node.NONE }
val metadata = SignatureMetadata(lastRegular != -1, firstOptional != -1, vararg != -1)
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")
@@ -77,13 +81,19 @@ object FunctionSignatureParser {
}
if (unionTypeNode.items.size == 1) {
return matcherForSingleTypeNode(unionTypeNode.items[0] as SingleTypeNode)
return matcherForSingleTypeNode(
unionTypeNode.items[0] as SingleTypeNode
)
}
return oneOf(*unionTypeNode.items.map { matcherForSingleTypeNode(it as SingleTypeNode) }.toTypedArray())
return oneOf(*unionTypeNode.items.map {
matcherForSingleTypeNode(
it as SingleTypeNode
)
}.toTypedArray())
}
private fun matcherForSingleTypeNode(singleTypeNode: SingleTypeNode): Matcher {
fun matcherForSingleTypeNode(singleTypeNode: SingleTypeNode): Matcher {
// TODO
val type = DataType.valueOf((singleTypeNode.type as IdentifierNode).token.rawValue.toUpperCase())
if (singleTypeNode.specifiers == Node.NONE) {
@@ -107,7 +117,11 @@ object FunctionSignatureParser {
types.add(allTypes())
}
listSpecifierNode.items.forEach { types.add(matcherForSingleTypeNode(it as SingleTypeNode)) }
listSpecifierNode.items.forEach { types.add(
matcherForSingleTypeNode(
it as SingleTypeNode
)
) }
return listOfMatchers(*types.toTypedArray())
}
@@ -124,8 +138,16 @@ object FunctionSignatureParser {
values.add(allTypes())
}
keySpecifierNode.items.forEach { keys.add(matcherForSingleTypeNode(it as SingleTypeNode)) }
valueSpecifierNode.items.forEach { values.add(matcherForSingleTypeNode(it as SingleTypeNode)) }
keySpecifierNode.items.forEach { keys.add(
matcherForSingleTypeNode(
it as SingleTypeNode
)
) }
valueSpecifierNode.items.forEach { values.add(
matcherForSingleTypeNode(
it as SingleTypeNode
)
) }
return mapOfMatchers(keys, values)
}

View File

@@ -3,6 +3,10 @@ package io.smnp.dsl.ast.model.node
import io.smnp.dsl.token.model.entity.TokenPosition
class ExtendNode(type: Node, identifier: Node, functions: Node, position: TokenPosition) : Node(3, position) {
operator fun component1(): Node = children[0]
operator fun component2(): Node = children[1]
operator fun component3(): Node = children[2]
val type: Node
get() = children[0]

View File

@@ -0,0 +1,23 @@
package io.smnp.evaluation.evaluator
import io.smnp.callable.method.CustomMethod
import io.smnp.callable.util.FunctionSignatureParser
import io.smnp.dsl.ast.model.node.*
import io.smnp.environment.Environment
import io.smnp.evaluation.model.entity.EvaluatorOutput
class ExtendEvaluator : Evaluator() {
override fun supportedNodes() = listOf(ExtendNode::class)
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val (typeNode, identifierNode, methodsNode) = node as ExtendNode
val type = FunctionSignatureParser.matcherForSingleTypeNode(typeNode as SingleTypeNode)
val identifier = (identifierNode as IdentifierNode).token.rawValue
methodsNode.children
.map { CustomMethod.create(type, identifier, it as FunctionDefinitionNode) }
.forEach { environment.defineMethod(it) }
return EvaluatorOutput.ok()
}
}

View File

@@ -14,6 +14,7 @@ class RootEvaluator : Evaluator() {
ImportEvaluator(),
ExpressionEvaluator(),
FunctionDefinitionEvaluator(),
ExtendEvaluator(),
DefaultEvaluator()
), "correct statement")

View File

@@ -11,7 +11,7 @@ import io.smnp.type.model.Value
class ListAccessMethod : Method(ofType(LIST), "get") {
override fun define(new: MethodDefinitionTool) {
new method simple(ofType(INT)) define { _, value, (index) ->
new method simple(ofType(INT)) body { _, value, (index) ->
val list = value.value!! as List<Value>
val i = index.value!! as Int

View File

@@ -11,7 +11,7 @@ import io.smnp.type.model.Value
class MapAccessMethod : Method(ofType(MAP), "get") {
override fun define(new: MethodDefinitionTool) {
new method simple(allTypes()) define { _, obj, (key) ->
new method simple(allTypes()) body { _, obj, (key) ->
val map = (obj.value!! as Map<Value, Value>)
map[key] ?: throw RuntimeException("Key '${key.value!!}' not found")
}