Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,19 @@ priorities.
and assembly-only closure mechanism. Until the projection passes land,
`Syntax` is an empty block and `Commands` is empty, so early use remains
fail-closed while v0.2 `Clauses` behavior is unchanged.
- [ ] Add the structural and command-occurrence projections for the existing
grammar before enabling any control-flow construct.
- [x] Add the parser-owned structural projector and conservative compatibility
flattener. It walks every syntax shape in deterministic authored order,
assigns immediate roles and compositional ancestry, preserves the exact
`Clause` instance and its authored operator, joins parser-owned analysis
facts without mutating compatibility leaves, and discards every partial
projection on malformed, aliased, cyclic, or over-depth structure or
invalid joined facts. Direct tests pin ordering, branch and pipeline
precedence, ancestry coordinates, reference identity, span and enum
validity, value/redirect invariants, safe defaults, copied collections,
and the 16-container bound.
- [ ] Adapt the existing Bash and PowerShell grammars to emit the structural
and command-occurrence projections before enabling any control-flow
construct.
- [ ] Deliver paired Bash and PowerShell `$()` substitution slices for all
locked executable value positions, including ordering, ancestry,
shell-specific cwd propagation, literal/escaped boundaries, dynamic
Expand Down
24 changes: 24 additions & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,30 @@ zero-based `ChildIndex` in the structural collection that owns the
`CommandSubstitutionSyntax`. For a simple command this is its `Substitutions`
collection; for an iterator it is the containing iterator-command collection.

Each ancestry frame describes the relationship from its `AncestorKind` to the
next node on the path. The root block uses `Root`; non-root blocks and command
lists use `Statement`; pipelines use `PipelineStage`; groups use `GroupBody`;
foreach nodes use `Iterator` or `LoopBody`; condition loops use `Condition` or
`LoopBody`; conditionals use `Branch`; conditional-branch nodes use
`Condition` or `Branch`; and command substitutions use `Substitution`.
Repeated children use their zero-based authored index. The `else` child uses
the branch count, placing it after every condition/body pair. Frame source
ranges belong to the ancestor. Blocks, command lists, and groups retain the
incoming immediate role; pipeline stages, iterator/body regions,
condition/body regions, branches, and substitutions replace it with their
nearer execution role.

Projection accepts only a parser-owned tree: a syntax-node or `Clause`
reference cannot appear at two authored positions, node and fragment spans are
either both unavailable or a non-negative start/length pair, and structural
enum values consumed by the projector must be known. Empty blocks remain
valid, but empty pipelines, command lists, and conditionals are malformed.
Joined value domains, effective-argument coordinates, cwd facts, redirect
coordinates, redirect shapes, and heredoc facts must satisfy their contracts.
Any violation discards the partial `Commands` and `Clauses` collections and
makes the outer parse unparseable; it is never published as a complete
occurrence.

`EffectiveArgument.ClauseElementIndex` is a stable authored coordinate. The
analysis never mutates a compatibility `Arg` to hold loop-specific values.
An occurrence can be structurally complete while one effective value remains
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,49 @@ their enum zero values. Ancestry SHALL be ordered outermost to innermost,
exclude the simple-command leaf, and retain child indices and exact-or-null
source ranges for correlation.

Each frame SHALL describe the relationship from its ancestor to the next node
on the path. The root block SHALL use `Root`; non-root blocks and command lists
SHALL use `Statement`; pipelines SHALL use `PipelineStage`; groups SHALL use
`GroupBody`; foreach nodes SHALL use `Iterator` or `LoopBody`; condition loops
SHALL use `Condition` or `LoopBody`; conditionals SHALL use `Branch`;
conditional-branch nodes SHALL use `Condition` or `Branch`; and substitutions
SHALL use `Substitution`. Repeated children SHALL use their zero-based authored
index, with an `else` child indexed after all conditional branches. Frame
source ranges SHALL identify the ancestor. Blocks, command lists, and groups
SHALL retain the incoming immediate role; a nearer pipeline, iterator, body,
condition, branch, or substitution relation SHALL replace it.

#### Scenario: Root and nested block coordinates are deterministic
- **WHEN** a root statement contains a loop-body pipeline
- **THEN** a stage occurrence has outer-to-inner `Root`, `LoopBody`,
`Statement`, and `PipelineStage` ancestry
- **THEN** each repeated relation carries its authored child index

### Requirement: Projection rejects malformed parser-owned structure and facts
The projector SHALL accept only a tree with one syntax-node and one `Clause`
reference per authored simple-command position. Node and source-fragment spans
SHALL be both unavailable or a non-negative start/length pair. Structural enum
values consumed by projection SHALL be known. Empty blocks MAY be valid, but
empty pipelines, command lists, and conditionals SHALL be rejected.

Value domains, cwd facts, effective-argument coordinates, redirect
coordinates, redirect shapes, and heredoc facts SHALL satisfy their locked
record invariants before projection succeeds. Any repeated identity, malformed
shape, invalid coordinate, cycle, or depth overflow SHALL discard every
partial command and compatibility result.

#### Scenario: Shared leaf identity is not counted twice
- **WHEN** an internal parser bug places one syntax leaf or `Clause` reference
at two authored positions
- **THEN** projection fails instead of emitting two occurrences
- **THEN** no partial command or compatibility projection is returned

#### Scenario: Complete occurrence cannot contain invalid facts
- **WHEN** parser-owned analysis supplies an invalid value domain, argument
coordinate, redirect shape, or unknown scope-affecting structural kind
- **THEN** projection fails closed
- **THEN** the occurrence is not published with `IsComplete=true`

#### Scenario: While condition and body roles
- **WHEN** Bash parses `while curl URL; do sleep 1; done`
- **THEN** `curl` is identified as a condition occurrence
Expand Down
4 changes: 2 additions & 2 deletions openspec/changes/v0-3-structured-shell-analysis/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
- [x] 3.1 Add the locked public syntax-node hierarchy and defaults to the public API snapshot.
- [x] 3.2 Add the locked command-occurrence, role, ancestry, completeness, and analysis records to the public API snapshot.
- [x] 3.3 Add `ParsedCommand.Syntax` and `ParsedCommand.Commands` while retaining all v0.2 members.
- [ ] 3.4 Build a library-owned traversal that emits each simple command occurrence exactly once in deterministic source order.
- [ ] 3.5 Build the conservative `Clauses` compatibility flattener without inventing cross-structure compound operators.
- [x] 3.4 Build a library-owned traversal that emits each simple command occurrence exactly once in deterministic source order.
- [x] 3.5 Build the conservative `Clauses` compatibility flattener without inventing cross-structure compound operators.
- [ ] 3.6 Adapt the existing Bash grammar to emit the structural model with no newly supported syntax.
- [ ] 3.7 Adapt the existing PowerShell grammar to emit the structural model with no newly supported syntax.
- [ ] 3.8 Add tests proving existing parser inputs retain their v0.2 leaf and compatibility results.
Expand Down
Loading