Enable passing arguments to custom functions

This commit is contained in:
2020-03-11 22:16:29 +01:00
parent 7d61756273
commit 53bba579c1
11 changed files with 110 additions and 32 deletions

View File

@@ -0,0 +1,25 @@
package io.smnp.collection
class Stack<T> private constructor(private val list: MutableList<T>) : List<T> 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 <T> of(vararg items: T): Stack<T> {
return Stack(mutableListOf(*items))
}
}
}

View File

@@ -12,4 +12,9 @@ interface Environment {
fun printCallStack() fun printCallStack()
fun defineFunction(function: Function) fun defineFunction(function: Function)
fun defineMethod(method: Method) fun defineMethod(method: Method)
fun pushScope(scope: MutableMap<String, Value> = mutableMapOf())
fun popScope(): Map<String, Value>?
fun printScopes()
fun setVariable(name: String, value: Value)
fun getVariable(name: String): Value
} }

View File

@@ -1,28 +1,22 @@
package io.smnp.runtime.model package io.smnp.runtime.model
import io.smnp.collection.Stack
import io.smnp.type.model.Value import io.smnp.type.model.Value
import io.smnp.type.module.Module import io.smnp.type.module.Module
class CallStack { class CallStack {
private val items = mutableListOf<CallStackItem>() private val items = Stack.of<CallStackFrame>()
fun push(module: Module, name: String, arguments: List<Value>) { fun push(module: Module, name: String, arguments: List<Value>) {
items.add(CallStackItem(module, name, arguments)) items.push(CallStackFrame(module, name, arguments))
} }
fun push(item: CallStackItem) { fun push(frame: CallStackFrame) = items.push(frame)
items.add(item)
}
fun pop(): CallStackItem? { fun pop() = items.pop()
if(items.isEmpty()) {
return null
}
val last = items.last() fun top() = items.top()
items.removeAt(items.size-1)
return last
}
fun pretty() { fun pretty() {
items.asReversed().forEachIndexed { index, item -> println("[${items.size - index - 1}] $item") } items.asReversed().forEachIndexed { index, item -> println("[${items.size - index - 1}] $item") }

View File

@@ -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<Value>
) {
private val scopes = Stack.of<MutableMap<String, Value>>(mutableMapOf())
fun pushScope(scope: MutableMap<String, Value> = 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())}"
}

View File

@@ -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<Value>
) {
override fun toString() = "${module.canonicalName}::$name${ActualSignatureFormatter.format(arguments.toTypedArray())}"
}

View File

@@ -15,15 +15,18 @@ object CustomFunction {
override fun define(new: FunctionDefinitionTool) { override fun define(new: FunctionDefinitionTool) {
val (_, argumentsNode, bodyNode) = node val (_, argumentsNode, bodyNode) = node
val signature = FunctionSignatureParser.parseSignature(argumentsNode as FunctionDefinitionArgumentsNode) val signature = FunctionSignatureParser.parseSignature(argumentsNode as FunctionDefinitionArgumentsNode)
val evaluator = BlockEvaluator() val evaluator = BlockEvaluator(dedicatedScope = false)
new function signature body { env, args -> new function signature body { env, args ->
val boundArguments = FunctionEnvironmentProvider.provideEnvironment(argumentsNode, args, env) val boundArguments = FunctionEnvironmentProvider.provideEnvironment(argumentsNode, args, env)
// TODO push boundArguments to variables scope
try { try {
env.pushScope(boundArguments.toMutableMap())
evaluator.evaluate(bodyNode, env) evaluator.evaluate(bodyNode, env)
} catch(value: Return) { } catch(value: Return) {
return@body value.value return@body value.value
} finally {
env.popScope()
} }
Value.void() Value.void()

View File

@@ -79,4 +79,14 @@ class DefaultEnvironment : Environment {
override fun defineMethod(method: Method) { override fun defineMethod(method: Method) {
rootModule.addMethod(method) rootModule.addMethod(method)
} }
override fun pushScope(scope: MutableMap<String, Value>) = 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)
} }

View File

@@ -6,15 +6,18 @@ import io.smnp.environment.Environment
import io.smnp.evaluation.model.entity.EvaluatorOutput import io.smnp.evaluation.model.entity.EvaluatorOutput
import io.smnp.evaluation.model.enumeration.EvaluationResult 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 supportedNodes() = listOf(BlockNode::class)
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val evaluator = DefaultEvaluator() val evaluator = DefaultEvaluator()
if (dedicatedScope) environment.pushScope()
val ok = (node as BlockNode).statements.all { val ok = (node as BlockNode).statements.all {
evaluator.evaluate(it, environment).result != EvaluationResult.FAILED 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()
} }
} }

View File

@@ -10,7 +10,7 @@ class DefaultEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
return oneOf( return oneOf(
ConditionEvaluator(), ConditionEvaluator(),
BlockEvaluator(), BlockEvaluator(true),
ThrowEvaluator(), ThrowEvaluator(),
ReturnEvaluator(), ReturnEvaluator(),
ExpressionEvaluator() ExpressionEvaluator()

View File

@@ -17,6 +17,7 @@ class ExpressionEvaluator : Evaluator() {
NoteLiteralEvaluator(), NoteLiteralEvaluator(),
ListEvaluator(), ListEvaluator(),
MapEvaluator(), MapEvaluator(),
IdentifierEvaluator(),
MinusOperatorEvaluator(), MinusOperatorEvaluator(),
NotOperatorEvaluator(), NotOperatorEvaluator(),

View File

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