/
Expressions Language (Formula Evaluator Library)

Expressions Language (Formula Evaluator Library)

Formula Evaluator is a utility to evaluate simple formulas with binding variables.

Usage

Map binding = [a:1, b:2, c:3, map:[property1: 1], list: [1,2,3] ] String formula = " a + b * 2" def result = libs.FormulaEvaluator.Builder.setFormula(formula).setBinding(binding).eval() // = 5

Supported Operators, Functions and Symbols

Operator

Purpose

Samples

Operator

Purpose

Samples

+

Arithmetic – addition

a + b

-

Arithmetic – subtraction

a - b

*

Arithmetic – multiplication

a * b

/

Arithmetic – division

a / b

%

Arithmetic – modulo

a % b

**

Arithmetic – power

a ** b

==

Relational – equal

a == b

true == true

!=

Relational – different

a != b

>

Relational – greater than

a > b

<

Relational – less than

a < b

>=

Relational – greater than or equal

a >= b

<=

Relational – less than or equal

a <=b

!

Logical "not"

!a

!true

&&

Logical "and"

a && b

true && false

||

Logical "or"

a || b

true || false

AND

Logical "and"

a AND b

OR

Logical "or"

a OR b

IN

Logical IN

a IN [ 1, 2, 3]. Check if the left value is in the right value

IF

Function

IF (condition, return when true, return when false)

IS_NULL

Function

IS_NULL (value to check null) return true/false

SIZE

Function

SIZE (string or array to get size) return size in integer

AS_STRING

Function

AS_STRING (string) Cast parameter to a string

AS_NUMBER

Function

AS_NUMBER(string or number). Convert parameter to the number

VAR

Function

VAR("String as variable"). Consider the parameter as a variable and lookup for its value from the binding list

( )

Brackets/parentheses – to modify the order of evaluation

a * ( b + c )

. (dot)

Property access

map.property1 + 2

?.

Safe property access

map?.property1 + 2. Check the target availability before getting the property, and return null when the target object is unavailable.

[ ]

Property access, list index access

list[0] or map['property1']

[item1, item2]

Literal list

Define a list, eg.  a in [1,2,3 ]

?:

Elvis operator

a ?: 1

? :

Ternary operator

a > 0 ? a : b

Cache

For better performance, the evaluator uses api.global to cache the parsed formula and the evaluated result for expression, so it is required to set api.retainGlobal = true.

Binding

A binding map provides the value of the variables that are used in the formula.

Custom Function

Besides the predefined functions (IF, AS_NUMBER, AS_STRING, IS_NULL, SIZE, VAR), you can define your custom function using libs.FormulaEvaluator.Builder.addFunctionOperator(String functionName, Closure executor, Integer minParamRequired = null, Boolean evaluatedParam = true, Boolean constantAnalyze = true)

Let's build a custom function named  LOG_INFO for logging.



Example 1 - custom function without param
Closure logInfoExecutor = { -> api.logInfo("Logging from FormulaEvaluator expression") } libs.FormulaEvaluator.Builder.addFunctionOperator("LOG_INFO ", logInfoExecutor) String expression = "LOG_INFO()" libs.FormulaEvaluator.Builder.setFormula(expression) .eval() // Run and check the server log for "Logging from FormulaEvaluator expression"



Example 2 - custom function with param
Closure logInfoExecutor = { String message, def object-> api.logInfo(message, object) } libs.FormulaEvaluator.Builder.addFunctionOperator("LOG_INFO", logInfoExecutor, 2) String expression = "LOG_INFO('Expression message', 2)" libs.FormulaEvaluator.Builder.setFormula(expression) .eval() // Run and check server log for "Expression message: 2"



Function Configurations

  • functionName – Name of the function that can be used in the expression.

  • executor – Closure that defines how the function should be evaluated. The executor should accept the parameters as provided in the expression.

    • delegation – Context Map is set as executor delegation and can be accessed inside the executor by getDelegate(), e.g. Map context = getDelegate() as Map. The context map contains:

      • node – Current node is being evaluated. This is useful when you want to access node properties, e.g. expression, type, token, etc.

      • operator – Operator configuration that is being evaluated.

      • bindings – Binding list that is provided to the evaluator.

      • cache – Evaluation cache that is provided to the evaluator.

  • minParamRequired – Defines the minimum parameters that are required by the executor. The evaluator will raise an exception if the actual count of parameters provided in the expression is less than this parameter.

  • constantAnalyze – Defines if the function node should be analyzed as a constant node by the parser. A constant node evaluated result will be cached in the global cache and will be shared for all expressions to improve the performance. Basically, string, number, true, false, and null values will be analyzed as constant nodes. Any operators with all constant nodes as children will also be considered as a constant. The none constant nodes evaluated results are still cached within the evaluation session.

Evaluation Configurations 

The evaluation configurations provide some conveniences while writing expressions. However,  they drive the parser & evaluator's normal behavior, then might end up with some performance issues.  You should use it with caution.

String Expression Binding Lookup (stringExpressionBindingLookup)

A string can be used as a variable, and therefore the library will look up in the provided bindings for a value.

This behavior leads to a counter-intuitive expression ( see https://pricefx.atlassian.net/browse/PFPCS-4771). So from version 1.1.5, this behavior is changed and the string will not be looked up for variable value by default. A default function named VAR is provided if you need to look up a value from the provided bindings by a key.

There is a configuration that allows you to enable the string expression binding lookup behavior and it can be set by using libs.FormulaEvaluator.Builder.setStringExpressionBindingLookup(true|false).

String expression binding lookup enabled



String expression binding lookup disabled by default

By enabling the string expression binding lookup configuration, you cannot use the VAR default function.

VAR() vs String expression binding lookup enabled



Dot Access Expression Binding Lookup Fallback (dotAccessExpressionBindingLookupFallback)

This configuration allows you to use the property access expression, e.g. "someObject.property" to look up in the provided binding for a value as a fallback when the left node (someObject in our case)  has a null evaluated result.

The configuration can be set by using libs.FormulaEvaluator.Builder.setDotAccessExpressionBindingLookupFallback(true|false).

Dot Access Expression Binding Lookup Fallback



Error Handling

When the expression is not valid, the system will return the feature name and error codes that help with troubleshooting:

The error message structure: FEATURE_NAME-ERROR_CODE::ERROR_MESSAGE

Feature 

Name

Feature 

Name

Tokenizer

FEA_TOKENIZER_ERR

Parser

FEA_PARSER_ERR

Operator

FEA_OPERATOR_ERR

Evaluator

FEA_EVALUATOR_ERR



Error code

Context

Invalid expression example

Error message example

Error code

Context

Invalid expression example

Error message example

INVALID_EXPRESSION

  • The expression is empty

  • The expression is not valid (incomplete expression)

a + b)

FEA_PARSER_ERR-INVALID_EXPRESSION::Invalid expression at position 5

UNEXPECTED_EXPRESSION

  • The expression is not present in the formula

a > b ? a :

FEA_PARSER_ERR-UNEXPECTED_EXPRESSION::An expression is expected at position 10, but found ':'

INVALID_BINARY_EXPRESSION

  • The left and right expressions are not present when creating a binary node

TBD



INVALID_FUNCTION_EXPRESSION

  • The function expression is not valid

IF(a > b, a, , )

FEA_PARSER_ERR-INVALID_FUNCTION_EXPRESSION::Invalid function IF expression

FUNCTION_NAME_ALREADY_DEFINED

  • The function is already defined in the function operator config

TBD



FUNCTION_NAME_REQUIRED

  • Missing function name

TBD



FUNCTION_EXECUTOR_REQUIRED

  • Missing function executer

TBD



INVALID_OPERATOR_PARAM

  • Missing function parameters | the number of parameters does not meet the minimum required by the function

AS_NUMBER()

FEA_EVALUATOR_ERR-INVALID_OPERATOR_PARAM::The number of parameters must be at least 1, but there are only 0 parameter(s)

INVALID_PROPERTY_ACCESS_EXPRESSION

  • Accessing function properties

map.IF(a > b, 1, 2)

FEA_PARSER_ERR-INVALID_PROPERTY_ACCESS_EXPRESSION::Invalid access property

INVALID_CHARACTER

  • Can't find the expected character in the tokenizer

TBD



INVALID_NUMBER

  • Input decimal number has more than 1 dot (".") characters

1 + 4.400.

FEA_TOKENIZER_ERR-INVALID_NUMBER::Invalid number 4.400. at position 9

INVALID_SYMBOL

  • The formula contains unsupported operators

1+=1

FEA_TOKENIZER_ERR-INVALID_SYMBOL::Unknown symbol = at position 2

INVALID_STRING

  • The string does not have a close quote

'TEST' + 'CASE

FEA_TOKENIZER_ERR-INVALID_STRING::Invalid string CASE at position 14

INVALID_VARIABLE

  • The variable is not defined in the binding

a > 1 ? a : 1

FEA_OPERATOR_ERR-INVALID_VARIABLE::Variable 'a' not defined





Related content

Formula Evaluator Library
Formula Evaluator Library
More like this
Add and Define Element
Add and Define Element
More like this
Configure Element Expression
Configure Element Expression
More like this
Configure Element Expression
Configure Element Expression
More like this