From 53bba579c1b6adc93f59c2c3e98fcb59e86df511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?= Date: Wed, 11 Mar 2020 22:16:29 +0100 Subject: [PATCH] Enable passing arguments to custom functions --- .../main/kotlin/io/smnp/collection/Stack.kt | 25 +++++++++++++ .../kotlin/io/smnp/environment/Environment.kt | 5 +++ .../kotlin/io/smnp/runtime/model/CallStack.kt | 20 ++++------- .../io/smnp/runtime/model/CallStackFrame.kt | 35 +++++++++++++++++++ .../io/smnp/runtime/model/CallStackItem.kt | 13 ------- .../smnp/callable/function/CustomFunction.kt | 7 ++-- .../io/smnp/environment/DefaultEnvironment.kt | 10 ++++++ .../evaluation/evaluator/BlockEvaluator.kt | 7 ++-- .../evaluation/evaluator/DefaultEvaluator.kt | 2 +- .../evaluator/ExpressionEvaluator.kt | 3 +- .../evaluator/IdentifierEvaluator.kt | 15 ++++++++ 11 files changed, 110 insertions(+), 32 deletions(-) create mode 100644 api/src/main/kotlin/io/smnp/collection/Stack.kt create mode 100644 api/src/main/kotlin/io/smnp/runtime/model/CallStackFrame.kt delete mode 100644 api/src/main/kotlin/io/smnp/runtime/model/CallStackItem.kt create mode 100644 app/src/main/kotlin/io/smnp/evaluation/evaluator/IdentifierEvaluator.kt diff --git a/api/src/main/kotlin/io/smnp/collection/Stack.kt b/api/src/main/kotlin/io/smnp/collection/Stack.kt new file mode 100644 index 0000000..ca69efa --- /dev/null +++ b/api/src/main/kotlin/io/smnp/collection/Stack.kt @@ -0,0 +1,25 @@ +package io.smnp.collection + +class Stack private constructor(private val list: MutableList) : List by list { + fun push(item: T) { + list.add(item) + } + + fun pop(): T? { + if(list.isEmpty()) { + return null + } + + val last = list.last() + list.removeAt(list.size-1) + return last + } + + fun top() = list.last() + + companion object { + fun of(vararg items: T): Stack { + return Stack(mutableListOf(*items)) + } + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/io/smnp/environment/Environment.kt b/api/src/main/kotlin/io/smnp/environment/Environment.kt index 264dfdc..bd74856 100644 --- a/api/src/main/kotlin/io/smnp/environment/Environment.kt +++ b/api/src/main/kotlin/io/smnp/environment/Environment.kt @@ -12,4 +12,9 @@ interface Environment { fun printCallStack() fun defineFunction(function: Function) fun defineMethod(method: Method) + fun pushScope(scope: MutableMap = mutableMapOf()) + fun popScope(): Map? + fun printScopes() + fun setVariable(name: String, value: Value) + fun getVariable(name: String): Value } \ No newline at end of file diff --git a/api/src/main/kotlin/io/smnp/runtime/model/CallStack.kt b/api/src/main/kotlin/io/smnp/runtime/model/CallStack.kt index 90c9799..9e28b5c 100644 --- a/api/src/main/kotlin/io/smnp/runtime/model/CallStack.kt +++ b/api/src/main/kotlin/io/smnp/runtime/model/CallStack.kt @@ -1,28 +1,22 @@ package io.smnp.runtime.model +import io.smnp.collection.Stack import io.smnp.type.model.Value import io.smnp.type.module.Module + class CallStack { - private val items = mutableListOf() + private val items = Stack.of() fun push(module: Module, name: String, arguments: List) { - items.add(CallStackItem(module, name, arguments)) + items.push(CallStackFrame(module, name, arguments)) } - fun push(item: CallStackItem) { - items.add(item) - } + fun push(frame: CallStackFrame) = items.push(frame) - fun pop(): CallStackItem? { - if(items.isEmpty()) { - return null - } + fun pop() = items.pop() - val last = items.last() - items.removeAt(items.size-1) - return last - } + fun top() = items.top() fun pretty() { items.asReversed().forEachIndexed { index, item -> println("[${items.size - index - 1}] $item") } diff --git a/api/src/main/kotlin/io/smnp/runtime/model/CallStackFrame.kt b/api/src/main/kotlin/io/smnp/runtime/model/CallStackFrame.kt new file mode 100644 index 0000000..c75f4cf --- /dev/null +++ b/api/src/main/kotlin/io/smnp/runtime/model/CallStackFrame.kt @@ -0,0 +1,35 @@ +package io.smnp.runtime.model + +import io.smnp.callable.signature.ActualSignatureFormatter +import io.smnp.collection.Stack +import io.smnp.type.model.Value +import io.smnp.type.module.Module + +data class CallStackFrame( + val module: Module, + val name: String, + val arguments: List +) { + private val scopes = Stack.of>(mutableMapOf()) + + fun pushScope(scope: MutableMap = mutableMapOf()) { + scopes.push(scope) + } + + fun popScope() = scopes.pop() + + fun setVariable(name: String, value: Value) { + val scope = scopes.lastOrNull { it.containsKey(name) } ?: scopes.top() + scope[name] = value + } + + fun getVariable(name: String): Value { + return scopes.lastOrNull { it.containsKey(name) }?.get(name) ?: throw RuntimeException("Variable `$name` not found") + } + + fun prettyScope() { + scopes.asReversed().forEachIndexed { index, item -> println("[${scopes.size - index - 1}] $item") } + } + + override fun toString() = "${module.canonicalName}::$name${ActualSignatureFormatter.format(arguments.toTypedArray())}" +} \ No newline at end of file diff --git a/api/src/main/kotlin/io/smnp/runtime/model/CallStackItem.kt b/api/src/main/kotlin/io/smnp/runtime/model/CallStackItem.kt deleted file mode 100644 index 96ea18c..0000000 --- a/api/src/main/kotlin/io/smnp/runtime/model/CallStackItem.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.smnp.runtime.model - -import io.smnp.callable.signature.ActualSignatureFormatter -import io.smnp.type.model.Value -import io.smnp.type.module.Module - -data class CallStackItem( - val module: Module, - val name: String, - val arguments: List -) { - override fun toString() = "${module.canonicalName}::$name${ActualSignatureFormatter.format(arguments.toTypedArray())}" -} \ No newline at end of file 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 473c3bc..db66d91 100644 --- a/app/src/main/kotlin/io/smnp/callable/function/CustomFunction.kt +++ b/app/src/main/kotlin/io/smnp/callable/function/CustomFunction.kt @@ -15,15 +15,18 @@ object CustomFunction { override fun define(new: FunctionDefinitionTool) { val (_, argumentsNode, bodyNode) = node val signature = FunctionSignatureParser.parseSignature(argumentsNode as FunctionDefinitionArgumentsNode) - val evaluator = BlockEvaluator() + val evaluator = BlockEvaluator(dedicatedScope = false) new function signature body { env, args -> val boundArguments = FunctionEnvironmentProvider.provideEnvironment(argumentsNode, args, env) - // TODO push boundArguments to variables scope + try { + env.pushScope(boundArguments.toMutableMap()) evaluator.evaluate(bodyNode, env) } catch(value: Return) { return@body value.value + } finally { + env.popScope() } Value.void() diff --git a/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt b/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt index 917778d..33246bd 100644 --- a/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt +++ b/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt @@ -79,4 +79,14 @@ class DefaultEnvironment : Environment { override fun defineMethod(method: Method) { rootModule.addMethod(method) } + + override fun pushScope(scope: MutableMap) = callStack.top().pushScope(scope) + + override fun popScope() = callStack.top().popScope() + + override fun printScopes() = callStack.top().prettyScope() + + override fun setVariable(name: String, value: Value) = callStack.top().setVariable(name, value) + + override fun getVariable(name: String) = callStack.top().getVariable(name) } \ No newline at end of file diff --git a/app/src/main/kotlin/io/smnp/evaluation/evaluator/BlockEvaluator.kt b/app/src/main/kotlin/io/smnp/evaluation/evaluator/BlockEvaluator.kt index 2a1f7bf..b1d3ddd 100644 --- a/app/src/main/kotlin/io/smnp/evaluation/evaluator/BlockEvaluator.kt +++ b/app/src/main/kotlin/io/smnp/evaluation/evaluator/BlockEvaluator.kt @@ -6,15 +6,18 @@ import io.smnp.environment.Environment import io.smnp.evaluation.model.entity.EvaluatorOutput import io.smnp.evaluation.model.enumeration.EvaluationResult -class BlockEvaluator : Evaluator() { +class BlockEvaluator(private val dedicatedScope: Boolean) : Evaluator() { override fun supportedNodes() = listOf(BlockNode::class) override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { val evaluator = DefaultEvaluator() + + if (dedicatedScope) environment.pushScope() val ok = (node as BlockNode).statements.all { evaluator.evaluate(it, environment).result != EvaluationResult.FAILED } + if (dedicatedScope) environment.popScope() - return if(ok) EvaluatorOutput.ok() else EvaluatorOutput.fail() + return if (ok) EvaluatorOutput.ok() else EvaluatorOutput.fail() } } \ No newline at end of file diff --git a/app/src/main/kotlin/io/smnp/evaluation/evaluator/DefaultEvaluator.kt b/app/src/main/kotlin/io/smnp/evaluation/evaluator/DefaultEvaluator.kt index 2dc133b..73e9aa1 100644 --- a/app/src/main/kotlin/io/smnp/evaluation/evaluator/DefaultEvaluator.kt +++ b/app/src/main/kotlin/io/smnp/evaluation/evaluator/DefaultEvaluator.kt @@ -10,7 +10,7 @@ class DefaultEvaluator : Evaluator() { override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { return oneOf( ConditionEvaluator(), - BlockEvaluator(), + BlockEvaluator(true), ThrowEvaluator(), ReturnEvaluator(), ExpressionEvaluator() diff --git a/app/src/main/kotlin/io/smnp/evaluation/evaluator/ExpressionEvaluator.kt b/app/src/main/kotlin/io/smnp/evaluation/evaluator/ExpressionEvaluator.kt index 3b46fcd..2ae70c9 100644 --- a/app/src/main/kotlin/io/smnp/evaluation/evaluator/ExpressionEvaluator.kt +++ b/app/src/main/kotlin/io/smnp/evaluation/evaluator/ExpressionEvaluator.kt @@ -16,7 +16,8 @@ class ExpressionEvaluator : Evaluator() { BoolLiteralEvaluator(), NoteLiteralEvaluator(), ListEvaluator(), - MapEvaluator(), + MapEvaluator(), + IdentifierEvaluator(), MinusOperatorEvaluator(), NotOperatorEvaluator(), diff --git a/app/src/main/kotlin/io/smnp/evaluation/evaluator/IdentifierEvaluator.kt b/app/src/main/kotlin/io/smnp/evaluation/evaluator/IdentifierEvaluator.kt new file mode 100644 index 0000000..ba7d82c --- /dev/null +++ b/app/src/main/kotlin/io/smnp/evaluation/evaluator/IdentifierEvaluator.kt @@ -0,0 +1,15 @@ +package io.smnp.evaluation.evaluator + +import io.smnp.dsl.ast.model.node.IdentifierNode +import io.smnp.dsl.ast.model.node.Node +import io.smnp.environment.Environment +import io.smnp.evaluation.model.entity.EvaluatorOutput + +class IdentifierEvaluator : Evaluator() { + override fun supportedNodes() = listOf(IdentifierNode::class) + + override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { + val identifier = (node as IdentifierNode).token.rawValue + return EvaluatorOutput.value(environment.getVariable(identifier)) + } +} \ No newline at end of file