Custom Operators

For use as a DSL (Domain-Specific Languages), it is sometimes more convenient to augment Rhai with customized operators performing specific logic.

Engine::register_custom_operator registers a keyword as a custom operator, giving it a particular precedence (which cannot be zero).

Example


#![allow(unused)]
fn main() {
use rhai::Engine;

let mut engine = Engine::new();

// Register a custom operator '#' and give it a precedence of 160
// (i.e. between +|- and *|/)
// Also register the implementation of the customer operator as a function
engine.register_custom_operator("#", 160)?
      .register_fn("#", |x: i64, y: i64| (x * y) - (x + y));

// The custom operator can be used in expressions
let result = engine.eval_expression::<i64>("1 + 2 * 3 # 4 - 5 / 6")?;
//                                                    ^ custom operator

// The above is equivalent to: 1 + ((2 * 3) # 4) - (5 / 6)
result == 15;
}

Alternatives to a Custom Operator

Custom operators are merely syntactic sugar. They map directly to registered functions.


#![allow(unused)]
fn main() {
let mut engine = Engine::new();

// Define 'foo' operator
engine.register_custom_operator("foo", 160)?;

engine.eval::<i64>("1 + 2 * 3 foo 4 - 5 / 6")?;       // use custom operator

engine.eval::<i64>("1 + foo(2 * 3, 4) - 5 / 6")?;     // <- above is equivalent to this
}

A script using custom operators can always be pre-processed, via a pre-processor application, into a syntax that uses the corresponding function calls.

Using Engine::register_custom_operator merely enables a convenient shortcut.

Must be a Valid Identifier or Reserved Symbol

All custom operators must be identifiers that follow the same naming rules as variables.

Alternatively, they can also be reserved symbols, disabled operators or keywords.


#![allow(unused)]
fn main() {
engine.register_custom_operator("foo", 20)?;          // 'foo' is a valid custom operator

engine.register_custom_operator("#", 20)?;            // the reserved symbol '#' is also
                                                      // a valid custom operator

engine.register_custom_operator("+", 30)?;            // <- error: '+' is an active operator

engine.register_custom_operator("=>", 30)?;           // <- error: '=>' is an active symbol
}

Binary Operators Only

All custom operators must be binary (i.e. they take two operands). Unary custom operators are not supported.


#![allow(unused)]
fn main() {
// Register unary '#' operator
engine.register_custom_operator("#", 160)?
      .register_fn("#", |x: i64| x * x);

engine.eval::<i64>("# 42")?;                          // <- syntax error
}

Operator Precedence

All operators in Rhai has a precedence indicating how tightly they bind.

A higher precedence binds more tightly than a lower precedence, so * and / binds before + and - etc.

When registering a custom operator, the operator’s precedence must also be provided.

The following precedence table shows the built-in precedence of standard Rhai operators:

CategoryOperatorsBindingPrecedence (0-255)
Logic and bit masks||, |, ^left30
Logic and bit masks&&, &left60
Comparisons==, !=left90
Containment[inleft110
Comparisons>, >=, <, <=left130
Ranges.., ..=left140
Arithmetic+, -left150
Arithmetic*, /, %left180
Arithmetic**right190
Bit-shifts<<, >>left210
Unary operatorsunary +, -, !righthighest
Object field access.righthighest