Migrate loop evaluator to Kotlin

This commit is contained in:
2020-03-13 07:25:27 +01:00
parent 26d072d46f
commit 7008bc626c
3 changed files with 172 additions and 1 deletions

View File

@@ -1,6 +1,11 @@
package io.smnp.dsl.ast.model.node
class LoopNode(iterator: Node, parameters: Node, operator: Node, statement: Node, filter: Node): Node(4, operator.position) {
operator fun component1() = children[0]
operator fun component2() = children[1]
operator fun component3() = children[2]
operator fun component4() = children[3]
val iterator: Node
get() = children[0]

View File

@@ -29,7 +29,9 @@ class ExpressionEvaluator : Evaluator() {
RelationOperatorEvaluator(),
AssignmentOperatorEvaluator(),
FunctionCallEvaluator()
FunctionCallEvaluator(),
LoopEvaluator()
).evaluate(node, environment)
if(output.result == EvaluationResult.OK) {

View File

@@ -0,0 +1,164 @@
package io.smnp.evaluation.evaluator
import io.smnp.dsl.ast.model.node.IdentifierNode
import io.smnp.dsl.ast.model.node.LoopNode
import io.smnp.dsl.ast.model.node.Node
import io.smnp.environment.Environment
import io.smnp.error.EvaluationException
import io.smnp.evaluation.model.entity.EvaluatorOutput
import io.smnp.evaluation.model.enumeration.EvaluationResult
import io.smnp.type.enumeration.DataType.*
import io.smnp.type.model.Value
class LoopEvaluator : Evaluator() {
private val defaultEvaluator = assert(DefaultEvaluator(), "correct statement")
private val expressionEvaluator = assert(ExpressionEvaluator(), "expression")
override fun supportedNodes() = listOf(LoopNode::class)
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val (iteratorNode, parametersNode, statementNode, filterNode) = node as LoopNode
val iterator = expressionEvaluator.evaluate(iteratorNode, environment).value!!
environment.pushScope()
val output = when (iterator.type) {
INT -> evaluateForInt(iterator, parametersNode, statementNode, filterNode, environment)
LIST -> evaluateForList(iterator, parametersNode, statementNode, filterNode, environment)
MAP -> evaluateForMap(iterator, parametersNode, statementNode, filterNode, environment)
BOOL -> evaluateForBool(iteratorNode, parametersNode, statementNode, filterNode, environment)
else -> throw EvaluationException(
"Expected for-loop with int iterator or foreach-loop with list or map iterator or while-loop with bool iterator, found ${iterator.type.name.toLowerCase()}",
iteratorNode.position
)
}
environment.popScope()
return output
}
private fun evaluateForInt(
iterator: Value,
parametersNode: Node,
statementNode: Node,
filterNode: Node,
environment: Environment
): EvaluatorOutput {
return output { outputs ->
var index = 0
repeat(iterator.value as Int) {
parameters(parametersNode, { id -> environment.setVariable(id, Value.int(index)) })
if (filter(filterNode, environment)) {
outputs.add(defaultEvaluator.evaluate(statementNode, environment))
index++
}
}
}
}
private fun evaluateForList(
iterator: Value,
parametersNode: Node,
statementNode: Node,
filterNode: Node,
environment: Environment
): EvaluatorOutput {
return output { outputs ->
var index = 0
(iterator.value as List<Value>).forEach { item ->
parameters(parametersNode,
{ id -> environment.setVariable(id, item) },
{ id -> environment.setVariable(id, Value.int(index)) }
)
if (filter(filterNode, environment)) {
outputs.add(defaultEvaluator.evaluate(statementNode, environment))
index++
}
}
}
}
private fun evaluateForMap(
iterator: Value,
parametersNode: Node,
statementNode: Node,
filterNode: Node,
environment: Environment
): EvaluatorOutput {
return output { outputs ->
var index = 0
(iterator.value as Map<Value, Value>).forEach { (key, value) ->
parameters(parametersNode,
{ id -> environment.setVariable(id, value) },
{ id -> environment.setVariable(id, key) },
{ id -> environment.setVariable(id, Value.int(index)) }
)
if (filter(filterNode, environment)) {
outputs.add(defaultEvaluator.evaluate(statementNode, environment))
index++
}
}
}
}
private fun evaluateForBool(
iteratorNode: Node,
parametersNode: Node,
statementNode: Node,
filterNode: Node,
environment: Environment
): EvaluatorOutput {
if (parametersNode != Node.NONE) {
throw EvaluationException("Parameters are not supported in the while-loop", parametersNode.position)
}
if (filterNode != Node.NONE) {
throw EvaluationException("Filter is not supported in the while-loop", filterNode.position)
}
return output { outputs ->
while (expressionEvaluator.evaluate(iteratorNode, environment).value!!.value as Boolean) {
outputs.add(defaultEvaluator.evaluate(statementNode, environment))
}
}
}
private fun parameters(parametersNode: Node, vararg parameterConsumers: (String) -> Unit) {
if (parametersNode.children.isNotEmpty()) {
parameterConsumers.forEachIndexed { index, consumer ->
if (index < parametersNode.children.size) {
consumer((parametersNode.children[index] as IdentifierNode).token.rawValue)
}
}
}
}
private fun filter(filterNode: Node, environment: Environment): Boolean {
if (filterNode != Node.NONE) {
val condition = expressionEvaluator.evaluate(filterNode, environment).value!!
if (condition.type != BOOL) {
throw EvaluationException("Filter condition should be evaluated to bool type", filterNode.position)
}
return condition.value as Boolean
}
return true
}
private infix fun output(evaluate: (MutableList<EvaluatorOutput>) -> Unit): EvaluatorOutput {
val outputs = mutableListOf<EvaluatorOutput>()
evaluate(outputs)
return when {
outputs.all { it.result == EvaluationResult.VALUE } -> EvaluatorOutput.value(Value.list(outputs.map { it.value!! }))
// Disclaimer: It musn't be ok() because ExpressionEvaluator expects non-ok success output from each
// of subevaluators.
// Because loop needs to act as an expression its evaluator needs to meet ExpressionEvaluator requirements
// and return value() with VOID type.
outputs.none { it.result == EvaluationResult.FAILED } -> EvaluatorOutput.value(Value.void())
else -> EvaluatorOutput.fail()
}
}
}