Statifier.Machine.State (Statifier v2.0.0)

Copy Markdown View Source

One compiled <scxml>, <state>, <parallel>, <final>, or <history> element - the interned, indexed counterpart to Statifier.Document.State (and, for index 0, to Statifier.Document itself).

kind :: :scxml | :state | :parallel | :final | :history. Unlike the Document layer, :scxml is a real kind here: the root is state index 0, parent: nil, id: nil (Statifier.Machine's moduledoc), so LCCA and get_transition_domain need no root special case.

atomic?/compound? are not stored - Statifier.Machine.atomic?/2 and compound?/2 derive them from children, the same divergence-avoidance reasoning Statifier.Document.State already states for why kind stays the element kind.

Fields that are kind-scoped

FieldMeaningful on
idany (nilable except it is always nil on :scxml)
parentany (nil only at index 0)
lastany - the high end of this state's self-inclusive descendant range, index being the low end
childrenany ([] on an atomic state)
initial:state, :scxml - [] on :parallel, :final, :history, and any atomic state
history_type:history
history_childrenany compound/parallel state - its own direct :history children, so exit-time recording finds them without scanning
transitions:state, :parallel, :history - the state's own selectable transitions' t_index list
onentry / onexitany - [Machine.Block.t()], one entry per <onentry>/<onexit> element the state wrote
initial_transition:state, :scxml - the <initial> element's transition t_index, or nil
history_default:history - its default transition's t_index
donedata:final - the compiled Machine.Donedata.t(), or nil
data:state, :parallel, :scxml - the d_indexes this state's own <datamodel> declares, [] when it has none
invoke:state, :parallel - the compiled Machine.Invoke.t() list this state's own <invoke> children produced, in document order, [] when it has none
attribute_locationsany - the owning element's own written-attribute spans, carried verbatim

Every field in the table except donedata, data and invoke is written in one pass: the compiler's state-interning walk builds each %Statifier.Machine.State{} whole, index and t_index/c_index references included - attribute_locations among them, carried straight from the source node at both construction sites. Those three are the exception, each folded on after that walk by its own helper (Statifier.Compiler's with_donedata/2, with_data/2 and with_invoke/2) once its own compilation pass has run, so they are the only fields whose values are not known when the struct is first built.

attribute_locations

attribute_locations is the owning Statifier.Document.State's own map, carried through unchanged rather than distilled into per-attribute *_location fields - the escape hatch Statifier.Machine.Invoke's moduledoc describes, applied here because id, initial and a history's type each have an attribute-level diagnostic use and none pays for a field of its own.

At index 0 the source is %Statifier.Document{} itself, not a Statifier.Document.State, exactly as location at index 0 is the document's own - so the root's map holds the <scxml> element's written attributes (initial, name, datamodel, binding, version, xmlns), and is %{} only for a document that wrote none of them.

The map keeps Statifier.Document's key-presence contract verbatim: an entry exists only for an attribute the author actually wrote. A :history state whose history_type is :shallow because lowering applied the default carries no :type key; one that wrote type="shallow" does. %{} means the element wrote no attributes at all.

Summary

Types

t()

@type t() :: %Statifier.Machine.State{
  attribute_locations: Statifier.Document.attribute_locations(),
  children: [non_neg_integer()],
  data: [non_neg_integer()],
  donedata: Statifier.Machine.Donedata.t() | nil,
  history_children: [non_neg_integer()],
  history_default: non_neg_integer() | nil,
  history_type: :shallow | :deep | nil,
  id: String.t() | nil,
  index: non_neg_integer(),
  initial: [non_neg_integer()],
  initial_transition: non_neg_integer() | nil,
  invoke: [Statifier.Machine.Invoke.t()],
  kind: :scxml | :state | :parallel | :final | :history,
  last: non_neg_integer(),
  location: Statifier.Parser.Location.t(),
  onentry: [Statifier.Machine.Block.t()],
  onexit: [Statifier.Machine.Block.t()],
  parent: non_neg_integer() | nil,
  transitions: [non_neg_integer()]
}