Migrate loop evaluator to Kotlin
This commit is contained in:
@@ -1,6 +1,11 @@
|
|||||||
package io.smnp.dsl.ast.model.node
|
package io.smnp.dsl.ast.model.node
|
||||||
|
|
||||||
class LoopNode(iterator: Node, parameters: Node, operator: Node, statement: Node, filter: Node): Node(4, operator.position) {
|
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
|
val iterator: Node
|
||||||
get() = children[0]
|
get() = children[0]
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ class ExpressionEvaluator : Evaluator() {
|
|||||||
RelationOperatorEvaluator(),
|
RelationOperatorEvaluator(),
|
||||||
AssignmentOperatorEvaluator(),
|
AssignmentOperatorEvaluator(),
|
||||||
|
|
||||||
FunctionCallEvaluator()
|
FunctionCallEvaluator(),
|
||||||
|
|
||||||
|
LoopEvaluator()
|
||||||
).evaluate(node, environment)
|
).evaluate(node, environment)
|
||||||
|
|
||||||
if(output.result == EvaluationResult.OK) {
|
if(output.result == EvaluationResult.OK) {
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user