embassy-supervisor-syntax
The parser and AST for the supervisor_graph! DSL of
embassy-supervisor: turn graph
declarations into [GraphSpec] values and reject malformed ones with
span-attached errors.
Not a stable API
The AST is an internal contract with embassy-supervisor-macros, which pins
this crate by exact version. Fields and variants change whenever the graph
syntax does. Depend on it directly only if you are willing to track it.
What parsing does not decide
Feature gating. This crate carries no features and accepts the grammar in
full: ready/bound dep markers, observed/beat/via, discover,
dataflow:, local, state:, beat_timeout:, ready_on_write always parse.
Whether a build permits a construct is policy, applied by the caller:
embassy-supervisor-macros rejects gated constructs in its gate pass. Every
gated construct keeps the Ident or literal its rejection points at, so those
errors carry the span the author wrote.
Name resolution. Unknown deps, duplicate names, executor slot references,
and the 256-node-slot cap (pool members included) are properties of a whole
graph, checked by embassy-supervisor-macros while expanding. A
supervisor_fragment! legitimately names nodes it does not contain; they
resolve at the compose site, so parsing it in isolation must succeed.
What parsing does check
Shape, which is part of the grammar rather than any build's policy. All errors are span-attached:
- Empty clause lists (
reads:,writes:,resources:,dataflow:,provides:must each declare at least one entry; omit the clause instead). - A repeated signal path, or one path declared both bare and indexed (
&ARRand&ARR[0]share an address, so nothing downstream could tell them apart). - Marker shape:
boundwithoutready,beat/viawithoutobserved, a barebeatentry,beaton areads:entry. - Numeric ranges:
slot_timeout:/ack_timeout:/beat_timeout:/pool_size:at least 1,beat_window:in 1..=255. - Clause combinations:
task:vsspawn:, clauses that requiretask:(pool_size:,resources:,state:,exit:,cancel),cancelwithMode::Pause,exit:on apool,ready_on_writewithoutbeat_timeout:and anobserved beatwrite. default executorshape: one per graph, never#[cfg]-gated, never inside@fragmentmarkers (it resolves across them at the compose site). Whether alocalslot's declarations share one executor is checked byembassy-supervisor-macros, not here.
Usage
use ;
let spec: GraphSpec = parse_str?;
for item in &spec.items
# Ok::
Fragment helpers
supervisor_fragment! items reach a compose site as tokens. Two helpers move
them between spellings:
- [
normalize_fragment_crate] rewrites every barecrateto$crate, so a fragment forwarded into another crate still names its own items. - [
substitute_dollar_crate] replaces every$cratewith a caller-chosen token stream, so fragment items parse as a [GraphSpec] (the macro substitutes a placeholder; a source reader substitutes the compose crate).