Math Expressions
The math expression language lets you compute derived values anywhere a number is expected — in math parameters on Definitions, in CID cells, in custom-view filter and accent expressions, and soon in KPI plugins.
The language is small on purpose. It's deterministic, has no side effects, and runs identically in two implementations:
- TypeScript (
@race/math-engine) — used by the API - Dart (
apps/client/lib/src/math/) — used by the Flutter client
Both pass the same 113-test corpus. If something works in one, it works in the other.
Quick example
PaceToLeader = LapTimeMs - Session.BestLapTimeMs
WeightDistribution = (CornerWeightFL + CornerWeightFR) / Total * 100
TyreLifeRemaining = If(NewMileage > 0, 100 * (1 - Mileage / NewMileage), 0)
Syntax
The parser is a Pratt parser supporting:
- Number literals:
42,3.14,1.6e3 - String literals:
"text"or'text' - Boolean:
true,false - Identifiers and member access:
Session.BestLapTimeMs,Lap.parameters.BrakeTemp - Function calls:
Min(a, b),Avg(samples) - Operators:
+ - * / %, comparison< <= > >= == !=, logical&& ||, unary! - - Parentheses for grouping
There are no statements, no assignments inside an expression, no loops, no closures. Everything is a single expression that evaluates to a value.
Scope traversal
Math expressions resolve identifiers against a hierarchical scope.
When you write Session.BestLapTimeMs inside a Lap-scope
expression, the evaluator walks from Lap → its parent Session and
looks up BestLapTimeMs. Available scopes are documented per
entity (Lap can see Run / RunSheet / Session / Car / Event, etc.).
Units
A typed parameter can declare a dimension and defaultUnit.
When the math evaluator pulls a parameter value, it can attach the
unit metadata so subsequent operations are unit-aware. In v0.1.0
this is opt-in — see the note under
Units and Dimensions for how
to switch it on.
Available functions
There are 35 built-in functions today
(packages/math-engine/src/functions.ts). Highlights:
- Aggregations —
Min,Max,Sum,Avg,Mean,Median,StdDev,Count,First,Last,BestX - Numeric —
Round,Floor,Ceil,Abs,Sqrt,Pow,Log - Logic —
If(cond, then, else),And,Or,Not,IfNull - Lookup / interpolation —
Lookup,LookupClosest,Interpolate,LinFit,Filter - Text —
Concatenate,Substring,Length,ToUpper,ToLower,Contains,Split,TextJoin - Conversion —
ToNumber,ToText,FormatNumber
See Math Expression Language for the full list with signatures and examples.
The KPI engine in packages/kpi-engine adds telemetry-specific
intrinsics — Channel, Window, RollingAvg, GateAbove,
GateBelow, EdgeCount, Delta, Where, etc. See
KPI Expression Language
for that catalogue.
Plugin-extensible
Math functions can be added by a plugin's math interface.
The plugin declares the function names + arity; the host calls
the plugin's call(name, args, ctx). The seeded
rolling-avg-rpm plugin exposes a single rollingAvgRpm(samples, windowSec) function this way.
See Writing a Plugin.
What you can do today
- Author math parameters in Admin → Definitions
(
/admin/definitions) — there's a live evaluator dialog that parses your expression and evaluates it against an editable sample-scope JSON. - Author CID cell expressions in Admin → Custom Views
(
/admin/custom-views) — the CID editor supports cell-level math expressions. - Author single-table
filterandrowAccentexpressions in the custom-view editor.
What's coming
- KPI plugin extensions surfaced in autocomplete — KPI intrinsics already live alongside math functions in the engine layer; the editor's autocomplete still lists math functions only.
- Scope-aware autocomplete — the Definition editor's math field autocomplete only shows local parameters today. Wiring it to show parent-scope parameters is in the queue.
- Inline error markers — currently parse errors show in the evaluator dialog; pushing them inline into the editor is pending.