Writing Reval rules

Intro

Reval is a library (or crate as libraries are called in Rust, the language that Reval is written in) for managing, interpreting and executing rules. This document is targeted at people writing rules for Reval. It describes the syntax and structure of the rules. This document does not describe details of a specific Reval implementation. Any information on the structure of input data, output data or extra functions provided by any system that uses Reval should be described in the documentation of that system.

Concepts

Rules

Rules are pieces of text describing how to process input data to generate a required output. How the input and output data is structured depends depends on your particular Reval implementation. Each rule has a name, this name is used to identify the rule in error messages and other output and should be unique in a ruleset. Each rule also contains an expression, this is the actual rule that will be evaluated to generate the output.

Rules can be written in one of two languages. The Reval language is the simpelest and most concise language to write rules. A simple rule written in the Reval language looks like this;

// Example rule
input_field >= i5

Rules can also be written in a language based on Json, this used to be the only way to write rules and is still available for backward compatibility. Parsing Json rules will be deprecated in version 0.8.0 of Reval and the Json rules parser will be removed in 0.9.0. The same rule that was specified above looks like this in Json, its quite a bit more verbose and harder to look at;

{
    "name": "Example rule",
    "expr": {"gte": [{"ref": "input_field"}, {"int": "5"}]}
}

The rule has the name "Example rule" and compares the value in input_field with the integer 5. It will return true if it is greater than or equal to 5 and false otherwise. This rule will return an error if input_field does not contain a value with an integer type.

Values and data types

Values can either be passed in to the the rules as input data or specified in the rule itself. Values passed in to the rules are named and can be used in rules by specifying the name similar to input_field in the example rule above. The value i5 in the example rule is a value that is specified in the rule itself. The prefix i specifies that the value has the Integer type, and 5 specifies the value.

Reval supports a range of data-types. The String type for text, a number of numeric types, a boolean type and a

TypeExampleDescription
String"this is a String"iValues of the string type contain text. String values are delimited with " characters.
Integeri5This is a numeric type that can contain whole values. Integers can be positive or negative. Integer values are prefixed with i in rules
Floatf.14e-5Float is a numeric type that contains floating point values. Float types can contain fractional values, positive or negative. Float values are prefixed with f
Decimald14.25Decimal is a numeric type that can contain fractional values. This data-type is similar to Float has different trade-offs. Decimal is typically used for monetary values. Decimal values are prefixed with d
Booleanfalse or trueThe Boolean data type represent truth values and can be true or false
NonenoneNone is used to indicate that a value does is not set or unspecified.
Vec[i4, i6, i12]Vec values contain a list or vector of other values.
Map{one: i1, two: i2}Map values map names to values, They are similar to a vec but every value in the list has a name that can be used to access that value

Operators

todo

Built in Functions

todo

User-Functions

A ruleset can have custom functions that allow rules to access functionality provided by the software that embeds the Reval rules-engine. Functions have a name, take a value as their input and return a value as output. Depending on how the function is implemented the output can be cached. So calling it multiple times might not incur a performance impact.

Json rules

Rules written in json will be deprecated in version 0.8.0 and support will be removed in version 0.9.0 in favor of rules written in the Reval DSL.

A rule is written as a json structure with a name and an associated expression. The name is what identifies the rule and should be unique in the rule-set. The expression is what will be evaluated. The expression is a nested tree-structure. At the root is the first expression that will be evaluated. Expressions can have parameters and depending on the expression these parameters can themselves be expressions.

{
    "name": "Example rule",
    "expr": {"string": ""}
}

Expressions

Expressions are written as a key-value pair in json, the key is the name of the expression and the value is either one parameter if the expression only takes one parameter. In most cases expressions take more than one parameter so the value will be a list.

Expression names are in lower-case.

Value Expressions

The simpelest expressions are value expressions. Reval values can have different types and there is one value expression for each type. The primitive types that Reval uses are String, Int, Decimal, Float and Bool and there are expressions for each of these;

{ "string": "This is a string value" }
{ "int": -5 }
{ "decimal": 5.132 }
{ "float": 5.15e28 }
{ "bool": true }

None value

none is a special value used to indicate that a value is not available. It can be used when passing values into a rule to indicate a missing value for example.

{"none"}

The expressions is_some and is_none can be used to test for none values. These are documented with the Special Expressions.

Comparison Expressions

Reval supports a number of comparison expressions that can be used to compare values. Al these expressions result in a boolean value.

Eq and neq

Eq is short for "equals" and compares equality. It returns true when the two parameters are exactly the same

{ "eq": [{"int": 5}, {"ref": "input"}] }

Neq is short for "not equal" and is the inverse of the eq expression. Compares equality of two parameters and returns false if they are the same.

{ "neq": [{"int": 5}, {"ref": "input"}] }

Special expressions

There are a couple of special expressions that allow express

Ref

The ref expression allow rules to access fields from input data by name

{"ref": "input_field_name"}

Idx

The idx expression indexes fields from map or vec values.

Fields from a vec can be indexed by number.

{"idx": [{ "ref": "some_vec_value" }, { "int": 5 } ]}

Fields from maps are indexed by string.

{"idx": [{ "ref": "some_map_value" }, { "string": "sub-field" } ]}

None checks

The is_some and is_none expressions test for none values.

is_some checks for none values and returns true if the expression returns a value that is not equal to none.

{"is_some": {"string": "some value"}}

This returns true because {"string": "some value"} is not none

is_none checks for none values and returns true if the expression returns a value that is equal to none.

{"is_none": "none"}

This returns true

Gt, gte, lt and lte

Gt is short for "greater than" it compares two numeric values of the same type, lt is "less than" and is the inverse. Gte and lte are the inclusive versions of these operations. They are short for "Greater than or equal" and "Less than or equal"

{ "gt": [{"int": 5}, {"ref": "input"}] }
{ "gte": [{"int": 5}, {"ref": "input"}] }
{ "lt": [{"int": 5}, {"ref": "input"}] }
{ "lte": [{"int": 5}, {"ref": "input"}] }

Logic expressions

Logic expressions perform logic operations on boolean values. These can be used to combine results from comparison expressions for example. Logic expressions take one or more boolean expressions as parameters and return a boolean value when evaluated.

Not

The not expression inverts a boolean value. So true becomes false and false becomes true.

{ "not": { "bool": false } }

And

The logical and expression takes two sub-expressions as parameters and only evaluates to true if both parameters evaluate to true.

{
    "and": [ 
        { "gte": [{ "ref": "input_1" }, { "int": 5 }]},
        { "gte": [{ "ref": "input_2" }, { "float": 15.9 }]}
    ]
}

Or

The logical or expression takes two sub-expressions as parameters and only evaluates to false if both parameters evaluate to false.

{
    "or": [ 
        { "gte": [{ "ref": "input_1" }, { "int": 5 }]},
        { "gte": [{ "ref": "input_2" }, { "float": 15.9 }]}
    ]
}

Calculation

  • Add
  • Sub
  • Mul
  • Div

Conversions

Reval has three expressions that allow conversions between numeric types, each of these takes one expression as a parameter.

  • CFloat
  • CInt
  • CDecimal

Reference

A reference guide to quickly find Reval expressions

Expressions

ExprDescription
stringConstruct a simple string value
intConstruct a simple int value
floatConstruct a simple float value
decimalConstruct a simple decimal value
boolConstruct a simple boolean value
mapConstruct a map value
vecConstruct a vec value
notLogical inversion, converts true to false and false to true
andLogical and, is true only when all parameters evaluate to true
orLogical or, is false only when all parameters evaluate to false
negNumeric inversion, makes positive values negative and negative values positive
addAddition, adds up numeric values
subSubtraction, subtracts numeric values
mulMultiplication, multiplies numeric values
divDivision, divides numeric values
ifControl flow expression, if takes three expressions as parameters. If the first expression evaluates to true the if expression will take the value of the second or then expression. If the first expression evaluates to false the if expression will take the value of the third or else expression.
refRef references a field from the input
idxAccess fields in map or vec types. idx indexes fields of maps with strings and vec fields can be indexed using integer values
funcCall a custom fuction by name
cintConvert a value to an integer
cfloatConvert a value to float
cdecimalConvert a value to decimal