Updated Application overview (markdown)
@@ -224,3 +224,59 @@ RootNode 1:16
|
|||||||
└─BoolLiteralNode 1:37
|
└─BoolLiteralNode 1:37
|
||||||
└ (bool, »false«, 1:37)
|
└ (bool, »false«, 1:37)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
All parsers-related code is located in `io.smnp.dsl.ast` package.
|
||||||
|
|
||||||
|
## Evaluator
|
||||||
|
Evaluator is the last stage of SMNP language processing pipeline and also is the heart
|
||||||
|
of entire SMNP tool, which takes AST as an input and performs programmed operations.
|
||||||
|
Similar to implemented parser, evaluator works *recursively* because of processing tree-like
|
||||||
|
structure.
|
||||||
|
Evaluator's architecture is similar to parser's one. Evaluator consists of
|
||||||
|
smaller evaluators which are able to evaluate small part of AST's node types.
|
||||||
|
Similar to parsers, the evaluators also uses a helper method (like `oneOf()`) to improve readability and decrease
|
||||||
|
the complexity along with the code repeatability.
|
||||||
|
|
||||||
|
Because evaluator introduces as *runtime* term, it also works on special object
|
||||||
|
called *environment*. The *environment* object contains some runtime information,
|
||||||
|
like loaded modules (with included functions and methods), call stack with included scopes and some meta information. This object is passed through all evaluators along with AST and its
|
||||||
|
subtrees.
|
||||||
|
|
||||||
|
Following listening shows the example evaluator which is `if` statement evaluator:
|
||||||
|
```kotlin
|
||||||
|
class ConditionEvaluator : Evaluator() {
|
||||||
|
private val expressionEvaluator = ExpressionEvaluator()
|
||||||
|
private val defaultEvaluator = DefaultEvaluator()
|
||||||
|
|
||||||
|
override fun supportedNodes() = listOf(ConditionNode::class)
|
||||||
|
|
||||||
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
|
val (conditionNode, trueBranchNode, falseBranchNode) = (node as ConditionNode)
|
||||||
|
val condition = expressionEvaluator.evaluate(conditionNode, environment).value
|
||||||
|
|
||||||
|
if (condition.type != DataType.BOOL) {
|
||||||
|
throw contextEvaluationException(
|
||||||
|
"Condition should be of bool type, found '${condition.value}'",
|
||||||
|
conditionNode.position,
|
||||||
|
environment
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition.value as Boolean) {
|
||||||
|
return defaultEvaluator.evaluate(trueBranchNode, environment)
|
||||||
|
} else if (falseBranchNode !is NoneNode) {
|
||||||
|
return defaultEvaluator.evaluate(falseBranchNode, environment)
|
||||||
|
}
|
||||||
|
|
||||||
|
return EvaluatorOutput.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The code above defines list of supported node, which in this case is a list with single element: `ConditionNode`. The `ConditionNode` is product of `ConditionParser`'s work which handles the `if` statements.
|
||||||
|
The `tryToEvaluate()` method contains the actually logic of evaluation, and in this case it:
|
||||||
|
1. evaluates the condition using `ExpressionEvaluator` (it always returns a value)
|
||||||
|
2. asserts the value to be of `bool` type - if it's other than `bool`, an exception is begin thrown
|
||||||
|
3. evaluates the `trueBranchNode` if the value is evaluated to `true`
|
||||||
|
4. if the condition is evaluated to `false`, it checks if `falseBranchNode` (that comes from `else` clause) is present. If so, it's being evaluated.
|
||||||
|
|
||||||
|
All evaluator-related code is located in `io.smnp.evaluation` package.
|
||||||
Reference in New Issue
Block a user