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
Type | Example | Description |
---|---|---|
String | "this is a String"i | Values of the string type contain text. String values are delimited with " characters. |
Integer | i5 | This is a numeric type that can contain whole values. Integers can be positive or negative. Integer values are prefixed with i in rules |
Float | f.14e-5 | Float is a numeric type that contains floating point values. Float types can contain fractional values, positive or negative. Float values are prefixed with f |
Decimal | d14.25 | Decimal 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 |
Boolean | false or true | The Boolean data type represent truth values and can be true or false |
None | none | None 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
Expr | Description |
---|---|
string | Construct a simple string value |
int | Construct a simple int value |
float | Construct a simple float value |
decimal | Construct a simple decimal value |
bool | Construct a simple boolean value |
map | Construct a map value |
vec | Construct a vec value |
not | Logical inversion, converts true to false and false to true |
and | Logical and, is true only when all parameters evaluate to true |
or | Logical or, is false only when all parameters evaluate to false |
neg | Numeric inversion, makes positive values negative and negative values positive |
add | Addition, adds up numeric values |
sub | Subtraction, subtracts numeric values |
mul | Multiplication, multiplies numeric values |
div | Division, divides numeric values |
if | Control 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. |
ref | Ref references a field from the input |
idx | Access fields in map or vec types. idx indexes fields of maps with strings and vec fields can be indexed using integer values |
func | Call a custom fuction by name |
cint | Convert a value to an integer |
cfloat | Convert a value to float |
cdecimal | Convert a value to decimal |