Language Reference

Language implementation v0.9, documented against the v1.0 target specification. No prior DSL experience required.

Contents

  1. Overview
  2. Rule Structure
  3. Expressions & Operators
  4. Decision Types
  5. Data Types
  6. Assignments & Computed Values
  7. Lists & Pattern Matching
  8. Time & Velocity Limits
  9. Missing Values
  10. Determinism Contract
  11. Complete Examples
  12. Quick Reference

Overview

RuleDSL is a purpose-built language for writing business decision rules. For the published DET corpus, v1.0.2 produced byte-identical decisions on Windows x86_64 and Linux x86_64 when the engine/ABI, bytecode, canonical input, and evaluation options matched. This does not formally prove every possible rule and input.

A rule file contains one or more named rules. Each rule has a condition (when) and an action (then). Rules are evaluated in priority order. The first rule whose condition matches produces the decision.

Design philosophy: The engine decides. The host application computes. RuleDSL deliberately excludes aggregation, date arithmetic, and string manipulation. These belong in your application. The engine evaluates the fields you provide and returns a decision.

Rule Structure

Every rule follows this pattern:

rule rule_name priority 100 {
  when <condition>;
  then <actions>;
}

Components

PartRequiredDescription
ruleYesKeyword. Starts a rule block.
rule_nameYesIdentifier. Appears in decision results.
priority NNoHigher number = evaluated first. Default is 0.
whenYesCondition expression. Shipped v0.9 applies dynamic truthiness.
thenYesOne or more actions, separated by commas.
v0.9 condition typing: conditions are not restricted to Boolean values. Number is false when within the relative epsilon of zero; String/Ident is false when empty; Missing is false. Shipped v0.9 has no separate non-Boolean condition-type error. See the v0.9 conformance status.

Evaluation Order

Rules are evaluated by priority (highest first). Among rules with equal priority, source order applies. The first match wins — no further rules are evaluated after a decision is reached.

Expressions & Operators

Comparison Operators

OperatorMeaningExample
==Equalcurrency == "USD"
!=Not equalstatus != "blocked"
<Less thanamount < 100
<=Less than or equalamount <= 500
>Greater thanamount > 1000
>=Greater than or equalrisk_score >= 80
v0.9 cross-type comparisons: comparing different value types does not raise a type error. ==, <, <=, >, and >= return false; != returns true. Validate types in the host or an input schema before evaluation. See the v0.9 conformance status.

Logical Operators

OperatorMeaningExample
andBoth must be trueamount > 1000 and currency == "USD"
orEither can be truecountry == "US" or country == "GB"
notNegationnot is_verified

Use parentheses to control evaluation order: (a or b) and c

No short-circuit in v0.9: and and or evaluate both operands. divisor != 0 and amount / divisor > 1 is therefore not a safe division guard. Validate the divisor in the host or use separate rules. See the v0.9 conformance status.

Arithmetic Operators

OperatorMeaningExample
+Additionamount + fee
-Subtractionbalance - amount
*Multiplicationrate * 100
/Divisionamount / 100

Standard precedence: * and / bind tighter than + and -. Use parentheses when in doubt.

Operator Precedence (Highest to Lowest)

LevelOperatorsAssociativity
1not, unary -Right
2* /Left
3+ -Left
4< <= > >=Left
5== !=Left
6andLeft
7orLeft

Decision Types

allow

Approve the request. Transaction proceeds.

decline

Reject the request. Transaction is blocked.

review

Flag for manual review. Requires human decision.

limit

Return a velocity-cap decision for the host to enforce. E.g., max 200 USD per day.

# Simple decisions
then allow;
then decline;
then review;

# Velocity limit (requires now_utc_ms in input)
then limit 200 USD per 1 D;

Data Types

TypeLiteral ExampleDescription
Number1200.0, 1e6, 100 USDIEEE 754 binary64. Integer, decimal, and exponent forms. Optional currency tag.
String"USD", "hello"Double-quoted. Used in comparisons and MATCH.
Booleantrue, falseUsed in conditions directly.
Identifieramount, customer.levelReferences input fields. Dotted paths supported.
Missing—Field not provided. Operations can emit runtime diagnostics and AX_ERR_RUNTIME; see Missing Values.

Numeric Literals

Numbers support integer, decimal, and scientific notation:

when amount > 10000;        # integer
when rate > 0.05;           # decimal
when amount > 1e6;          # scientific notation (1,000,000)
when rate > 2.5e-3;         # decimal + exponent (0.0025)
v0.9 numeric comparisons use a relative epsilon: 1e-9 × max(1, |a|, |b|). Do not treat a threshold as exact IEEE equality at that boundary. Represent financial amounts in integer minor units where practical and add boundary acceptance tests. See the v0.9 conformance status.

Currency-Tagged Numbers

A 3-letter currency tag on a numeric literal is metadata in shipped v0.9. The engine compares the numeric value and does not automatically filter by currency, so add an explicit host-supplied currency condition:

when amount >= 10000 USD and currency == "USD";
when amount >= 8000 EUR and currency == "EUR";

Dotted Paths

Access nested fields with dot notation:

when customer.risk_level > 70 and merchant.category == "gambling";

Assignments & Computed Values

The then clause can set output fields alongside the decision. These appear in the result and can be used by downstream systems.

rule fraud_signal priority 100 {
  when amount > 3000 and ip_country in [NG, RU];
  then risk_score = 95,
       reason = "multi_signal_fraud",
       category = "high_risk",
       decline;
}

Assignments support arithmetic expressions:

then risk_score = (amount / 100) + velocity_1h, review;

Lists & Pattern Matching

IN Lists

Check if a value is a member of a list:

when ip_country in [TR, NG, RU, KP];
when channel in [ECOM, APP, WEB];

List items can be identifiers (bare words) or strings.

MATCH (Substring Matching)

Check if a text field contains a substring:

when email match "@example.com";
when card_bin match "41111";
Safety: Applying MATCH to a non-text field (e.g., a number) raises a runtime error (MATCH_EXPECTS_TEXT). Ensure the matched field is a string.

Time & Velocity Limits

The limit decision expresses a velocity cap (an amount and a per window). The engine is stateless: the host computes counters, aggregates, and time-window fields, supplies any values the rule evaluates, and enforces the returned cap. now_utc_ms is validated as present but is not used computationally in v0.9:

then limit 500 USD per 1 D;    # max 500 USD per day
then limit 3 TRY per 1 H;      # max 3 transactions per hour

Time Units

CodeUnit
SSecond
MMinute
HHour
DDay
Explicit clock: The engine never reads the system clock. In the C API, supply now_utc_ms as an explicit field. The shipped Python and C# wrappers accept either their explicit clock argument or the documented explicit field, but not both. Omitting both for a time-based rule raises MISSING_NOW_UTC_MS.

Missing Values

If a referenced field is not provided in the input, it resolves to missing. This is not a normal "safe skip" contract:

Validate required fields before evaluation; do not rely on missing-value behavior as control flow. See the v0.9 conformance status.

# Host must require customer.loyalty_tier before evaluating this rule
rule vip_bypass {
  when customer.loyalty_tier == "platinum";
  then allow;
}

Determinism Contract

The v0.9 language implementation controls implicit time, evaluation order, numeric mode, and locale inputs. For the published DET corpus, the shipped v1.0.2 Windows and Linux x86_64 binaries produced matching decisions under the same engine/ABI, bytecode, canonical input, and equivalent-option conditions. This is scenario evidence, not a formal proof over all rules and inputs:

No implicit time

The engine never reads the system clock. Time is explicitly injected via now_utc_ms.

Stable evaluation order

Rules are evaluated by priority, then source order. No randomness, no hash-map iteration artifacts.

IEEE 754 numeric behavior

Arithmetic uses binary64 and comparisons use v0.9's documented relative epsilon. Fast-math is rejected at build time; NaN and Inf follow the documented rejection paths.

No locale dependency

Identifiers use raw UTF-8 octet matching. No Unicode normalization. No collation surprises.

Verified scope: same shipped v1.0.2 engine/ABI + same bytecode + canonical input + equivalent options produced byte-identical decisions on Windows x86_64 and Linux x86_64.

Complete Examples

Transaction Risk Scoring

rule block_sanctioned priority 200 {
  when ip_country in [KP, IR, SY];
  then reason = "sanctioned", decline;
}

rule high_risk_geo priority 100 {
  when amount > 5000 and ip_country in [NG, RU];
  then risk_score = 90, reason = "geo_high", decline;
}

rule review_medium priority 50 {
  when amount > 1000 and amount <= 5000;
  then risk_score = 60, reason = "medium_amount", review;
}

rule allow_default {
  when true;
  then risk_score = 10, reason = "baseline", allow;
}

Host-Supplied Spending Inputs and Limit Decisions

The host supplies transaction, device, aggregate, and time-window values; these rules evaluate those inputs but do not maintain a counter, window, or database.

rule block_new_device_high priority 100 {
  when is_new_device and amount > 2000;
  then reason = "new_device_risk", decline;
}

rule limit_daily priority 50 {
  when amount > 1000;
  then reason = "daily_cap", limit 500 USD per 1 D;
}

rule trusted_device priority 10 {
  when card_present and not is_new_device;
  then reason = "trusted", allow;
}

rule default_review {
  when true;
  then review;
}

Email & Channel Controls

rule flag_internal_email priority 100 {
  # Host supplies a normalized domain for exact matching
  when email_domain == "internal.corp";
  then reason = "internal_email", review;
}

rule block_risky_channel priority 50 {
  when channel in [ECOM, APP] and amount > 1000;
  then reason = "risky_channel", decline;
}

rule allow_other {
  when true;
  then allow;
}

Quick Reference

Keywords

KeywordContext
ruleStarts a rule block
prioritySets evaluation order (optional)
whenBegins condition expression
thenBegins action clause
and or notLogical operators
inList membership check
matchPattern match on text fields
allow decline reviewDecision outcomes
limit perVelocity limit decision
true falseBoolean literals

Keywords are case-insensitive: WHEN, When, and when are equivalent.

Reserved Input Field

FieldTypeRequired For
now_utc_msNumber (epoch milliseconds)limit ... per rules

Further Reading