# `Statifier.Lowering`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.15.0/lib/statifier/lowering.ex#L1)

The second arrow of the parser pipeline: a generic
`%Statifier.Parser.DOM.Element{}` tree in, a typed `%Statifier.Document{}`
tree out (`docs/architecture.md`, "Parser: DOM first, then lowering").

## The accumulator contract

`lower/2` performs one traversal. Every builder in
`Statifier.Lowering.Builders` lowers its children first via
`walk_children/2`, appending each child's errors to its own in document
order; `lower/2` sorts the whole accumulated list by
`location.start_offset` before returning, so the report reads in document
order regardless of the order a parent happened to emit its own error
relative to its children's.

`ctx` also carries `:source` - the source binary being lowered, seeded by
`lower/2` and copied forward unchanged by `walk_child/4` to every builder
in the tree. `build_content/2` is the one builder that reads it, to slice
`<content>`'s markup children (ADR-0041) back out of the original bytes.
Lowering still never re-parses anything; slicing only reads spans it
already has.

A builder that cannot place or build something still returns the best
partial result it can (or nothing, when it cannot build one at all) so the
walk keeps going and later errors are still found - but that partial tree
is never handed back to the caller. `lower/2` returns `{:ok, document}`
only when the accumulated error list is empty; any non-empty list means
`{:error, errors}`, full stop, regardless of how much of the tree built
successfully. Handing back a document that lowering itself does not trust
would just move the "is this actually usable" question onto every caller.

## Dispatch is context-free

The dispatch map's keys are exactly the supported child element names; no
`build_*` function in `Builders` takes a parent element name as an
argument. A builder does not know or care what its parent was - the
parent, when it exists, is what decides whether the child's tagged
result has anywhere to go (`Builders.place/3`). The
parent's own element name is known only to itself, and is used solely to
word a `{:misplaced_element, name, parent_name}` error about one of *its
own* children - it is never handed down to a child builder.

## Only `<scxml>` may be the root

`<scxml>` is the one element that may be a document's root, and it is
never a child. `lower/2` builds it directly and refuses every other root
name as `{:unexpected_root, local_name}`, whatever that name is, so a
root such as `<state>` is refused rather than handed to a builder whose
result has nowhere to go. `<scxml>` is not a key of the dispatch map
either, so an `<scxml>` at any walked child site (a child that
`walk_child/4` looks up) misses the map and is reported as
`{:unsupported_element, name}`, like any other name the map does not
hold. A builder that reads its element children itself instead of
walking them answers for an `<scxml>` there as for any other element
child, as its own `@doc` states; for example, under `<data>` it is a
`{:misplaced_element, name, "data"}`, and inside `<content>` or
`<assign>` it is part of the opaque markup slice.

## Relaxed input

Both sites that resolve an element's namespace (`lower/2` for the root,
`walk_child/4` for each walked child) accept an element with no
namespace as SCXML's own vocabulary, not only one resolved to the SCXML
namespace; `walk_child/4` is the one site that looks a name up in the
dispatch map. `Statifier.Lowering.Namespace.scxml_vocabulary?/1` is the
mechanism and its moduledoc states the commitment; this is a pointer for
the reader who starts here instead.

# `lower`

```elixir
@spec lower(root :: Statifier.Parser.DOM.Element.t(), source :: binary()) ::
  {:ok, Statifier.Document.t()} | {:error, [Statifier.Lowering.Error.t()]}
```

Lowers a generic `%Statifier.Parser.DOM.Element{}` tree - the parsed
`<scxml>` root - into a typed `%Statifier.Document{}` tree, in one
traversal that dispatches every child through `Statifier.Lowering.Builders`
by element name. `source` is the same source binary
`Statifier.Validator.validate/2` takes; it is seeded into `ctx` under
`:source` for the one builder that slices bytes out of it.

Returns `{:ok, document}` only when the whole walk accumulated no errors;
any error at all, anywhere in the tree, produces `{:error, errors}` with
the errors sorted into document order - never a partial document, even
when most of the tree built successfully. An element outside the SCXML
vocabulary (and not using the relaxed no-namespace fallback) is reported
as `{:foreign_element, name, uri, location}`; a non-`<scxml>` root name is
`{:unexpected_root, local_name, location}`, whether or not that name is
legal as a child (`<state>`, `<transition>`, ...); an `<scxml>` at a
walked child site is `{:unsupported_element, name, location}`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
