Skip to main content

ParsedSheet

Struct ParsedSheet 

Source
pub struct ParsedSheet {
    pub sheet: Sheet,
    pub cell_names: IndexMap<String, (CellId, TypeShape)>,
    pub output_names: IndexMap<String, CellId>,
    pub requirement_names: HashMap<RequirementId, String>,
    pub method_spans: HashMap<(RelationshipId, usize), SourceSpan>,
    pub relationship_spans: HashMap<RelationshipId, SourceSpan>,
    pub cell_spans: HashMap<CellId, SourceSpan>,
}
Expand description

The result of AdamParser::parse_str: a live [Sheet] plus the declared cell names, in source declaration order.

Derefs to [Sheet] so callers that only need sheet methods (e.g. propagate) can use the result exactly as if it were a Sheet.

§Examples

use adam_lang::{AdamParser, TypeRegistry};
use cel_parser::OpLookup;
let parsed = AdamParser::new(TypeRegistry::new(), OpLookup::new())
    .parse_str("sheet s { cell x: i32 = 1 require { @positive x > 0i32; }; }")?;
let id = parsed.cell_requirements(parsed.cell_names["x"].0).unwrap()[0];
assert_eq!(parsed.requirement_names[&id], "positive");

Fields§

§sheet: Sheet

The constructed sheet.

§cell_names: IndexMap<String, (CellId, TypeShape)>

Cell name → (CellId, TypeShape), in declaration order.

§output_names: IndexMap<String, CellId>

Output name → CellId, in declaration order — parity with cell_names, for callers that need to look up Sheet::cell_requirements_valid/Sheet::violated_requirements by name.

§requirement_names: HashMap<RequirementId, String>

Explicit @name labels keyed by requirement ID; unnamed requirements have no entry. Lookup takes expected O(1) time. Iterate the sheet’s requirement IDs, not this map, to preserve attachment or violation order.

§method_spans: HashMap<(RelationshipId, usize), SourceSpan>

(RelationshipId, method index) → the source span of that binding, populated for every successfully-added relationship. Lets a caller translate an adam_rs::Error’s ErrorSite::Method (raised well after parsing, e.g. from Sheet::propagate) back to a source location.

out declarations’ internal writer relationships (created via Sheet::add_out, not parse_relationship_decl) are not recorded here, since add_out returns only a CellId, not the writer’s RelationshipId. In practice this is narrow: an out writer’s body is always a CEL expression, so arithmetic failures already carry a cel_parser::SpanContext regardless of this gap. A MethodFailed/TypeMismatch from an out writer without a SpanContext falls back to Display instead of a source-span diagnostic — never worse than resolving no site at all.

§relationship_spans: HashMap<RelationshipId, SourceSpan>

RelationshipId → the source span of that relationship { ... } block, populated for every successfully-added relationship. Resolves an adam_rs::Error’s ErrorSite::Relationship (e.g. from Error::Conflict) back to a source location.

§cell_spans: HashMap<CellId, SourceSpan>

CellId → the source span of that cell’s declared name, populated for every declared cell, source, and out. Resolves an adam_rs::Error’s ErrorSite::Cell back to a source location.

Implementations§

Source§

impl ParsedSheet

Source

pub fn read_compound<R: ValueRepr>(&self, id: CellId) -> Result<R::Value>

Clones a compound cell into its exact canonical witness representation.

§Errors

Rejects unknown cells, concrete leaf storage, and recursive shape mismatches.

  • Complexity: O(n + v) validation and cloning for n shape nodes and v values.
§Examples
use adam_lang::{AdamParser, TypeRegistry};
use cel_runtime::{ArrayOf, Leaf};
let sheet = AdamParser::new(TypeRegistry::new(), cel_parser::OpLookup::new())
    .parse_str("sheet s { cell xs: [i32]; }").unwrap();
assert!(sheet.read_compound::<ArrayOf<Leaf<i32>>>(sheet.cell_names["xs"].0)?.is_empty());
Source

pub fn write_compound<R: ValueRepr>( &mut self, id: CellId, value: R::Value, ) -> Result<()>

Writes a compound aspiration after validating its complete witness identity.

Existing cell capabilities, rather than witness defaults, govern propagation.

§Errors

Rejects unknown cells, leaf storage, or any recursive shape mismatch before writing.

  • Complexity: O(n) validation and descriptor construction for n shape nodes.
§Examples
use adam_lang::{AdamParser, TypeRegistry};
use cel_runtime::{Array, ArrayOf, Leaf};
let mut sheet = AdamParser::new(TypeRegistry::new(), cel_parser::OpLookup::new())
    .parse_str("sheet s { source xs: [i32]; }").unwrap();
sheet.write_compound::<ArrayOf<Leaf<i32>>>(sheet.cell_names["xs"].0, Array::from_vec(vec![1]))?;
Source

pub fn source_compound<R: ValueRepr>(&self, id: CellId) -> Result<R::Value>

Clones the original compound aspiration, independently of any filter result.

§Errors

Rejects unknown cells, concrete leaf storage, or a mismatched recursive witness.

  • Complexity: O(n + v) for n descriptor nodes and v cloned values.
§Examples
use adam_lang::{AdamParser, TypeRegistry};
use cel_runtime::{ArrayOf, Leaf};
let sheet = AdamParser::new(TypeRegistry::new(), cel_parser::OpLookup::new())
    .parse_str("sheet s { source xs: [i32]; }").unwrap();
assert!(sheet.source_compound::<ArrayOf<Leaf<i32>>>(sheet.cell_names["xs"].0)?.is_empty());
Source

pub fn locate_error(&self, e: &Error) -> Vec<(SourceSpan, String)>

Resolves each of e’s ErrorSites to a source span and a human label, primary first.

Sites whose span is not recorded are skipped, so the result may be shorter than e.sites(); empty when none resolves (the caller then falls back to Display).

  • Complexity: O(s) in the number of sites.
§Examples
use adam_lang::{AdamParser, TypeRegistry};
use cel_parser::OpLookup;

let mut parser = AdamParser::new(TypeRegistry::new(), OpLookup::new());
// Two relationships forming an algebraic loop with no external source for either
// cell: `x` needs `y` and `y` needs `x`, so `propagate` can't pick a valid method
// assignment and returns `Error::Cycle`.
let mut parsed = parser
    .parse_str(
        "sheet s { cell x: i32 = 0; cell y: i32 = 0; \
         relationship { x := y + 1i32; } relationship { y := x + 1i32; } }",
    )
    .unwrap();
let e = parsed.propagate().unwrap_err();
let located = parsed.locate_error(&e);
assert!(!located.is_empty());
assert!(!located[0].1.is_empty());

Methods from Deref<Target = Sheet>§

pub fn add_cell<T>(&mut self, value: T) -> CellId
where T: Any + PartialEq + 'static,

Registers a cell with an initial value and returns a stable handle.

The cell’s TypeId is fixed at creation time; subsequent write and read calls that use a different type will return Error::TypeMismatch.

Each call increments the sheet’s internal strength counter and sets bit 63 of the result. This partitions the strength space: written/added cells always have higher strength than derived cells, ensuring stability across conditional branch switches.

  • Complexity: Amortized O(1), plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.

pub fn add_source<T>(&mut self, value: T) -> CellId
where T: Any + PartialEq + 'static,

Registers a cell that can never be claimed as any method’s output — always a planner source, forever.

  • Complexity: Amortized O(1), plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.

pub fn cell_kind(&self, id: CellId) -> Option<CellKind>

Returns id’s fixed cell kind.

Returns None if id is not a live cell in this sheet.

pub fn add_relationship( &mut self, methods: Vec<Method>, ) -> Result<RelationshipId, Error>

Registers a relationship defined by a non-empty list of methods.

All methods are validated: their declared TypeIds must match the registered cells, and each method must have at least one output. A method with no inputs is explicitly allowed: it defines a fixed point (a constant, independent of every other cell) rather than a derivation. On success the RelationshipId is added to each adjacent cell’s adjacency list.

A cell that appears in both a method’s inputs and its outputs is a self-referencing cell and is explicitly allowed.

§Errors
  • Error::InvalidMethod — methods is empty, or a method has no outputs.
  • Error::MismatchedMethodCells — some method’s inputs ∪ outputs differs from another method’s in the same relationship.
  • Error::InvalidMethodOutputs — a method’s own outputs list names a cell more than once, or two methods in the same relationship have identical or nested outputs sets. Overlapping non-nested output sets are allowed.
  • Error::InvalidId — a CellId in any method is not found in this sheet.
  • Error::TypeMismatch — a method’s declared TypeId does not match the cell’s registered TypeId.
  • Error::InvalidCellKind — a method’s output cell is Source-kind.

Guard independence is checked by [Sheet::validate] and by [Sheet::propagate], not by this mutator.

  • Complexity: O(m² × c) where m is the total number of methods and c is the maximum number of cells per method, due to pairwise output-set comparison, plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.

pub fn add_conditional<T>( &mut self, source: MatchExpr, branches: Vec<(Vec<T>, Vec<RelationshipId>)>, default: Vec<RelationshipId>, ) -> Result<ConditionalId, Error>
where T: Any + PartialEq + 'static,

Registers a conditional that activates relationships based on the value of a match subject: either a single existing cell, or a [MatchExpr] computed from multiple input cells.

Each element of branches is (keys, relationships): when the match subject’s value equals any key in keys, the branch’s relationships are added to the active set for propagate. Branches are evaluated in definition order; first match wins. default holds relationships activated when no branch matches; pass an empty Vec for no default.

§Errors
  • Error::InvalidId — the match subject references a cell not in this sheet.
  • Error::TypeMismatch — (expression match subject only) an input cell’s registered type doesn’t match the expression’s declared type for that input.
  • Error::InvalidConditional — the match subject’s output type does not match T; a referenced relationship does not exist; a relationship already appears in another conditional branch; or a branch has no keys.

Guard independence is checked by [Sheet::validate] and by [Sheet::propagate], not by this mutator.

  • Complexity: O(B·(K + R)) where B = branches, K = keys per branch, R = relationships per branch, plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.

pub fn add_requirement( &mut self, cell: CellId, requirement: Requirement, ) -> Result<RequirementId, Error>

Attaches a requirement to cell and returns its stable ID. requirement.inputs may be any cells in the sheet, not only cell itself. For a Cell/Source kind cell, also evaluates requirement immediately against current effective values — its value is already authoritative. Skipped for an Out-kind cell: its value isn’t authoritative until its writer next executes, so attachment always succeeds structurally there, deferring to the first post-propagate() diagnostic to report an initial violation if there is one.

§Errors
  • Error::InvalidId — cell, or one of requirement’s input cells, is not a live cell in this sheet.

  • Error::TypeMismatch — an input’s declared type does not match its cell’s registered type.

  • Error::InvalidRequirement — the input signature is invalid, or (Cell/Source kind only) evaluating requirement against the referenced cells’ current effective values returns Ok(false).

  • Error::MethodFailed — (Cell/Source kind only) evaluating requirement against current values returns Err.

  • Complexity: O(k) where k is requirement’s input count, plus predicate evaluation for a Cell/Source, plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.

§Examples
use adam_rs::{Requirement, Sheet};
let mut sheet = Sheet::new();
let cell = sheet.add_cell(1_i32);
let id = sheet.add_requirement(cell, Requirement::from_fn_1(cell, |x: &i32| Ok(*x > 0)))?;
assert_eq!(sheet.cell_requirements(cell), Some([id].as_slice()));

pub fn add_out( &mut self, writer: Method, requirements: Vec<Requirement>, ) -> Result<CellId, Error>

Registers writer as the sole producer of its one output cell, which becomes an out cell: always derived by writer, never write()-able, but otherwise an ordinary, freely-referenceable cell. requirements are attached to that cell one at a time, in order, via [Sheet::add_requirement].

§Errors
  • Error::InvalidOutput — writer does not have exactly one output cell.

  • Error::InvalidCellKind — the writer’s output cell is already Source or Out kind, or already claimed as some existing method’s output.

  • Any error [Sheet::add_relationship] or [Sheet::add_requirement] can return.

  • Postcondition: not atomic — if add_requirement fails partway through requirements, the output cell is left Out-kind, writer is already registered as its producer, and every requirement before the failing one is already attached; none of this is rolled back on error.

  • Complexity: O(k + i + m²×c) where k is the number of requirements and i is their total input count, plus the cost of add_relationship for writer alone (m = 1 method, c = cells in that method), plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.

§Examples
use adam_rs::{Method, Requirement, Sheet};
let mut sheet = Sheet::new();
let input = sheet.add_cell(1_i32);
let output = sheet.add_cell(0_i32);
let id = sheet.add_out(
    Method::from_fn_1_1(input, output, |x: &i32| Ok(*x)),
    vec![Requirement::from_fn_1(output, |x: &i32| Ok(*x > 0))],
)?;
assert_eq!(id, output);
sheet.propagate()?;
assert!(sheet.cell_requirements_valid(output));

pub fn add_filter(&mut self, cell: CellId, filter: Filter) -> Result<(), Error>

Attaches filter to cell.

Never evaluates filter’s function — attaching a filter is not a fresh external input, so it never changes cell’s current effective value. The next full [Sheet::propagate] call conforms cell via the planner’s PlanStep::FilterReclamp step; until then, read() reflects whatever cell held before this call.

§Errors
  • Error::InvalidId — cell, or one of filter’s argument cells, is not a live cell in this sheet.
  • Error::InvalidFilter — cell already has a filter, filter’s own value type does not match cell’s registered type, or filter’s argument list names cell itself.
  • Error::TypeMismatch — an argument cell’s registered type does not match the type filter declared for it.

Guard independence is checked by [Sheet::validate] and by [Sheet::propagate], not by this mutator.

  • Complexity: O(a) where a is the number of filter argument cells, plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.

pub fn filter_args(&self, id: CellId) -> Option<&[CellId]>

Returns the argument cells of id’s filter, in declaration order.

Returns None if id is not a live cell in this sheet, or has no filter.

pub fn filter_dependents(&self, id: CellId) -> &[CellId]

Returns the live cells whose filter references id as one of its dynamic arguments — the reverse of a filter’s own argument list ([Sheet::filter_args]).

  • Postcondition: empty if no live cell’s filter references id.

pub fn filter_kind(&self, id: CellId) -> Option<&FilterKind>

Returns the kind of validation/derivation id’s filter performs, if it has one.

Returns None if id is not a live cell in this sheet, or has no filter.

pub fn filter_range<T>(&self, id: CellId) -> Option<(T, T)>
where T: Any + Clone,

Returns id’s filter’s current (lo, hi) bounds, if it has a [FilterKind::Range] filter.

Resolves the filter’s argument cells’ current effective values via the same path [Sheet::add_filter] already uses, then calls the filter’s bounds function.

Returns None if id is not a live cell in this sheet, has no filter, its filter’s kind isn’t [FilterKind::Range], or the range expression fails to evaluate against the filter’s current argument values (e.g. a fallible arithmetic op in a range endpoint) — the same degraded-to-None outcome as any other reason no live bounds are available right now.

  • Complexity: O(a) where a is the number of the filter’s argument cells.

pub fn filter_violation(&self, id: CellId) -> Option<&FilterViolation>

Returns the filter violation recorded for id as of the last full propagate() call, if any.

  • Postcondition: None if id has no filter, id’s filter’s last-checked value held, or no full propagate() has run since id was last a plain external write.

pub fn filter_violated_cells(&self) -> impl Iterator<Item = CellId>

Iterates cells whose filter is currently violated, as of the last full propagate() call.

  • Complexity: O(n) where n is the number of currently-violated filters.

pub fn filter_violation_cells(&self) -> HashSet<CellId>

Returns the set of root cells currently determining a violated filter’s own value or any of its argument values, as of the last full propagate() call — the same “which upstream cells caused this” query requirement_contributing_cells/requirement_violation_cells already provide for Requirement.

  • Postcondition: empty if no filter is currently violated.
  • Complexity: O(sum of contributing_cells cost over every violated filter and its argument cells).

pub fn cell_requirements(&self, id: CellId) -> Option<&[RequirementId]>

Returns the requirements attached to id, in attachment order.

Returns None if id is not a live cell in this sheet.

pub fn requirement_cell(&self, id: RequirementId) -> Option<CellId>

Returns the cell requirement id is attached to.

Returns None if id is not a live requirement in this sheet.

pub fn requirement_inputs(&self, id: RequirementId) -> Option<&[CellId]>

Returns the cells requirement id reads.

Returns None if id is not a live requirement in this sheet.

pub fn cell_requirements_valid(&self, id: CellId) -> bool

Returns true if every requirement on id held as of the last propagate() call.

Returns false if no propagation has run yet. Also returns true for an id that is not a live cell in this sheet, since no requirement can have failed for a cell that doesn’t exist.

pub fn violated_requirements( &self, id: CellId, ) -> impl Iterator<Item = RequirementId>

Iterates the requirements on id that evaluated to false as of the last propagate() call.

  • Postcondition: empty if id’s requirements all held, id is not a live cell in this sheet, or no propagation has run yet.

pub fn contributing_cells(&self, id: CellId) -> HashSet<CellId>

Returns the set of root cells that could determine id’s value for some choice of cell strengths, as of the last propagate() call.

A cell is a root candidate for current whenever some active relationship adjacent to current has a method producing it — not just the one method the current strengths happen to have selected: a different strength ordering could pick a different method of that same relationship, making any of its other cells the source instead. So every method (of every active relationship touching current) with current among its outputs contributes its inputs to the walk, not only the currently-selected one. [Sheet::is_forced] already answers “could current itself be left as a free source under some strength” precisely, so current is added directly whenever it is not forced. A self-referencing input (present in both a method’s inputs and its outputs) is treated as one of its own roots directly, since it is read at its pre-execution value rather than derived further.

Every visited cell is also checked against every conditional in the sheet: if any of that conditional’s branches (or its default) has a method whose outputs include the visited cell, the conditional’s match cell is added as a contributor too, and is itself traced recursively. This covers both an active producer that happens to belong to a conditional branch, and a cell with no active producer specifically because the branch that would define it isn’t the one currently selected (that absence is itself a fact controlled by the match cell, not an indication that the match cell is irrelevant).

  • Postcondition: returns {id} if no propagation has run yet, or if id is not forced and no active or conditional relationship could produce it.

  • Complexity: O(N · (R·M + B)) where N is the number of cells reachable from id (including conditional match cells), R is the number of relationships adjacent to a visited cell, M is the maximum number of methods per relationship, and B is the total number of branch/default relationships across all conditionals.

pub fn requirement_contributing_cells( &self, id: RequirementId, ) -> HashSet<CellId>

Returns the union of [Sheet::contributing_cells] over requirement id’s own declared inputs.

Returns an empty set if id is not a live requirement in this sheet.

  • Complexity: O(K·N) where K is the requirement’s input count and N is the size of each input’s contributing set.

pub fn requirement_relevant_cells(&self) -> HashSet<CellId>

Returns the union of contributing_cells over every cell with at least one requirement — the set of cells currently determining at least one requirement-checked value, as of the last propagate() call.

  • Postcondition: empty if no cell in the sheet has any requirements.
  • Complexity: O(sum of contributing_cells cost over every cell with requirements).

pub fn requirement_violation_cells(&self) -> HashSet<CellId>

Returns the union of requirement_contributing_cells over every requirement that evaluated false as of the last propagate() call, across every cell in the sheet.

  • Postcondition: empty if no requirement anywhere in the sheet currently fails.
  • Complexity: O(sum of requirement_contributing_cells cost over every violated requirement).

pub fn write<T>(&mut self, id: CellId, value: T) -> Result<(), Error>
where T: Any + 'static,

Writes a value to a cell, incrementing the cell’s write-recency strength.

Each successful write increments a global monotonic counter and assigns the new value to cell.strength, so the most-recently-written cell always has the highest strength.

  • Postcondition: any pending derived override is cleared, so the written value is immediately visible via read().
  • Complexity: Expected O(1) validation and phase-membership work, plus O(A) when a claimed-cell write releases the final owners of prepared artifacts of total size A, excluding destruction of replaced values.
§Errors
  • Error::InvalidId — id is not a cell in this sheet.
  • Error::TypeMismatch — T does not match the cell’s registered TypeId.
  • Error::InvalidCellKind — id is Out-kind.

pub fn read<T>(&self, id: CellId) -> Result<&T, Error>
where T: Any + 'static,

Returns a shared reference to the cell’s effective current value: its derived override if one exists, otherwise its source (last written) value.

§Errors
  • Error::InvalidId — id is not a cell in this sheet.
  • Error::TypeMismatch — T does not match the cell’s registered TypeId.

pub fn source<T>(&self, id: CellId) -> Result<&T, Error>
where T: Any + 'static,

Returns the raw source slot: the last value written via write()/add_cell, ignoring any derived override produced by a self-referencing method or a conditionally forced relationship. For an ordinary (unshadowed) derived cell, propagate() writes straight into this same slot, so source() agrees with read(); the two diverge only for cells currently shadowed.

§Errors
  • Error::InvalidId — id is not a cell in this sheet.
  • Error::TypeMismatch — T does not match the cell’s registered TypeId.
§Examples
use adam_rs::Sheet;

let mut sheet = Sheet::new();
let a = sheet.add_cell(3_i32);
sheet.write(a, 8_i32).unwrap();
assert_eq!(*sheet.source::<i32>(a).unwrap(), 8);

pub fn changed(&self) -> impl Iterator<Item = CellId>

Iterates over the cells that were updated during the last propagate() call.

This includes cells written by selected methods and cells that reverted to their source values because the relationship that had been shadowing them (self-referencing or conditionally forced) is no longer producing them this round (Phase 5), even though no method wrote to them this round. It does not attempt to compare old/new values for equality. A propagate() call that returns Error::SeedCycle leaves this iterator empty: propagation clears stale changed-state before staged seed validation and aborts before committing any writes.

  • Complexity: O(n) where n is the number of changed cells.

pub fn clear_changed(&mut self)

Clears the changed-cell set and resets each cell’s changed flag.

Call after processing the results of propagate().

  • Complexity: O(n) where n is the number of changed cells.

pub fn validate(&mut self) -> Result<(), Error>

Validates static guard independence for the current sheet structure.

A filter’s argument cells and a conditional’s match cells must not depend on any cell governed by that filter or conditional in the static dependency graph. A successful call records the current structure as validated; a failed call leaves it unvalidated, so the same structure reports the dependency cycle again on the next [Sheet::validate] or [Sheet::propagate] call.

§Errors
  • Error::DependencyCycle — a guard edge lies on a cycle in the static dependency graph.
§Examples
use adam_rs::{Filter, Method, Sheet};

let mut sheet = Sheet::new();
let limit = sheet.add_cell(10_i32);
let value = sheet.add_cell(5_i32);
let copy = sheet.add_cell(0_i32);
sheet
    .add_relationship(vec![Method::from_fn_1_1(value, copy, |x: &i32| Ok(*x))])
    .unwrap();
sheet
    .add_filter(value, Filter::from_fn_1(limit, |v: &i32, hi: &i32| Ok((*v).min(*hi))))
    .unwrap();
sheet.validate().unwrap();
  • Complexity: O(1) when no structural mutation has happened since the last successful validation; otherwise O(V + E), where V is the number of cells and E is the number of static method and guard edges.

pub fn cells(&self) -> impl Iterator<Item = CellId>

Iterates all live cell IDs in the sheet.

  • Complexity: O(n) where n is the number of cells.

pub fn relationships(&self) -> impl Iterator<Item = RelationshipId>

Iterates all live relationship IDs in the sheet.

  • Complexity: O(n) where n is the number of relationships.

pub fn cell_adj(&self, id: CellId) -> Option<&[RelationshipId]>

Returns the relationships adjacent to id.

Returns None if id is not a live cell in this sheet.

  • Complexity: O(1).

pub fn relationship_adj(&self, id: RelationshipId) -> Option<&[CellId]>

Returns the cells adjacent to id (union across all methods).

Returns None if id is not a live relationship in this sheet.

  • Complexity: O(1).

pub fn propagate(&mut self) -> Result<(), Error>

Updates the sheet using eligible prepared plans or newly selected methods.

Validates static guard independence before mutating state, then clears the changed-cell set from the previous propagate() call before staged planning. After propagation, call [Sheet::changed] to inspect which cells were updated, and [Sheet::clear_changed] when done.

Method, filter, conditional-expression, and requirement callbacks must be purely functional. There is no guarantee that any callback executes, or when or how often it executes; results may be reused or recomputed while preserving cell values and diagnostics. The phases below describe the current algorithm, not a callback invocation contract.

Retains at most one prepared unconditional plan and one prepared main plan, each with its own released sources and active relationships. Successful writes to released sources preserve that phase’s eligibility; writes to claimed cells (including self-referencing claimants) discard it. Filter reclamps do not claim released sources. Actual structural mutations discard both entries, including partial mutations before an error; rejected writes leave eligibility unchanged. Prepared plans retain structural seed recipes, not values from earlier calls.

Phase 0 — Staging: propagation starts from each cell’s source value, excluding every derived override from the previous round, but does not mutate live cells.

Phase 1 — Pre-plan: when conditionals exist, an eligible prepared unconditional plan is obtained or a new one is prepared. Its selected guard-prerequisite cone executes so derived guard values are current before branch evaluation. Unrelated filters remain deferred until the final active plan, after the relationships that produce their arguments.

Phase 2 — Conditional evaluation: each conditional’s match cell value is read and compared against branch keys; the active relationship set is built.

Phase 3 — General plan: an eligible prepared main plan is obtained for the active set, or the Adam algorithm selects and prepares a new one. Seed-cycle validation and method execution consume staged Phase 1 values. A callback already evaluated in Phase 1 is reused only when the final plan preserves its selected method, input producers, and output classification. A mismatched plan returns a conservative conflict rather than committing stale values. This boundary may reject a sheet even when another assignment could have avoided the mismatch; propagation reports the implicated sites instead of attempting that alternate assignment. A Phase 1 seed callback is likewise reused only when every input reads the same staged source version, recursive seed result, or accumulated value; otherwise propagation returns a conflict instead of reusing a stale seed.

Phase 4 — Commit and strength post-processing: after planning, seed validation, and staged method execution all succeed, staged writes are published atomically. Derived cells then receive low-order strengths in evaluation order, enforcing the stability invariant. A cell claimed self-referencingly keeps any live explicit strength instead, since its own written value is still the authority behind the result.

Phase 5 — Reversion change-tracking: a cell whose derived override existed before this round but wasn’t reclaimed by any method this round has effectively reverted to its source value (e.g. its forcing conditional went inactive); it is marked changed even though no method wrote to it this round.

Phase 6 — Requirement evaluation: every registered requirement is evaluated against current cell values, rebuilding last_requirement_violations from scratch, so [Sheet::cell_requirements_valid] and [Sheet::violated_requirements] reflect this round.

If staged seed validation detects a non-self sibling dependency cycle, or compatibility checking returns a prerequisite conflict, propagate() returns its error after validation and changed-state clearing but before commit. Live cell values therefore remain untouched and [Sheet::changed] stays empty for that failing call.

Requirement callback errors occur after commit: published values and changed-state remain visible. Any propagation error discards both phases’ reuse eligibility, but retains the last successful display snapshot. Static validation errors occur before changed-state clearing. Filter failures remain diagnostics rather than propagation errors, and diagnostic results use current values.

  • Complexity: on a cache miss, prerequisite-cone indexing, traversal, and producer preparation take O(V + E) per selected plan, distinct from assignment matching. Cache hits omit this preparation, assignment matching, global strength sorting, and complete seed-elimination replay. Active-set comparison takes expected O(R) for R active relationships; a pending strength certificate takes O(C) for C cells. Current strength gates and local sibling sorts still run (O(S log S) comparisons for S siblings, with structural signature comparison costs). Fresh staged execution, seed evaluation, commit, and diagnostics retain their costs. Discarding final owners of prepared artifacts of total size A can take O(A).
§Errors
  • Error::DependencyCycle — a filter or conditional guard edge lies on a cycle in the static dependency graph.
  • Error::Conflict — no valid method assignment exists, or a pre-executed guard prerequisite is incompatible with the final selected plan.
  • Error::SeedCycle — sibling seed dependencies form a non-self cycle.
  • Error::MethodFailed — a method’s function returned an error, a method produced the wrong number of outputs, or a requirement’s function returned an error.
  • Error::TypeMismatch — a method output’s runtime type does not match the cell’s registered type.

pub fn selected_method(&self, rel: RelationshipId) -> Option<usize>

Returns the index of the method selected for rel in the last successful propagation.

Returns None if no propagation has run yet, rel is not in the cached plan, or rel was added after the last propagate() call.

pub fn method_inputs( &self, rel: RelationshipId, idx: usize, ) -> Option<&[CellId]>

Returns the input cells of method idx in relationship rel.

Returns None if rel is not a live relationship or idx is out of bounds.

pub fn method_outputs( &self, rel: RelationshipId, idx: usize, ) -> Option<&[CellId]>

Returns the output cells of method idx in relationship rel.

Returns None if rel is not a live relationship or idx is out of bounds.

pub fn is_source(&self, id: CellId) -> bool

Returns true if id was not written by any selected method in the last successful propagation.

Returns false if no propagation has succeeded yet. This display query does not determine prepared-plan eligibility.

Cells absent from the completed plan, including newly added cells, return true.

  • Complexity: Expected O(1).

pub fn is_forced(&self, id: CellId) -> bool

Returns true if id can never be a source, as of the last successful propagate() call.

Some active relationship’s method structure guarantees the cell is always produced by a method, regardless of strength — writing to it has no lasting effect once propagate() runs again. Useful for disabling input fields in a UI.

Returns false if no propagation has run yet.

pub fn forced_cells(&self) -> impl Iterator<Item = CellId>

Iterates cells that are forced (see [Sheet::is_forced]) as of the last successful propagate() call.

  • Complexity: O(n) where n is the number of forced cells.

pub fn is_relationship_forced(&self, id: RelationshipId) -> bool

Returns true if id had exactly one viable method as of the last successful propagate() call — the planner has no alternative method to choose for this relationship, regardless of cell strength.

Returns false if no propagation has run yet.

pub fn forced_relationships(&self) -> impl Iterator<Item = RelationshipId>

Iterates relationships that are forced (see [Sheet::is_relationship_forced]) as of the last successful propagate() call.

  • Complexity: O(n) where n is the number of forced relationships.

pub fn conditionals(&self) -> impl Iterator<Item = ConditionalId>

Iterates all live conditional IDs in the sheet.

  • Complexity: O(n) where n is the number of conditionals.

pub fn out_cells(&self) -> impl Iterator<Item = CellId>

Iterates all live Out-kind cells in the sheet.

  • Complexity: O(n) where n is the number of cells.

pub fn conditional_match_cells(&self, id: ConditionalId) -> Option<&[CellId]>

Returns the match cells for conditional id: a single cell for a plain match subject, or every input of a [MatchExpr] match subject.

Returns None if id is not a live conditional in this sheet.

pub fn conditional_branch_count(&self, id: ConditionalId) -> Option<usize>

Returns the number of named branches in conditional id.

Returns None if id is not a live conditional in this sheet.

pub fn conditional_branch_relationships( &self, id: ConditionalId, branch: usize, ) -> Option<&[RelationshipId]>

Returns the relationship IDs for branch branch of conditional id.

Returns None if id is not a live conditional, or branch is out of bounds.

pub fn conditional_default_relationships( &self, id: ConditionalId, ) -> Option<&[RelationshipId]>

Returns the default relationship IDs for conditional id.

These relationships are active when no named branch key matches the match cell. Returns None if id is not a live conditional in this sheet.

pub fn conditional_active_branch( &self, id: ConditionalId, ) -> Result<Option<usize>, Error>

Returns the index of the currently matching branch for conditional id.

Evaluates branch keys against the match subject’s current value in definition order; returns the index of the first matching branch. Returns Ok(None) if no branch key matches (the default branch is active) or if id is not a live conditional.

§Errors
  • Error::MethodFailed — id is a live, expression-sourced conditional whose function returned an error.

  • Complexity: O(B·K) where B = branches, K = keys per branch.

Trait Implementations§

Source§

impl Debug for ParsedSheet

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Deref for ParsedSheet

Source§

type Target = Sheet

The resulting type after dereferencing.
Source§

fn deref(&self) -> &Sheet

Dereferences the value.
Source§

impl DerefMut for ParsedSheet

Source§

fn deref_mut(&mut self) -> &mut Sheet

Mutably dereferences the value.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.