summaryrefslogtreecommitdiff
path: root/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Evaluator.kt
diff options
context:
space:
mode:
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.kt122
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
+ }
+}