1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/*
 * DMNTK - Decision Model and Notation Toolkit
 *
 * FEEL and DMN model evaluator.
 *
 * Copyright 2018-2021 Dariusz Depta Engos Software <dariusz.depta@engos.software>
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

use self::errors::*;
use dmntk_feel_parser::dmntk_feel::context::FeelContext;
use dmntk_feel_parser::dmntk_feel::dmntk_common::{null_with_trace, Result};
use dmntk_feel_parser::dmntk_feel::values::Value;
use dmntk_feel_parser::dmntk_feel::{AstNode, FeelType, Scope};

mod eval;
mod eval_bif;

#[cfg(test)]
pub mod tests;

/// Evaluates a [Value] from given [AstNode].
pub fn evaluate(scope: &Scope, node: &AstNode) -> Result<Value> {
  self::eval::engine::eval(scope, node)
}

/// Evaluates the sum of specified values.
pub fn evaluate_sum(values: Vec<Value>) -> Value {
  self::eval_bif::core::sum(&values)
}

/// Evaluates the minimum value from specified values.
pub fn evaluate_min(values: Vec<Value>) -> Value {
  self::eval_bif::core::min(&values)
}

/// Evaluates the maximum value from specified values.
pub fn evaluate_max(values: Vec<Value>) -> Value {
  self::eval_bif::core::max(&values)
}

/// Evaluates the type of the AST node.
pub fn evaluate_node_type(scope: &Scope, node: &AstNode) -> FeelType {
  node.type_of(scope)
}

/// Compares two values and returns `true` when the two `FEEL` values are equal.
pub fn evaluate_equals(left: &Value, right: &Value) -> bool {
  self::eval::engine::eval_ternary_equality(&left, &right).unwrap_or(false)
}

/// Evaluates function definition.
pub fn evaluate_function(scope: &Scope, arguments: &FeelContext, function: &Value) -> Result<Value> {
  if let Value::FunctionDefinition(_, body) = function {
    return self::eval::engine::eval_function_definition_instance(scope, arguments, body);
  }
  Ok(null_with_trace!("expected function definition value as an input"))
}

/// Evaluates a context.
pub fn evaluate_context(scope: &Scope, input: &str) -> Result<FeelContext> {
  if let Ok(node) = dmntk_feel_parser::parse_context(&scope, input, false) {
    if let Value::Context(context) = self::eval::engine::eval(&scope, &node)? {
      return Ok(context);
    }
  }
  Err(expected_context())
}

/// Definitions of errors reported by `FEEL` evaluator.
mod errors {
  use dmntk_feel_parser::dmntk_feel::dmntk_common::DmntkError;

  #[derive(Debug, PartialEq)]
  pub enum FeelEvaluatorError {
    ContextHasNoValueForKey(String),
    ExpectedContext,
  }

  impl From<FeelEvaluatorError> for DmntkError {
    fn from(e: FeelEvaluatorError) -> Self {
      DmntkError::new("FeelEvaluatorError", &format!("{}", e))
    }
  }

  impl std::fmt::Display for FeelEvaluatorError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
      match self {
        FeelEvaluatorError::ContextHasNoValueForKey(name) => {
          write!(f, "current context contains no value for key: {}", name)
        }
        FeelEvaluatorError::ExpectedContext => {
          write!(f, "expected context as input")
        }
      }
    }
  }

  pub fn context_has_no_value_for_key(key: String) -> DmntkError {
    FeelEvaluatorError::ContextHasNoValueForKey(key).into()
  }

  pub fn expected_context() -> DmntkError {
    FeelEvaluatorError::ExpectedContext.into()
  }
}