Skip to content

feat: enrich AST with spans, structured lists, and pipe separators#7

Merged
mpecan merged 3 commits intomainfrom
feat/ast-enrichment
Mar 25, 2026
Merged

feat: enrich AST with spans, structured lists, and pipe separators#7
mpecan merged 3 commits intomainfrom
feat/ast-enrichment

Conversation

@mpecan
Copy link
Copy Markdown
Owner

@mpecan mpecan commented Mar 25, 2026

Summary

  • Span infrastructure: Rename Node enum → NodeKind, wrap in Node { kind: NodeKind, span: Span } struct with source_text() convenience method
  • Separated assignments: Command variant gains assignments: Vec<Node> field distinct from words (ready for lexer AssignmentWord support)
  • Structured lists: Replace List { parts: Vec<Node> } (interleaved commands/operators) with List { items: Vec<ListItem> } using typed ListOperator enum; remove Operator variant
  • Pipeline separators: Add separators: Vec<PipeSep> to Pipeline distinguishing | from |&; remove PipeBoth node variant
  • S-expression output remains byte-identical to Parable — all existing tests pass unchanged

Motivation

Downstream developer feedback identified the lack of source positions and structured metadata as the primary pain point when consuming the AST. This eliminates the need for fragile workarounds like source.find(word), scanning for operators by string matching, and looks_like_assignment() heuristics.

Test plan

  • All 52 tests pass (44 unit + 8 integration)
  • cargo clippy --all-targets -- -D warnings clean
  • cargo fmt --check clean
  • S-expression output unchanged (Parable compatibility)
  • New tests for ListItem structure, PipeSep, Command.assignments, source_text()

🤖 Generated with Claude Code

mpecan and others added 3 commits March 25, 2026 07:36
…ssignments

Add source position tracking, structured command lists, pipe operator
metadata, and separated variable assignments based on downstream developer
feedback. S-expression output remains byte-identical to Parable.

- Rename `Node` enum to `NodeKind`, wrap in `Node { kind, span }` struct
- Add `Span { start, end }` with `source_text()` convenience method
- Add `assignments` field to `Command` (separate from `words`)
- Replace `List { parts }` with `List { items: Vec<ListItem> }` using
  typed `ListOperator` enum; remove `Operator` variant
- Add `separators: Vec<PipeSep>` to `Pipeline`; remove `PipeBoth` variant
- Add tests for new structural features

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ailures

- Fix format_list dropping trailing operators (e.g., `cmd &` in command
  substitutions lost the `&`) by writing the last item's operator
- Assert on Parable compatibility test failures so CI catches regressions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the single monolithic `parable_test_suite` test with 36 individual
`#[test]` functions (one per .tests file) using a declarative macro. Each
file now appears as a separate test in `cargo test` output, making failures
easier to identify and allowing targeted re-runs like
`cargo test parable_12_command_substitution`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mpecan mpecan merged commit 53269b7 into main Mar 25, 2026
5 checks passed
mpecan added a commit that referenced this pull request Mar 25, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.1.6](rable-v0.1.5...rable-v0.1.6)
(2026-03-25)


### Features

* enrich AST with spans, structured lists, and pipe separators
([#7](#7))
([53269b7](53269b7))
* enrich AST with spans, structured lists, pipe separators, and
assignments
([7eec43e](7eec43e))
* improve Rust developer experience
([2ac7654](2ac7654))


### Bug Fixes

* trailing list operators in format and assert on Parable compat
failures
([3a98be1](3a98be1))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
mpecan added a commit that referenced this pull request Mar 25, 2026
## Summary

Follows up on #7 (AST enrichment) to actually populate spans on all
nodes.

- Add `last_token_end` tracking to Lexer for precise span endpoints
- Add `peek_pos()`, `span_from()`, `spanned()` helpers to Parser
- Populate spans on all node types: Command, Word, Pipeline, List,
Redirect, HereDoc, compound commands, conditional expressions, Negation,
Time
- Fix `source_text()` char-index-to-byte-offset conversion
- 17 span coverage tests (100% line coverage on span logic)

## Test plan

- [x] 103 tests pass (60 unit + 37 integration + 6 doc)
- [x] `cargo clippy --all-targets -- -D warnings` clean
- [x] Span-related code in ast.rs at 100% line coverage

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant