flowrep.parsers.symbol_scope module

class flowrep.parsers.symbol_scope.SymbolConsumption(symbol: str, consumer_node: str, consumer_port: str, source: flowrep.edge_models.InputSource | flowrep.edge_models.SourceHandle)[source]

Bases: object

consumer_node: str
consumer_port: str
source: InputSource | SourceHandle
symbol: str
class flowrep.parsers.symbol_scope.SymbolProduction(output_port: str, source: flowrep.edge_models.SourceHandle | flowrep.edge_models.InputSource)[source]

Bases: object

output_port: str
source: SourceHandle | InputSource
class flowrep.parsers.symbol_scope.SymbolScope(sources: dict[str, InputSource | SourceHandle], available_accumulators: set[str] | None = None, reserved_accumulators: set[str] | None = None)[source]

Bases: Mapping[str, InputSource | SourceHandle]

Tracks which symbols are in scope and where their data comes from.

Immutable-ish: forking for child scopes (e.g. for-node bodies) returns a new instance with remapped symbols.

Accumulators follow a three-stage lifecycle: - declared_accumulators: locally declared via acc = []. Owned by this scope

and passed to child scopes as available_accumulators on fork.

  • available_accumulators: inherited from the parent scope’s declared_accumulators.

    These are the only accumulators a scope is allowed to .append() to. This guarantees that an accumulator is only consumable one nesting level below its declaration, preventing grandparent accumulator access.

  • consumed_accumulators: maps accumulator_name appended_symbol. Populated by

    use_accumulator() and read by the parent to finalise control-flow node outputs.

property all_accumulators: set[str]
property assigned_symbols: list[str]

Identify symbols that were assigned (registered to child nodes) locally.

In a forked scope every inherited symbol starts as an InputSource. Any key whose source is now a SourceHandle must have been assigned by a node inside the branch.

consume(symbol: str, consumer_node: str, consumer_port: str) None[source]

Record that consumer_node.consumer_port reads from symbol.

property edges: dict[TargetHandle, SourceHandle]
fork(symbol_remap: dict[str, str] | None = None, available_accumulators: set[str] | None = None) SymbolScope[source]

Create a child scope for a nested control-flow body.

Every symbol in the current _sources is carried over as a fresh InputSource in the child. symbol_remap allows renaming symbols in transit (e.g. a for-loop replacing the iterable symbol with the iteration variable).

Accumulator propagation is controlled explicitly via available_accumulators. For-loop bodies pass the parent’s declared_accumulators so the body can .append(); while-loop and if/else bodies pass None (the default) to start with an empty set, since those control-flow models do not support cross-iteration accumulation.

The parent’s available_accumulators are always added to the child’s reserved_accumulators so that erroneous grandparent access is caught with a clear error rather than silently ignored.

property input_edges: dict[TargetHandle, InputSource]
property inputs: list[str]

Ordered unique symbols consumed from InputSources.

property output_edges: dict[OutputTarget, SourceHandle | InputSource]
property outputs: list[str]

Ordered unique output port names.

produce(output_port: str, symbol: str | None = None) None[source]

Record that output_port is sourced from symbol.

produce_symbols(symbols: list[str]) None[source]

Record that an output port of the same name is sources from each symbol.

register(new_symbols: list[str], child: LabeledRecipe) None[source]

Map new symbols 1:1 to child node outputs. Enforces uniqueness.

register_accumulator(new: str) None[source]
use_accumulator(accumulator_symbol: str, appended_symbol: str) None[source]