The throwaway index the checks share, built once per Statifier.Validator.validate/2
call and discarded afterward. It must never be returned, cached, or handed
to the compiler: Statifier.Compiler builds the real interned index, and
sharing this one would couple two layers the architecture keeps
independent. Correctness over speed here, per the bead's own design note.
states maps a named state's id to its struct. ancestors maps a named
state's id to the list of its named ancestors' ids, itself excluded -
descendant?/3 turns that into a list-membership test rather than a
re-walk. The list carries no order contract: membership is the whole of
what anything reads, and walk/3 builds it innermost-first to avoid an
append per tree level. A state with a nil id cannot be named by anything,
so it is absent from both maps.
parents maps every state struct (nameless ones included) to its
immediate parent - another State.t(), or the Document.t() for a
top-level state - which is why it is keyed by the struct itself rather
than by id.
transitions is the flat [{transition, owner}] list: every
<transition> in the document, tagged with the slot it
came from. owner is {:plain, state} for a state's or parallel's own
transitions, {:initial, state} for the transitions inside that state's
<initial> element, and {:history, state} for a :history state's own
transitions (its default-transition candidates). Checks 2, 4, and 5 all
consume this one traversal instead of each re-walking the tree.
invoke_content_markup? mirrors Statifier.compile/2's
invoke_content_markup option (ADR-0042), defaulting to false. It is
not a general validation off-switch: Statifier.Validator.Checks.Boilerplate
is the only check that reads it, and only to accept a root that resolves
to no namespace at all, never a root that declares a wrong one.
Summary
Functions
Walks document once, threading ancestry as it descends and collecting
the shared transition list. source is the binary document was parsed
from; it is stored verbatim for checks that need Location.slice/2
and is not otherwise read here.
Whether ancestor_id is one of descendant_id's named ancestors.
Types
@type owner() :: {:plain, Statifier.Document.State.t()} | {:initial, Statifier.Document.State.t()} | {:history, Statifier.Document.State.t()}
@type t() :: %Statifier.Validator.Context{ ancestors: %{optional(String.t()) => [String.t()]}, invoke_content_markup?: boolean(), parents: %{ required(Statifier.Document.State.t()) => Statifier.Document.State.t() | Statifier.Document.t() }, source: binary(), states: %{optional(String.t()) => Statifier.Document.State.t()}, transitions: [{Statifier.Document.Transition.t(), owner()}] }
Functions
@spec build(document :: Statifier.Document.t(), source :: binary(), opts :: keyword()) :: t()
Walks document once, threading ancestry as it descends and collecting
the shared transition list. source is the binary document was parsed
from; it is stored verbatim for checks that need Location.slice/2
and is not otherwise read here.
opts defaults to []; invoke_content_markup: true sets the
same-named boolean field, per Statifier.Validator.validate/3.
@spec descendant?( context :: t(), ancestor_id :: String.t(), descendant_id :: String.t() ) :: boolean()
Whether ancestor_id is one of descendant_id's named ancestors.