The Formula Compiler

From expression to reactive function

When you type Ball.x + 100 in a formula field, the compiler transforms it into a dependency-tracked JavaScript function. This page explains how.


Pipeline

Source text → Acorn parse → AST transform → Astring codegen → Compiled function

  1. Parse — acorn parses the expression into an AST (Abstract Syntax Tree)
  2. Transform — node references become ctx.resolve() calls, method calls become ctx.callMethod() calls
  3. Codegen — astring generates JavaScript from the transformed AST
  4. Compile — the generated code is wrapped in a function with the formula context

Three compile modes

ModeInputOutputUsed for
formulaBall.x + 100Dependency-tracked expressionProperty formulas
eventthis.x += 10; event.target.destroy()Statement block with this and eventEvent handlers
methodthis.x = 0; this.y = 0Callable function with thisUser-defined methods

AST transforms

Node references

Ball.x → ctx.resolve('Ball', 'x')
parent.rotation → ctx.resolve('parent', 'rotation')
this.width → ctx.resolve('this', 'width')

The resolver looks up the node by name in the scene, registers a dependency, and returns the current value.

Method calls

this.reset() → ctx.callMethod('this', 'reset', [])
Ball.explode() → ctx.callMethod('Ball', 'explode', [])

Tag access

Tags are virtual nodes. The compiler handles bracket notation specially:

Tag.brick → ctx.resolve('Tag', 'brick') // returns VNode[]
Tag.brick.length → ctx.resolve('Tag', 'brick').length // count
Tag.brick[0].x → ctx.resolve('Tag', 'brick')[0].x // first brick's x

Compound assignments

this.x += 10 → ctx.set('this', 'x', ctx.resolve('this', 'x') + 10)
this.score++ → ctx.set('this', 'score', ctx.resolve('this', 'score') + 1)

Dependency extraction

During compilation, the transformer collects all resolve() calls. These become the formula's dependency list, used for:

  • Topological sort (evaluation order)
  • Dirty propagation (which formulas to re-evaluate)
  • Cycle detection (finding circular references)

Globals whitelist

These globals are available in all formula contexts without import: console, JSON, Math, setTimeout, setInterval, clearTimeout, clearInterval, parseInt, parseFloat, isNaN, isFinite, Number, String, Array, Object

Source maps

Compiled formulas include inline VLQ-encoded source maps and sourceURL labels. Open browser DevTools, set a breakpoint in a formula — the debugger shows your original expression, not the compiled output.


Example

You write:

clamp(mouse.x - Face.x, -20, 20) + 70

Compiler produces:

(function(ctx) {
  return clamp(ctx.resolve('mouse', 'x') - ctx.resolve('Face', 'x'), -20, 20) + 70
})
//# sourceURL=formula://LeftEye/x
//# sourceMappingURL=data:...

Dependencies extracted: ['mouse.x', 'Face.x']


Back to formulasReactive signalsEvents system

← НазадEvents & MethodsДальше →Reactive Formulas