The OpexFlow DSL is a tiny, statement-based language evaluated natively against the
in-memory event struct. It exists for the 90% of transforms that are filters and field
math, where spinning up a JS interpreter per event is pure waste.

> **Info**
>
> **Why a DSL.** Every JavaScript transform pays two taxes: **marshal** the event to a JS
> object and back (four serialize passes across the FFI boundary), then **interpret** the
> function. The DSL skips both. It compiles once at route load, then reads and mutates
> typed fields in place with no thread hop. It runs **15 to 50x** faster than the
> equivalent JS for simple transforms. For anything the DSL can't express (loops, regex,
> string munging), drop into [JavaScript](/docs/javascript-transforms).

DSL programs live inline (`script_inline`) or in a file (`script`), exactly where a JS
script would. Pick the backend with `backend: "dsl"`.

## Syntax overview

A program is a list of statements, **one per line**. Comments start with `#`. There are
exactly three statements:

```text
# a complete DSL program
when metadata.temp > 80 || metadata.vib > 5.0   # keep iff true; else DROP the record
set metadata.alert = true                 # mutate a field
set status.state = "alarm"                 # ... another field
```

| Statement | Meaning |
| --- | --- |
| `when <expr>` | Filter. The record is kept iff `<expr>` is true; otherwise it's dropped (nothing flows to sinks). At most one `when` per program. |
| `set <path> = <expr>` | Mutate a field. Targets: `metadata.*` (nested, auto-created), `status.state`, `status.alarmCode`, `status.alarmMessage`, `machine.id`, `machine.name`. |
| `drop` | Drop the record unconditionally. Equivalent to JS `return null`. |

If a program has no `when` and no `drop`, the (possibly mutated) record always passes
through to its sinks.

## Expressions & operators

| Operator | Description |
| --- | --- |
| `||` `&&` `!` | Boolean logic (or / and / not). |
| `== != < <= > >=` | Comparisons, return booleans. Numeric first, string ordering as fallback. |
| `+ - * /` | Arithmetic. `+` also concatenates two strings. |
| `( )` | Grouping. |

## Literals

```text
42   3.14             # numbers (float)
"running"            # string
true  false  null    # booleans, null
```

## Paths

Paths read directly from `CanonicalMachineData`. Dotted, with numeric segments
indexing arrays:

| Path | Reads |
| --- | --- |
| `source` | Origin protocol (`modbus`, `opcua`, `http`, …). |
| `machine.id` / `machine.name` | Stable machine id / display name. |
| `status.state` | `running` \| `idle` \| `alarm` \| `offline`. |
| `status.alarmCode` / `status.alarmMessage` | Active alarm (or null). |
| `spindle.speed` / `spindle.load` | Spindle readings (null if no spindle). |
| `axes.<i>.position` | Indexed axis position (also `.name`, `.unit`). |
| `metadata.<key>...` | Free-form nested JSON, where sensors like `temp`, `vib`, `rpm` live. |

## Examples

**Conditional alert (filter + flag)**

```text
when metadata.temp > 80 || metadata.vib > 5.0
set metadata.alert = metadata.temp > 80 || metadata.vib > 5.0
```

**Force alarm state on high load**

```text
when spindle.load > 90
set status.state = "alarm"
set status.alarmMessage = "spindle overload"
```

**Unit conversion + nested metadata**

```text
set metadata.temp_f = metadata.temp_c * 9 / 5 + 32
set metadata.flags.hot = metadata.temp_c > 60
```

**Pass only running machines**

```text
when !(status.state == "offline")
```

> **Info**
>
> **Tip.** Don't memorize this. Describe what you want in the
> [AI transform panel](/docs/ai-generator) and OpexFlow writes the DSL (or JS) for you,
> server-validated before it lands.

## DSL cheat sheet

```text
when expr            keep iff true
set path = expr      mutate field
drop                 drop the record
metadata.x           sensor values
status.state         run state
|| && !              boolean logic
== != > <            comparisons
+ - * /              arithmetic
# comment            one per line
"str" / true / null
```

## Try it: live REPL

Edit the record and the program, then run. This is an in-browser evaluator of the
documented DSL subset operating on the record object, no server round-trip. The
interactive REPL is available in the running connector's in-app docs; the reference
behavior is fully covered by the syntax, operators, and examples above.