StatifierBlocks.Compiler (StatifierBlocks v0.1.0)

Copy Markdown View Source

The one-way compile: a block document plus a palette in, one artifact out (ADR-0004 decisions 1-4, 6-7).

compile/3 is a total function of {document, palette}. No process state, no global registry, no IO, no clock - the same purity ADR-0002 decision 4 imposes on the callbacks, imposed on the pipeline that calls them. It returns {:ok, %StatifierBlocks.Compiled{}} or {:error, [%StatifierBlocks.Compiler.Finding{}]}, never raises, and never partially succeeds.

The pipeline

Each stage runs over the whole document; the first stage that produces errors stops the compile and reports every error it found. Stopping rather than accumulating across stages is deliberate - see StatifierBlocks.Compiler.Finding.

  1. Document - StatifierBlocks.Document.validate/1. Structural only; no palette is consulted.
  2. Resolve - every block through StatifierBlocks.Palette.resolve/2, which also applies an in-memory config migration (ADR-0002 decision 8). Nothing is written back.
  3. Config - every block's validate_config/1.
  4. Structure - StatifierBlocks.Assignability.validate/3: may this block land in this slot, by kind tag and by data-flow type (ADR-0003)?
  5. Emit - bottom-up. Each block's emit/2 is called with its children already compiled and summarized, its emission is attributed (StatifierBlocks.Compiler.Attribution), and its child placeholders are spliced with those children's own emissions.
  6. Chart - serialize once through StatifierBlocks.Compiler.Serializer, which writes the bytes and the provenance map together, then run those bytes through Statifier.compile/2 and map every finding back through provenance (StatifierBlocks.Compiler.Chart).

Findings from every stage are reported in document order over blocks - StatifierBlocks.Document.blocks/1's pre-order - which is how upstream's own document-order sort survives the trip.

The Structure stage is not yet whole

Decision 10's table names three things in this stage: slot arity, :undeclared_slot, and assignability. Only the third runs here. Palette-aware arity and undeclared-slot validation is sb-da9's, filed and unworked; this module draws the seam and leaves it empty rather than growing a second implementation of ADR-0002 decision 6's rules beside the one that bead will ship. Until it lands the pipeline simply never visits a slot slots/1 did not declare, so an undeclared slot's blocks are absent from the emission rather than misplaced in it - which is a silent drop, and exactly what sb-da9 exists to make loud.

Options

  • :known_invoke_types - decision 8's opt-in lint. A set (or list) of invoke types the caller believes will be registered; every emitted type absent from it becomes a warning, never an error. See StatifierBlocks.Compiler.InvokeTypes.
  • :entry_type - ADR-0003 decision 4's caller-supplied context: the type flowing into the document's root. Defaults to absent, which StatifierBlocks.Assignability reads as :unknown. ADR-0004's own typespec lists only the first option, because it delegated assignability wholesale to ADR-0003 (decision 11) without noticing that ADR-0003's context is caller-supplied and therefore has to arrive through this function. This is that arrival, not a second decision about what assignability means.

Determinism (decision 6)

For a fixed {document canonical bytes, palette, compiler version}, the generated SCXML is byte-identical on every machine and every run, forever.

All three inputs are real, and StatifierBlocks.CompilationRecord records all three. What this module contributes to the guarantee is that it never iterates a bare map: slots are visited in slots/1 declaration order (ADR-0002 decision 6 made that order meaningful), children in document order, and attributes are sorted at construction by StatifierBlocks.Emission.element/3.

The guarantee is not reversible and must not be read as one. Equal output does not imply equal input: a metadata-only edit changes the document hash and produces identical SCXML, because metadata is not compiled. A host may use "same triple" to skip a recompile, and may not use "same SCXML" to conclude the document is unchanged.

Summary

Types

:known_invoke_types enables decision 8's optional two-registry lint; :entry_type is ADR-0003 decision 4's caller-supplied context. See the moduledoc.

Functions

Compiles document against palette.

Decision 6's third determinism input: this package's version.

Types

option()

@type option() ::
  {:known_invoke_types, Enumerable.t()}
  | {:entry_type, StatifierBlocks.Assignability.type_expr() | :unknown}

:known_invoke_types enables decision 8's optional two-registry lint; :entry_type is ADR-0003 decision 4's caller-supplied context. See the moduledoc.

Functions

compile(document, palette, opts \\ [])

Compiles document against palette.

Total: {:ok, %StatifierBlocks.Compiled{}} or {:error, [%StatifierBlocks.Compiler.Finding{}]}, never a raise and never a partial success. Errors come from the first failing stage only (decision 10); warnings ride on the artifact when the compile succeeds.

compiler_version()

@spec compiler_version() :: String.t()

Decision 6's third determinism input: this package's version.