A compiled <if> executable-content node (spec 4.3, 4.4, 4.5) - the
interned counterpart to Statifier.Document.If. branches is the
document-order partition list: each Statifier.Machine.Content.If.Branch
holds its compiled cond (nil for the <else> branch - spec 4.5.1:
"<else> ... is equivalent to an <elseif> with a 'cond' that always
evaluates to true"), the diagnostic span that cond compiled against, and
the dense c_index list assigned to that partition's own content
(Statifier.Compiler's Decision 2), resolved through
Statifier.Machine.content/2 at runtime rather than carried inline.
Why this node's execute/2 does not recurse through the block runner
Statifier.Interpreter.Content's block-running function is not called
from here, for four reasons:
- That function returns a bare
Statifier.MachineState.t(), not aStatifier.ExecutableContent.Context.t()- it builds a fresh evaluator context for its own call and discards it on return, so an<assign>inside a selected partition could never thread its rebuilt datamodel context back to a node after the</if>in the same enclosing block. SCION'sif_else/test0requires exactly that visibility, so recursing through that function would be wrong here, not merely inelegant. - That function is the sole site that turns a node's bare
{:error, _}into the platform's own execution-failure notification (ADR-0003). Recursing through it would make this file an indirect raiser of that notification, which the structural sweep incontent_acceptance_test.exsexists to catch - this file must never mention that notification's own name, nor construct one of the structs the platform uses to carry it. - That function emits one trace effect per call, carrying an
owner- an<if>is not a block owner and has noownervalue of its own to give it. - It would make this file depend on
Statifier.Interpreter, reentering the very protocol dispatch this file is itself a leaf of.
So execute/2 folds the selected branch's own c_index list directly:
resolve each through Statifier.Machine.content/2, dispatch through
Statifier.ExecutableContent.execute/2 - the same two-line
resolve-then-dispatch shape the block runner's own per-node step takes -
accumulating effects in document order and halting on the first failure.
This ~dozen-line fold is deliberately duplicated rather than shared: every
candidate shared home either reintroduces reason 4's layering inversion or
turns a runner-only helper into one whose only other caller is a leaf.
Its Statifier.ExecutableContent implementation lives right below the
struct: this file is the whole node, top to bottom, with no dispatcher
anywhere else in the tree.
Summary
Types
@type t() :: %Statifier.Machine.Content.If{ branches: [Statifier.Machine.Content.If.Branch.t()], c_index: non_neg_integer(), location: Statifier.Parser.Location.t() }