The third arrow of the parser pipeline, and the only gate in front of the
Machine compiler (docs/architecture.md principle 4): a %Statifier.Document{}
in, {:ok, document, warnings} | {:error, errors, warnings} out. The
interpreter only ever accepts a compiled Machine, so there is no
"validate if not already validated" fallback anywhere downstream - this
pass is the single place a malformed document is ever caught.
It also carries a second, non-fatal finding channel (ADR-0033): a check in
@warning_checks reports a Statifier.Validator.Warning, a document
conformance finding the engine has a defined behavior for either way, and
it never gates compilation the way an @checks finding does.
Five contracts, inherited from Statifier.Lowering's own and stated here
so a reader does not have to infer them from the code:
- Collect-all, never fail-fast. Every check runs on every call, over the whole document; a document that trips five distinct checks reports five errors, not one. This applies to both channels.
- Document-order sort. The accumulated error list, and separately the
accumulated warning list, is sorted by
location.start_offsetbefore it is returned, regardless of which check happened to report first. - Never a partial result.
validate/2returns{:ok, document, warnings}only when the accumulated error list is empty, anddocumentis always the caller's own input, unchanged - the validator never rewrites what it is given (contrast v1's source rewriting at../statifier/lib/statifier/parser/scxml.ex:76-103). sourcemust be the document's own binary.validate/2takes the exact sourcedocumentwas parsed from, guarded byis_binary/1. Passing a different binary produces wrongLocation.slice/2results in any check that reads it, not a crash - there is no arity-1 convenience that could silently disable those checks.- Both arms carry warnings. The error arm carries the warning list too - dropping warnings whenever an error also fires would make the warning channel fail-fast against the error channel, which contract 1 does not ask for on its own.
Summary
Functions
Runs all twenty @checks and the @warning_checks against document,
collecting every finding on each channel rather than stopping at the
first one, and returns {:ok, document, warnings} when no error fired or
{:error, errors, warnings} otherwise - both arms carry warnings per
contract 5. Each of errors and warnings is sorted by
location.start_offset regardless of which check reported it. document
is returned unchanged on success - this pass never rewrites its input.
Functions
@spec validate( document :: Statifier.Document.t(), source :: binary(), opts :: keyword() ) :: {:ok, Statifier.Document.t(), [Statifier.Validator.Warning.t()]} | {:error, [Statifier.Validator.Error.t()], [Statifier.Validator.Warning.t()]}
Runs all twenty @checks and the @warning_checks against document,
collecting every finding on each channel rather than stopping at the
first one, and returns {:ok, document, warnings} when no error fired or
{:error, errors, warnings} otherwise - both arms carry warnings per
contract 5. Each of errors and warnings is sorted by
location.start_offset regardless of which check reported it. document
is returned unchanged on success - this pass never rewrites its input.
source must be the exact binary document was parsed from; a mismatched
source produces wrong Location.slice/2 results in any check that reads
it rather than raising, so callers must not pass a substitute binary.
opts defaults to [] and is threaded straight onto
Statifier.Validator.Context.build/3; today's one recognized option is
invoke_content_markup: true (ADR-0042), which Checks.Boilerplate reads
off the context to relax its root-namespace check. See Statifier.compile/2
for what the flag is for and why it exists.