diff options
Diffstat (limited to 'support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Evaluator.kt')
-rw-r--r-- | support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Evaluator.kt | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Evaluator.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Evaluator.kt new file mode 100644 index 0000000000..78b98e709a --- /dev/null +++ b/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Evaluator.kt @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: BSD-3-Clause +package org.islandoftex.arara.model + +import org.islandoftex.arara.Arara +import org.islandoftex.arara.configuration.AraraSpec +import org.islandoftex.arara.localization.LanguageController +import org.islandoftex.arara.localization.Messages +import org.islandoftex.arara.ruleset.Conditional +import org.islandoftex.arara.utils.Methods +import org.mvel2.templates.TemplateRuntime + +/** + * Implements the evaluator model, on which a conditional can be analyzed and + * processed. + * + * @author Island of TeX + * @version 5.0 + * @since 4.0 + */ +class Evaluator { + // this attribute holds the maximum number of + // loops arara will accept; it's like + // reaching infinity + private val loops: Int = Arara.config[AraraSpec.Execution.maxLoops] + + // the counter for the current execution, it + // helps us keep track of the number of times + // this evaluation has happened, and also to + // prevent potential infinite loops + private var counter: Int = 0 + + // a flag that indicates the + // evaluation to halt regardless + // of the the result + private var halt: Boolean = false + + /** + * Check if a condition is of type if or unless and whether halt + * is set. + * @param type The type to check. + * @param haltCheck The value [halt] should have. + * @return `(type == if || type == unless) && haltCheck` + */ + private fun isIfUnlessAndHalt( + type: Conditional.ConditionalType, + haltCheck: Boolean = true + ): Boolean = + (type == Conditional.ConditionalType.IF || + type == Conditional.ConditionalType.UNLESS) && + halt == haltCheck + + /** + * Only run the evaluation of the conditional including a check whether + * the result needs to be inverted. + * @param conditional The conditional. + * @return The result of the evaluation. + */ + @Throws(AraraException::class, RuntimeException::class) + private fun evaluateCondition(conditional: Conditional): Boolean { + val result = TemplateRuntime.eval("@{ " + conditional.condition + " }", + Methods.getConditionalMethods()) + return if (result is Boolean) { + if (conditional.type == Conditional.ConditionalType.UNLESS || + conditional.type == Conditional.ConditionalType.UNTIL) + !result + else + result + } else { + throw AraraException(messages.getMessage( + Messages.ERROR_EVALUATE_NOT_BOOLEAN_VALUE)) + } + } + + /** + * Evaluate the provided conditional. + * + * @param conditional The conditional object. + * @return A boolean value indicating if the conditional holds. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + @Throws(AraraException::class) + @Suppress("TooGenericExceptionCaught") + fun evaluate(conditional: Conditional): Boolean { + // when in dry-run mode or not evaluating a + // conditional, arara always ignores conditional + // evaluations + if (conditional.type == Conditional.ConditionalType.NONE || + Arara.config[AraraSpec.Execution.dryrun] || + isIfUnlessAndHalt(conditional.type, true)) + return false + else if (isIfUnlessAndHalt(conditional.type, false)) { + halt = true + } + + // check counters and see if the execution + // has reached our concept of infinity, + // thus breaking the cycles + counter++ + return when { + conditional.type === Conditional.ConditionalType.WHILE + && counter > loops -> false + conditional.type === Conditional.ConditionalType.UNTIL + && counter >= loops -> false + else -> { + try { + evaluateCondition(conditional) + } catch (exception: RuntimeException) { + throw AraraException(messages.getMessage(Messages + .ERROR_EVALUATE_COMPILATION_FAILED), + exception) + } + } + } + } + + companion object { + // the application messages obtained from the + // language controller + private val messages = LanguageController + } +} |