diff --git a/api/src/main/kotlin/io/smnp/callable/method/MethodDefinitionTool.kt b/api/src/main/kotlin/io/smnp/callable/method/MethodDefinitionTool.kt index 8c65500..c0448a5 100644 --- a/api/src/main/kotlin/io/smnp/callable/method/MethodDefinitionTool.kt +++ b/api/src/main/kotlin/io/smnp/callable/method/MethodDefinitionTool.kt @@ -12,7 +12,7 @@ class MethodDefinitionTool { } inner class MethodDefinitionToolStage2(private val signature: Signature) { - infix fun define(body: (Environment, Value, List) -> Value) { + infix fun body(body: (Environment, Value, List) -> Value) { definitions.add(MethodDefinition(signature, body)) } } diff --git a/app/src/main/kotlin/io/smnp/callable/function/CustomFunction.kt b/app/src/main/kotlin/io/smnp/callable/function/CustomFunction.kt index db66d91..df25e77 100644 --- a/app/src/main/kotlin/io/smnp/callable/function/CustomFunction.kt +++ b/app/src/main/kotlin/io/smnp/callable/function/CustomFunction.kt @@ -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 diff --git a/app/src/main/kotlin/io/smnp/callable/method/CustomMethod.kt b/app/src/main/kotlin/io/smnp/callable/method/CustomMethod.kt new file mode 100644 index 0000000..622905b --- /dev/null +++ b/app/src/main/kotlin/io/smnp/callable/method/CustomMethod.kt @@ -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() + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/io/smnp/callable/function/FunctionEnvironmentProvider.kt b/app/src/main/kotlin/io/smnp/callable/util/FunctionEnvironmentProvider.kt similarity index 98% rename from app/src/main/kotlin/io/smnp/callable/function/FunctionEnvironmentProvider.kt rename to app/src/main/kotlin/io/smnp/callable/util/FunctionEnvironmentProvider.kt index 23ad96f..a3ebf28 100644 --- a/app/src/main/kotlin/io/smnp/callable/function/FunctionEnvironmentProvider.kt +++ b/app/src/main/kotlin/io/smnp/callable/util/FunctionEnvironmentProvider.kt @@ -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 diff --git a/app/src/main/kotlin/io/smnp/callable/function/FunctionSignatureParser.kt b/app/src/main/kotlin/io/smnp/callable/util/FunctionSignatureParser.kt similarity index 83% rename from app/src/main/kotlin/io/smnp/callable/function/FunctionSignatureParser.kt rename to app/src/main/kotlin/io/smnp/callable/util/FunctionSignatureParser.kt index 8133b15..017afde 100644 --- a/app/src/main/kotlin/io/smnp/callable/function/FunctionSignatureParser.kt +++ b/app/src/main/kotlin/io/smnp/callable/util/FunctionSignatureParser.kt @@ -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) } diff --git a/app/src/main/kotlin/io/smnp/dsl/ast/model/node/ExtendNode.kt b/app/src/main/kotlin/io/smnp/dsl/ast/model/node/ExtendNode.kt index fbe701c..12a6034 100644 --- a/app/src/main/kotlin/io/smnp/dsl/ast/model/node/ExtendNode.kt +++ b/app/src/main/kotlin/io/smnp/dsl/ast/model/node/ExtendNode.kt @@ -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] diff --git a/app/src/main/kotlin/io/smnp/evaluation/evaluator/ExtendEvaluator.kt b/app/src/main/kotlin/io/smnp/evaluation/evaluator/ExtendEvaluator.kt new file mode 100644 index 0000000..cb7eb0e --- /dev/null +++ b/app/src/main/kotlin/io/smnp/evaluation/evaluator/ExtendEvaluator.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/io/smnp/evaluation/evaluator/RootEvaluator.kt b/app/src/main/kotlin/io/smnp/evaluation/evaluator/RootEvaluator.kt index f1ad6d5..c8a92e5 100644 --- a/app/src/main/kotlin/io/smnp/evaluation/evaluator/RootEvaluator.kt +++ b/app/src/main/kotlin/io/smnp/evaluation/evaluator/RootEvaluator.kt @@ -14,6 +14,7 @@ class RootEvaluator : Evaluator() { ImportEvaluator(), ExpressionEvaluator(), FunctionDefinitionEvaluator(), + ExtendEvaluator(), DefaultEvaluator() ), "correct statement") diff --git a/modules/lang/src/main/kotlin/io/smnp/ext/lang/method/ListAccessMethod.kt b/modules/lang/src/main/kotlin/io/smnp/ext/lang/method/ListAccessMethod.kt index 1efd43e..af1fdce 100644 --- a/modules/lang/src/main/kotlin/io/smnp/ext/lang/method/ListAccessMethod.kt +++ b/modules/lang/src/main/kotlin/io/smnp/ext/lang/method/ListAccessMethod.kt @@ -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 val i = index.value!! as Int diff --git a/modules/lang/src/main/kotlin/io/smnp/ext/lang/method/MapAccessMethod.kt b/modules/lang/src/main/kotlin/io/smnp/ext/lang/method/MapAccessMethod.kt index 5ac682d..1ed323b 100644 --- a/modules/lang/src/main/kotlin/io/smnp/ext/lang/method/MapAccessMethod.kt +++ b/modules/lang/src/main/kotlin/io/smnp/ext/lang/method/MapAccessMethod.kt @@ -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) map[key] ?: throw RuntimeException("Key '${key.value!!}' not found") }