Add support for defining custom methods
This commit is contained in:
@@ -12,7 +12,7 @@ class MethodDefinitionTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inner class MethodDefinitionToolStage2(private val signature: Signature) {
|
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))
|
definitions.add(MethodDefinition(signature, body))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package io.smnp.callable.function
|
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.FunctionDefinitionArgumentsNode
|
||||||
import io.smnp.dsl.ast.model.node.FunctionDefinitionNode
|
import io.smnp.dsl.ast.model.node.FunctionDefinitionNode
|
||||||
import io.smnp.dsl.ast.model.node.IdentifierNode
|
import io.smnp.dsl.ast.model.node.IdentifierNode
|
||||||
|
|||||||
42
app/src/main/kotlin/io/smnp/callable/method/CustomMethod.kt
Normal file
42
app/src/main/kotlin/io/smnp/callable/method/CustomMethod.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.FunctionDefinitionArgumentsNode
|
||||||
import io.smnp.dsl.ast.model.node.IdentifierNode
|
import io.smnp.dsl.ast.model.node.IdentifierNode
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package io.smnp.callable.function
|
package io.smnp.callable.util
|
||||||
|
|
||||||
import io.smnp.callable.signature.Signature
|
import io.smnp.callable.signature.Signature
|
||||||
import io.smnp.dsl.ast.model.node.*
|
import io.smnp.dsl.ast.model.node.*
|
||||||
@@ -54,7 +54,11 @@ object FunctionSignatureParser {
|
|||||||
val vararg =
|
val vararg =
|
||||||
signature.items.indexOfFirst { it is RegularFunctionDefinitionArgumentNode && it.vararg != Node.NONE }
|
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) {
|
if (metadata.hasVararg && metadata.hasOptional) {
|
||||||
throw RuntimeException("Optional arguments and vararg cannot be mixed in same signature")
|
throw RuntimeException("Optional arguments and vararg cannot be mixed in same signature")
|
||||||
@@ -77,13 +81,19 @@ object FunctionSignatureParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (unionTypeNode.items.size == 1) {
|
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
|
// TODO
|
||||||
val type = DataType.valueOf((singleTypeNode.type as IdentifierNode).token.rawValue.toUpperCase())
|
val type = DataType.valueOf((singleTypeNode.type as IdentifierNode).token.rawValue.toUpperCase())
|
||||||
if (singleTypeNode.specifiers == Node.NONE) {
|
if (singleTypeNode.specifiers == Node.NONE) {
|
||||||
@@ -107,7 +117,11 @@ object FunctionSignatureParser {
|
|||||||
types.add(allTypes())
|
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())
|
return listOfMatchers(*types.toTypedArray())
|
||||||
}
|
}
|
||||||
@@ -124,8 +138,16 @@ object FunctionSignatureParser {
|
|||||||
values.add(allTypes())
|
values.add(allTypes())
|
||||||
}
|
}
|
||||||
|
|
||||||
keySpecifierNode.items.forEach { keys.add(matcherForSingleTypeNode(it as SingleTypeNode)) }
|
keySpecifierNode.items.forEach { keys.add(
|
||||||
valueSpecifierNode.items.forEach { values.add(matcherForSingleTypeNode(it as SingleTypeNode)) }
|
matcherForSingleTypeNode(
|
||||||
|
it as SingleTypeNode
|
||||||
|
)
|
||||||
|
) }
|
||||||
|
valueSpecifierNode.items.forEach { values.add(
|
||||||
|
matcherForSingleTypeNode(
|
||||||
|
it as SingleTypeNode
|
||||||
|
)
|
||||||
|
) }
|
||||||
|
|
||||||
return mapOfMatchers(keys, values)
|
return mapOfMatchers(keys, values)
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,10 @@ package io.smnp.dsl.ast.model.node
|
|||||||
import io.smnp.dsl.token.model.entity.TokenPosition
|
import io.smnp.dsl.token.model.entity.TokenPosition
|
||||||
|
|
||||||
class ExtendNode(type: Node, identifier: Node, functions: Node, position: TokenPosition) : Node(3, position) {
|
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
|
val type: Node
|
||||||
get() = children[0]
|
get() = children[0]
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ class RootEvaluator : Evaluator() {
|
|||||||
ImportEvaluator(),
|
ImportEvaluator(),
|
||||||
ExpressionEvaluator(),
|
ExpressionEvaluator(),
|
||||||
FunctionDefinitionEvaluator(),
|
FunctionDefinitionEvaluator(),
|
||||||
|
ExtendEvaluator(),
|
||||||
DefaultEvaluator()
|
DefaultEvaluator()
|
||||||
), "correct statement")
|
), "correct statement")
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import io.smnp.type.model.Value
|
|||||||
|
|
||||||
class ListAccessMethod : Method(ofType(LIST), "get") {
|
class ListAccessMethod : Method(ofType(LIST), "get") {
|
||||||
override fun define(new: MethodDefinitionTool) {
|
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 list = value.value!! as List<Value>
|
||||||
val i = index.value!! as Int
|
val i = index.value!! as Int
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import io.smnp.type.model.Value
|
|||||||
|
|
||||||
class MapAccessMethod : Method(ofType(MAP), "get") {
|
class MapAccessMethod : Method(ofType(MAP), "get") {
|
||||||
override fun define(new: MethodDefinitionTool) {
|
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>)
|
val map = (obj.value!! as Map<Value, Value>)
|
||||||
map[key] ?: throw RuntimeException("Key '${key.value!!}' not found")
|
map[key] ?: throw RuntimeException("Key '${key.value!!}' not found")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user