# PTBlock and PTBlockSequence

`PTBlock` is the content of one basic scope: a vector of [`PTModel`](ptmodel.md), a vector of [`PTInstance`](ptmodel.md), a vector of [`PTBehavioral`](ptbehavioral.md) (behavioral source instances), plus - lazily allocated via `unique_ptr`, only if actually needed - a vector of nested `PTBlockSequence` for `@if` chains directly inside this block. The lazy allocation matters because most `PTBlock`s (an ordinary top-level or subcircuit body with no conditional blocks) never need it; paying for an empty `vector<PTBlockSequence>` on every single one would be pure waste. [`ParserTables::processBehaviorals()`](parsertables.md) walks every block's `behaviorals()` and, for each entry, adds a generated `PTModel`/`PTInstance` pair to that same block - it does not remove the original `PTBehavioral` entries, so after processing a block holds both its source behaviorals and the models/instances generated from them.

`PTBlockSequenceEntry` is `std::tuple<Loc, Rpn, PTBlock>`: one arm of an `@if`/`@elseif`/`@else` chain - where the arm started, its condition [expression](rpnexpr.md) (empty, for `@else`), and the block of statements it guards. `PTBlockSequence` is the ordered vector of these entries making up one whole `@if...@end` chain; `back()` gives mutable access to whichever arm's block was most recently opened - exactly what [the parser](dflparser.md)'s `condblock_build` rule uses to fold each subsequent `instance`/`model`/nested `condblock` into the currently-open arm.

`PTBlock::add(PTBlockSequence&&)` is declared inside `PTBlock` but its body is defined just after `PTBlockSequence`, with a comment explaining why: `PTBlockSequenceEntry` embeds a `PTBlock` by value, so `PTBlockSequence` can't be made a complete type before `PTBlock` without becoming circular - the method is declared where `PTBlock` needs it and only defined once `PTBlockSequence` is fully known.

[`PTSubcircuitDefinition`](ptsubckt.md) holds one `PTBlock` (its `root_`) for the definition's own top-level content.
