Skip to content

Implement transformer parser#66

Merged
leynos merged 4 commits into
mainfrom
codex/implement-transformer-parser
Jul 14, 2025
Merged

Implement transformer parser#66
leynos merged 4 commits into
mainfrom
codex/implement-transformer-parser

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Jul 11, 2025

Summary

  • add transformer span tracking and CST node
  • expose Transformer AST wrapper with accessor methods
  • parse extern transformer declarations and collect spans
  • update parser tests to cover transformer parsing
  • keep Markdown formatting consistent

Testing

  • make lint
  • make test

https://chatgpt.com/codex/tasks/task_e_687194d20f708322999ef4e1be9e7b8e

Summary by Sourcery

Implement transformer parsing end-to-end by tracking transformer spans in the parser, enriching the AST with a Transformer node and accessors, and adding helper utilities and tests to validate transformer declaration parsing.

New Features:

  • Add support for parsing extern transformer declarations with span tracking
  • Introduce a Transformer AST node with methods to access its name, inputs, and outputs
  • Expose transformers() accessor on the root AST to collect all transformer declarations
  • Add a generic collect_extern_declarations helper for parsing extern declarations
  • Add parse_ident_list utility for parsing lists of identifiers after a colon

Enhancements:

  • Extend ParsedSpans, parser logic, and green tree builder to handle transformer spans
  • Apply minor Markdown formatting consistency fix

Tests:

  • Add comprehensive parser tests for transformer declarations covering single/multiple I/O, invalid cases, whitespace handling, duplicate names, and reserved keywords

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Jul 11, 2025

Reviewer's Guide

This PR adds full support for parsing extern transformer declarations by extending span tracking, introducing a generic extern-declaration collector, implementing transformer-specific parsing logic, exposing a Transformer AST wrapper with accessors, updating parse utilities, and adding comprehensive tests.

Class diagram for new and updated AST types (Transformer support)

classDiagram
    class Root {
        +transformers() Vec~Transformer~
    }
    class Transformer {
        +syntax() SyntaxNode
        +name() Option~String~
        +inputs() Vec~(String, String)~
        +outputs() Vec~String~
    }
    Root --> "*" Transformer
Loading

Class diagram for updated ParsedSpans struct (transformer span tracking)

classDiagram
    class ParsedSpans {
        +transformers: Vec~Span~
        +transformers() &[Span]
    }
Loading

Flow diagram for collecting extern transformer declaration spans

flowchart TD
    A[Tokens + Source] --> B[collect_extern_declarations]
    B --> C[collect_transformer_spans]
    C --> D[ParsedSpans.transformers]
    D --> E[build_green_tree]
    E --> F[AST with Transformer nodes]
Loading

File-Level Changes

Change Details Files
Integrate transformer span tracking into the parser
  • Added transformers field and accessor in ParsedSpans
  • Included transformer span collection and error aggregation in parse_tokens
  • Updated build_green_tree to recognize transformer spans
src/parser/mod.rs
Extract generic extern declaration span collector
  • Introduced collect_extern_declarations helper with token_dispatch
  • Replaced in-place extern logic with the generic function
src/parser/mod.rs
Implement collect_transformer_spans for transformer declarations
  • Defined transformer_decl parser combinator for SyntaxKind::K_TRANSFORMER
  • Hooked collect_transformer_spans into the span collection pipeline
src/parser/mod.rs
Add Transformer AST wrapper and root accessor
  • Added root.transformers() to collect transformer nodes
  • Introduced Transformer struct with syntax(), name(), inputs(), outputs() methods
  • Added skip_to_transformer_keyword utility
src/parser/mod.rs
Introduce parse_ident_list utility
  • Added parse_ident_list in parse_utils to extract identifier lists after colon
src/parser/ast/parse_utils.rs
Add parser tests for transformer declarations
  • Added fixtures for various transformer scenarios
  • Added rstest test functions covering valid and invalid cases
tests/parser.rs
Escape markdownlint footnote in documentation
  • Updated documentation-style-guide.md to escape the footnote marker
docs/documentation-style-guide.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 11, 2025

Summary by CodeRabbit

  • New Features
    • Added support for parsing and handling transformer declarations, including recognition of inputs and outputs.
    • Users can now define transformers in their code, similar to existing constructs.
  • Bug Fixes
    • Improved parser handling of whitespace, duplicate input names, and reserved keywords in transformer declarations.
  • Tests
    • Introduced comprehensive test cases for various transformer declaration scenarios and edge cases.
  • Documentation
    • Enhanced inline documentation for new transformer-related parsing logic.

Walkthrough

Integrate support for parsing, collecting, and handling transformer declarations within the parser module. Extend the CST and typed AST to accommodate transformers, introduce new helper functions for parsing output lists, and add comprehensive test coverage for various transformer declaration scenarios and edge cases.

Changes

File(s) Change Summary
src/parser/mod.rs, src/parser/ast/mod.rs, ... Add parsing, span collection, CST and AST support for transformer declarations; update ParsedSpans struct.
src/parser/ast/parse_utils.rs Add parse_output_list for parsing transformer outputs; add helper for skipping to top-level colon.
tests/parser.rs Add fixtures and tests for transformer declarations, covering multiple scenarios and error cases.

Sequence Diagram(s)

sequenceDiagram
    participant Source
    participant Parser
    participant CSTBuilder
    participant AST

    Source->>Parser: Provide tokens (with potential transformer decls)
    Parser->>Parser: Collect transformer spans via collect_transformer_spans
    Parser->>CSTBuilder: Pass transformer spans to build_green_tree
    CSTBuilder->>CSTBuilder: Add transformer nodes to CST
    CSTBuilder->>AST: Provide CST with transformer nodes
    AST->>AST: Expose transformer nodes via Root::transformers()
    AST->>AST: Allow access to name, inputs, outputs of each Transformer
Loading

Possibly related PRs

Poem

Transformers now join the parser’s parade,
With inputs and outputs so carefully made.
Through tests they march, both simple and wild,
No whitespace or keyword left undefiled.
The AST smiles, its family grown,
For now, transformer seeds are sown!
🤖✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c6673d and 9e13b69.

📒 Files selected for processing (2)
  • src/parser/ast/parse_utils.rs (1 hunks)
  • src/parser/mod.rs (18 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

Instructions used from:

Sources:
⚙️ CodeRabbit Configuration File

🧬 Code Graph Analysis (1)
src/parser/mod.rs (4)
src/parser/token_stream.rs (3)
  • tokens (105-107)
  • src (118-120)
  • new (41-47)
src/parser/span_collector.rs (1)
  • new (44-50)
tests/parser.rs (1)
  • text (46-46)
src/parser/ast/parse_utils.rs (3)
  • parse_name_type_pairs (364-428)
  • parse_output_list (461-489)
  • parse_type_after_colon (600-631)
🔇 Additional comments (7)
src/parser/ast/parse_utils.rs (1)

430-444: LGTM!

The helper function correctly tracks parentheses depth and uses saturating_sub to prevent underflow.

src/parser/mod.rs (6)

886-915: LGTM!

The implementation correctly uses the generic helper and properly validates that transformers have at least one output.


1905-1940: LGTM!

The Transformer AST implementation follows established patterns and provides appropriate accessor methods.


214-216: LGTM!

The transformer spans are properly integrated into ParsedSpans following the established pattern.

Also applies to: 237-238, 258-258, 293-298


1023-1024: LGTM!

The transformer spans are correctly integrated into the CST building process.

Also applies to: 1032-1033, 1035-1064


1402-1433: LGTM!

Good refactoring to extract the common skip pattern into a generic helper.


486-501: Add bounds checking when accessing the tokens array.

The current implementation correctly handles whitespace without newlines, but accessing st.stream.tokens().get(idx) could fail if idx goes out of bounds.

Replace line 498 with bounds-checked access:

-        if st.stream.tokens().get(idx).map(|t| t.0) != Some(decl_kind) {
+        if !matches!(st.stream.tokens().get(idx), Some((kind, _)) if *kind == decl_kind) {

Likely an incorrect or invalid review comment.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/implement-transformer-parser

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes and found some issues that need to be addressed.

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/parser/mod.rs:1880` </location>
<code_context>
+
+        /// Name of the transformer if present.
+        #[must_use]
+        pub fn name(&self) -> Option<String> {
+            self.syntax
+                .children_with_tokens()
</code_context>

<issue_to_address>
The name() method assumes a specific token order; consider resilience to formatting.

If there are extra tokens (e.g., whitespace or comments) between K_TRANSFORMER and T_IDENT, this logic may not work as intended. Please verify this aligns with the parser's output in all scenarios.
</issue_to_address>

### Comment 2
<location> `src/parser/mod.rs:834` </location>
<code_context>
 }

+/// Collects the spans of transformer declarations in the token stream.
+fn collect_transformer_spans(
+    tokens: &[(SyntaxKind, Span)],
+    src: &str,
</code_context>

<issue_to_address>
Consider refactoring the various extern declaration collectors into a single generic helper function to eliminate duplicated boilerplate.

Here is one way to collapse all of the “extern X‐decl” collectors (functions, transformers, indexes, …) into a single helper, so that `collect_transformer_spans` simply instantiates that helper with its own parser.  This preserves every bit of behavior but removes nearly all of the duplicated boilerplate.

```rust
// new in your module
fn collect_extern_declarations<P>(
    tokens: &[(SyntaxKind, Span)],
    src: &str,
    decl_kind: SyntaxKind,
    decl_parser: P,
) -> (Vec<Span>, Vec<Simple<SyntaxKind>>)
where
    P: Parser<SyntaxKind, Span, Error = Simple<SyntaxKind>> + Clone,
{
    type State<'a> = SpanCollector<'a, Vec<Simple<SyntaxKind>>>;

    let mut st = State::new(tokens, src, Vec::new());

    // this closure handles both the `extern` prefix and skipping
    let handler = move |st: &mut State<'_>, span: Span| {
        // only parse if the *next* token is the decl we expect
        if !matches!(
            st.stream.peek_after_ws_inline().map(|t| t.0),
            Some(`decl_kind`)
        ) {
            st.skip_line();
            return;
        }

        // build a parser that eats `extern` and then our decl
        let ws = inline_ws().repeated();
        let parser = just(SyntaxKind::K_EXTERN)
            .padded_by(ws.clone())
            .ignore_then(decl_parser.clone());

        let (res, err) = st.parse_span(parser, span.start);
        if let Some(sp) = res {
            st.spans.push(span.start..sp.end);
            st.stream.skip_until(sp.end);
        } else {
            st.extra.extend(err);
            st.skip_line();
        }
    };

    token_dispatch!(st, { SyntaxKind::K_EXTERN => handler });
    st.into_parts()
}

// now your transformer‐specific collector is just one line:
fn collect_transformer_spans(
    tokens: &[(SyntaxKind, Span)],
    src: &str
) -> (Vec<Span>, Vec<Simple<SyntaxKind>>) {
    collect_extern_declarations(
        tokens,
        src,
        SyntaxKind::K_TRANSFORMER,
        transformer_decl(), // your existing parser fn
    )
}
```

Apply the same pattern to the other `collect_*_spans` funcs: factor out the shared `SpanCollector` setup and `token_dispatch!` logic into `collect_extern_declarations`, and leave each domain‐specific collector as a 1‐liner.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/parser/mod.rs
Comment thread src/parser/mod.rs
Use a generic helper to parse extern declarations and simplify transformer span collection. Adjust transformer name parsing for whitespace tolerance.
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 13, 2025

@sourcery-ai resolve

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 14, 2025

@sourcery-ai review

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes and found some issues that need to be addressed.

  • The manual traversal in Transformer::name and outputs is brittle—consider reusing your existing parse_name_type_pairs or other parser combinators to simplify and harden the AST wrapper logic.
  • collect_extern_declarations currently boxes the parser and requires Clone bounds—refactor it to accept parser closures or references to avoid boxing overhead and simplify its signature.
  • The AST accessor methods allocate new Strings on each call; consider returning &str slices or text ranges from the underlying syntax to reduce unnecessary allocations.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The manual traversal in Transformer::name and outputs is brittle—consider reusing your existing parse_name_type_pairs or other parser combinators to simplify and harden the AST wrapper logic.
- collect_extern_declarations currently boxes the parser and requires Clone bounds—refactor it to accept parser closures or references to avoid boxing overhead and simplify its signature.
- The AST accessor methods allocate new Strings on each call; consider returning &str slices or text ranges from the underlying syntax to reduce unnecessary allocations.

## Individual Comments

### Comment 1
<location> `tests/parser.rs:908` </location>
<code_context>
+
+#[rstest]
+#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
+fn transformer_single_parsed(transformer_single_io: &str) {
+    let parsed = parse(transformer_single_io);
+    assert!(parsed.errors().is_empty());
+    let transformers = parsed.root().transformers();
+    assert_eq!(transformers.len(), 1);
+    let t = transformers.first().expect("transformer missing");
+    assert_eq!(t.name(), Some("normalize".into()));
+    assert_eq!(
+        t.inputs(),
+        vec![("input".into(), "UnnormalizedData".into())]
+    );
+    assert_eq!(t.outputs(), vec![String::from("NormalizedData")]);
+}
+
</code_context>

<issue_to_address>
Consider adding tests for edge cases in transformer parsing.

Please add tests for cases like transformers with no inputs or outputs, extra whitespace, duplicate input names, and reserved keywords as input/output names to ensure comprehensive parser coverage.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
#[rstest]
#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
fn transformer_single_parsed(transformer_single_io: &str) {
    let parsed = parse(transformer_single_io);
    assert!(parsed.errors().is_empty());
    let transformers = parsed.root().transformers();
    assert_eq!(transformers.len(), 1);
    let t = transformers.first().expect("transformer missing");
    assert_eq!(t.name(), Some("normalize".into()));
    assert_eq!(
        t.inputs(),
        vec![("input".into(), "UnnormalizedData".into())]
    );
    assert_eq!(t.outputs(), vec![String::from("NormalizedData")]);
}
=======
#[rstest]
#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
fn transformer_single_parsed(transformer_single_io: &str) {
    let parsed = parse(transformer_single_io);
    assert!(parsed.errors().is_empty());
    let transformers = parsed.root().transformers();
    assert_eq!(transformers.len(), 1);
    let t = transformers.first().expect("transformer missing");
    assert_eq!(t.name(), Some("normalize".into()));
    assert_eq!(
        t.inputs(),
        vec![("input".into(), "UnnormalizedData".into())]
    );
    assert_eq!(t.outputs(), vec![String::from("NormalizedData")]);
}

#[rstest]
#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
fn transformer_no_inputs() {
    let src = "extern transformer no_inputs(): OutputType";
    let parsed = parse(src);
    assert!(parsed.errors().is_empty());
    let transformers = parsed.root().transformers();
    assert_eq!(transformers.len(), 1);
    let t = transformers.first().expect("transformer missing");
    assert_eq!(t.name(), Some("no_inputs".into()));
    assert!(t.inputs().is_empty());
    assert_eq!(t.outputs(), vec![String::from("OutputType")]);
}

#[rstest]
#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
fn transformer_no_outputs() {
    let src = "extern transformer no_outputs(input: InputType):";
    let parsed = parse(src);
    // Should either error or produce a transformer with empty outputs
    let transformers = parsed.root().transformers();
    if parsed.errors().is_empty() {
        assert_eq!(transformers.len(), 1);
        let t = transformers.first().expect("transformer missing");
        assert_eq!(t.name(), Some("no_outputs".into()));
        assert_eq!(t.inputs(), vec![("input".into(), "InputType".into())]);
        assert!(t.outputs().is_empty());
    } else {
        // Acceptable if parser errors on missing outputs
        assert!(!parsed.errors().is_empty());
    }
}

#[rstest]
#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
fn transformer_extra_whitespace() {
    let src = " extern   transformer   spaced  (  foo  :  Bar  ,  baz : Qux )  :  Out1 , Out2 ";
    let parsed = parse(src);
    assert!(parsed.errors().is_empty());
    let transformers = parsed.root().transformers();
    assert_eq!(transformers.len(), 1);
    let t = transformers.first().expect("transformer missing");
    assert_eq!(t.name(), Some("spaced".into()));
    assert_eq!(
        t.inputs(),
        vec![
            ("foo".into(), "Bar".into()),
            ("baz".into(), "Qux".into())
        ]
    );
    assert_eq!(t.outputs(), vec!["Out1".to_string(), "Out2".to_string()]);
}

#[rstest]
#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
fn transformer_duplicate_input_names() {
    let src = "extern transformer dup_inputs(foo: Bar, foo: Baz): Out";
    let parsed = parse(src);
    // Should error or handle duplicate input names
    if parsed.errors().is_empty() {
        let transformers = parsed.root().transformers();
        assert_eq!(transformers.len(), 1);
        let t = transformers.first().expect("transformer missing");
        // Should not allow duplicate input names
        let inputs = t.inputs();
        let foo_count = inputs.iter().filter(|(name, _)| name == "foo").count();
        assert!(foo_count <= 1, "Duplicate input names should not be allowed");
    } else {
        // Acceptable if parser errors on duplicate names
        assert!(!parsed.errors().is_empty());
    }
}

#[rstest]
#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
fn transformer_reserved_keyword_names() {
    let src = "extern transformer reserved(transformer: Type, extern: Type): out";
    let parsed = parse(src);
    // Should error or handle reserved keywords as names
    if parsed.errors().is_empty() {
        let transformers = parsed.root().transformers();
        assert_eq!(transformers.len(), 1);
        let t = transformers.first().expect("transformer missing");
        let inputs = t.inputs();
        // Check if reserved keywords are present as input names
        assert!(!inputs.iter().any(|(name, _)| name == "transformer" || name == "extern"),
            "Reserved keywords should not be allowed as input names");
    } else {
        // Acceptable if parser errors on reserved keywords
        assert!(!parsed.errors().is_empty());
    }
}
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread tests/parser.rs
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 14, 2025

@sourcery-ai resolve

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 14, 2025

@sourcery-ai review

Copy link
Copy Markdown

@SourceryAI SourceryAI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes and found some issues that need to be addressed.

  • The skip_to_transformer_keyword helper duplicates token-skipping logic; consider abstracting a generic skip_to_kind utility to use across different AST wrappers.
  • The parse_ident_list function manually tracks parentheses depth to locate the colon; you could leverage the existing balanced_block or a token-based parser combinator to simplify and remove manual depth-tracking.
  • The inline_ws().repeated() padding logic is repeated in transformer_decl; extracting a shared padded combinator or alias for common whitespace patterns would DRY up the parser definitions.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The skip_to_transformer_keyword helper duplicates token-skipping logic; consider abstracting a generic skip_to_kind utility to use across different AST wrappers.
- The parse_ident_list function manually tracks parentheses depth to locate the colon; you could leverage the existing balanced_block or a token-based parser combinator to simplify and remove manual depth-tracking.
- The inline_ws().repeated() padding logic is repeated in transformer_decl; extracting a shared padded combinator or alias for common whitespace patterns would DRY up the parser definitions.

## Individual Comments

### Comment 1
<location> `src/parser/mod.rs:486` </location>
<code_context>
+    let mut st = State::new(tokens, src, Vec::new());
+
+    let handler = move |st: &mut State<'_>, span: Span| {
+        if st.stream.peek_after_ws_inline().map(|t| t.0) != Some(decl_kind) {
+            st.skip_line();
+            return;
</code_context>

<issue_to_address>
The check for decl_kind after K_EXTERN may skip valid declarations if whitespace/comments intervene.

This logic may fail if comments or other tokens appear between K_EXTERN and the declaration kind. Please ensure the check handles all valid input scenarios.
</issue_to_address>

Hi @leynos! 👋

Thanks for trying out Sourcery by commenting with @sourcery-ai review! 🚀

Install the sourcery-ai bot to get automatic code reviews on every pull request ✨

Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/parser/mod.rs Outdated
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes and found some issues that need to be addressed.

  • The parse_ident_list helper is currently specialized for transformer outputs; consider renaming it or documenting its exact contract to clarify its purpose and avoid confusion when reused elsewhere.
  • The build_green_tree function now has duplicate maybe_start/maybe_finish calls for transformers similar to other node kinds—consider abstracting this pattern into a helper to reduce boilerplate and simplify future maintenance.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The parse_ident_list helper is currently specialized for transformer outputs; consider renaming it or documenting its exact contract to clarify its purpose and avoid confusion when reused elsewhere.
- The build_green_tree function now has duplicate maybe_start/maybe_finish calls for transformers similar to other node kinds—consider abstracting this pattern into a helper to reduce boilerplate and simplify future maintenance.

## Individual Comments

### Comment 1
<location> `docs/documentation-style-guide.md:109` </location>
<code_context>
 ```

-[^markdownlint]: A linter that enforces consistent Markdown formatting.
+\[^markdownlint\]: A linter that enforces consistent Markdown formatting.
</code_context>

<issue_to_address>
Escaping in footnote reference is unnecessary and may break Markdown rendering.

The '[^markdownlint]:' syntax is correct in Markdown. Please revert the escaping unless there's a specific need for it.
</issue_to_address>

### Comment 2
<location> `src/parser/ast/parse_utils.rs:432` </location>
<code_context>

+/// Parse an identifier list following a colon.
+#[must_use]
+pub(super) fn parse_ident_list<I>(iter: I) -> Vec<String>
+where
+    I: Iterator<Item = SyntaxElement<DdlogLanguage>>,
</code_context>

<issue_to_address>
Consider reusing an existing separated-by combinator and extracting depth counting into a helper to simplify parse_ident_list.

Consider reusing your existing “separated‐by” combinator instead of rolling manual loops/peekables. For example, if you have something like:

```rust
fn parse_separated<I, F, T>(
    iter: &mut Peekable<I>,
    sep: SyntaxKind,
    mut f: F,
) -> Vec<T>
where
    I: Iterator<Item = SyntaxElement<DdlogLanguage>>,
    F: FnMut(&mut Peekable<I>) -> T,
{ /* … */ }
```

you could rewrite `parse_ident_list` as:

```rust
use rowan::NodeOrToken;

fn skip_to_top_level_colon<I>(iter: &mut Peekable<I>) 
where I: Iterator<Item = SyntaxElement<DdlogLanguage>> {
    let mut depth = 0;
    while let Some(elem) = iter.next() {
        match elem.kind() {
            SyntaxKind::T_LPAREN => depth += 1,
            SyntaxKind::T_RPAREN => depth = depth.saturating_sub(1),
            SyntaxKind::T_COLON if depth == 0 => break,
            _ => {}
        }
    }
}

pub(super) fn parse_ident_list<I>(iter: I) -> Vec<String>
where
    I: Iterator<Item = SyntaxElement<DdlogLanguage>>,
{
    let mut iter = iter.peekable();
    skip_to_top_level_colon(&mut iter);
    // consume the colon itself
    iter.next();

    parse_separated(&mut iter, SyntaxKind::T_COMMA, |it| {
        skip_whitespace_and_comments(it);
        match it.next() {
            Some(NodeOrToken::Token(t)) if t.kind() == SyntaxKind::T_IDENT => {
                t.text().to_string()
            }
            _ => unreachable!("expected identifier after comma"),
        }
    })
}
```

This:

- Moves your “depth counting” into a small helper (`skip_to_top_level_colon`)
- Delegates comma-separated iteration to `parse_separated`
- Keeps whitespace/comments skipping inside the short closure

Result: identical behavior with far less procedural boilerplate.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread docs/documentation-style-guide.md Outdated
Comment thread src/parser/ast/parse_utils.rs Outdated
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 14, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 14, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 14, 2025

@sourcery-ai resolve

@leynos leynos merged commit 04b93bc into main Jul 14, 2025
2 checks passed
@leynos leynos deleted the codex/implement-transformer-parser branch July 14, 2025 18:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants