Skip to content

allow default conversion for optional tokens (fixes #32)#38

Merged
tomtau merged 2 commits intomasterfrom
default-token
Aug 4, 2025
Merged

allow default conversion for optional tokens (fixes #32)#38
tomtau merged 2 commits intomasterfrom
default-token

Conversation

@tomtau
Copy link
Contributor

@tomtau tomtau commented Aug 4, 2025

fixes #32

Summary by CodeRabbit

  • New Features

    • Added support for specifying default values for AST fields using a new attribute, allowing fields to have default values when optional grammar rules are absent.
  • Documentation

    • Extended documentation with a detailed explanation and examples for the new default attribute, demonstrating how to simplify ASTs by avoiding Option types.
  • Examples

    • Introduced new example grammars and Rust programs showcasing default values in variable and function declarations.
  • Style

    • Updated print statements throughout examples and error messages to use Rust’s named argument formatting for improved clarity.

@coderabbitai
Copy link

coderabbitai bot commented Aug 4, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This update introduces support for specifying default values for optional grammar rules in Pest ASTs using a new #[pest_ast(default(...))] attribute. It adds documentation and example grammars and ASTs demonstrating the feature, updates the procedural macro implementation to parse and apply the default attribute, and modernizes format string usage in several example files.

Changes

Cohort / File(s) Change Summary
Crate Version Bumps
Cargo.toml, derive/Cargo.toml
Bumped dependency versions: from-pest to 0.3.4 and pest-ast to 0.3.6.
Documentation
derive/README.md
Added a section explaining the new #[pest_ast(default(...))] attribute, with usage examples and generated code behavior.
New Example: Defaults Showcase
derive/examples/defaults_showcase.pest, derive/examples/defaults_showcase.rs
Introduced a grammar and Rust example demonstrating variable declarations with defaulted fields using the new attribute.
New Example: Function Defaults
derive/examples/function_defaults.pest, derive/examples/function_defaults.rs
Added a grammar and Rust example for function declarations, showcasing default return types via the new attribute.
Procedural Macro: Attribute Parsing
derive/src/attributes.rs
Added parsing and tokenization logic for the new default attribute in the macro system.
Procedural Macro: Field Conversion
derive/src/from_pest/field.rs
Implemented support for applying default values when parsing fields, using the new attribute.
Example Output Formatting
derive/examples/simple_enum_derives.rs, derive/examples/simple_struct_derives.rs, examples/csv.rs, src/lib.rs
Updated println! and write! calls to use Rust’s named argument formatting syntax for improved clarity.

Sequence Diagram(s)

sequenceDiagram
    participant Input as User Input
    participant Parser as Pest Parser
    participant Macro as pest_ast Macro
    participant AST as AST Struct

    Input->>Parser: Provide source code
    Parser->>Macro: Parse grammar rules
    Macro->>AST: Attempt field parsing
    alt Field present
        Macro->>AST: Assign parsed value
    else Field absent (NoMatch)
        Macro->>AST: Assign default value (from #[pest_ast(default(...))])
    else Other error
        Macro->>AST: Propagate error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~18 minutes

Assessment against linked issues

Objective Addressed Explanation
Allow default tokens to be returned for optional tokens in ASTs (#32)
Enable specifying default values for optional grammar rules via attribute syntax (#32)
Provide documentation and examples for new default attribute usage (#32)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes were found.

Poem

A rabbit hops through fields of code,
Where defaults now lighten the parser’s load.
Optional tokens? No more despair—
Just add an attribute, and values appear!
With examples and docs, the path is clear,
Hooray for defaults—let’s all give a cheer!
🥕✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch default-token

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 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.
  • 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.

Support

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

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 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.

@tomtau tomtau requested a review from Copilot August 4, 2025 07:19
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds support for default values in optional tokens when deriving FromPest, allowing fields to have fallback values instead of requiring Option<T> types. It also updates string formatting to use newer Rust syntax.

  • Introduces the #[pest_ast(default(...))] attribute for specifying default values for optional fields
  • Updates string formatting from format!("{}", var) to format!("{var}") throughout examples
  • Adds comprehensive examples and tests demonstrating the new default value functionality

Reviewed Changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/lib.rs Updates error message formatting to use newer string interpolation syntax
examples/csv.rs Updates println! statements to use string interpolation instead of positional formatting
derive/src/from_pest/field.rs Adds Default conversion strategy and implements logic for handling default values
derive/src/attributes.rs Adds parsing support for the new default attribute with custom keyword and expression handling
derive/examples/simple_struct_derives.rs Updates println! formatting to use string interpolation
derive/examples/simple_enum_derives.rs Updates println! formatting to use string interpolation
derive/examples/function_defaults.rs New example demonstrating default values for function return types
derive/examples/function_defaults.pest Grammar file for the function defaults example
derive/examples/defaults_showcase.rs Comprehensive example showcasing multiple default value scenarios
derive/examples/defaults_showcase.pest Grammar file for the defaults showcase example
derive/README.md Adds documentation explaining the new default values feature
derive/Cargo.toml Bumps version from 0.3.5 to 0.3.6
Cargo.toml Bumps version from 0.3.3 to 0.3.4
Comments suppressed due to low confidence (1)

derive/examples/function_defaults.rs:74

  • [nitpick] This error case returns a generic NoMatch error. Consider providing a more specific error message that indicates the expected type names were not found.
    pub id: Id<'pest>,

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (5)
derive/examples/simple_struct_derives.rs (1)

57-60: Same MSRV consideration as noted in src/lib.rs – the new inline capture requires Rust 1.58+.

derive/examples/simple_enum_derives.rs (1)

61-64: Same MSRV consideration as noted in src/lib.rs – the new inline capture requires Rust 1.58+.

examples/csv.rs (1)

59-60: Same MSRV consideration as noted in src/lib.rs – the new inline capture requires Rust 1.58+.

Also applies to: 74-75

derive/examples/defaults_showcase.rs (2)

74-74: Handle parsing errors instead of using unwrap().

Using unwrap() on parse() can cause panics if the string is not a valid number. Consider using proper error handling even in example code.

-                    Rule::number => Ok(Expr::Number(inner_pair.as_str().parse().unwrap())),
+                    Rule::number => inner_pair
+                        .as_str()
+                        .parse()
+                        .map(Expr::Number)
+                        .map_err(|_| from_pest::ConversionError::NoMatch),

83-83: Handle parsing errors instead of using unwrap().

Using unwrap() on parse() can cause panics if the string is not a valid number. Consider using proper error handling even in example code.

-            Rule::number => Ok(Expr::Number(pair.as_str().parse().unwrap())),
+            Rule::number => pair
+                .as_str()
+                .parse()
+                .map(Expr::Number)
+                .map_err(|_| from_pest::ConversionError::NoMatch),
🧹 Nitpick comments (4)
derive/examples/defaults_showcase.pest (1)

14-15: string rule doesn’t handle escaped quotes.

string = { "\"" ~ (!("\"") ~ ANY)* ~ "\"" }

This fails for inputs like "a \"quote\" inside".
A minimal fix is to allow backslash-escaped quotes:

string = { "\"" ~ ( "\\\"" | !("\"") ~ ANY )* ~ "\"" }
derive/examples/function_defaults.pest (2)

8-9: type_name sub-rule order may cause needless backtracking.

{ "void" | "int" | "string" | id } is fine, but because "void"/"int"/"string" are all valid ids, the parser first checks three literals and then still falls back to id. The extra checks are negligible here, yet for large grammars it can hurt performance.

Micro-optimisation:

type_name = _{ id | "void" | "int" | "string" }

(or remove id if the identifiers cannot overlap).


15-15: Trailing comma not allowed in parameter list.

params = { param ~ ("," ~ param)* } forbids fn f(a: int,) {}.
If you want to permit a trailing comma, change to:

params = { param ~ ("," ~ param)* ~ ("," )? }
derive/README.md (1)

310-371: Docs look great – tiny wording nit.

Line 368: “If conversion fails with NoMatch (optional rule not present), uses the default value” → consider “the default value is used” for voice consistency with the surrounding bullet points.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 624e338 and 76b9720.

📒 Files selected for processing (13)
  • Cargo.toml (1 hunks)
  • derive/Cargo.toml (1 hunks)
  • derive/README.md (1 hunks)
  • derive/examples/defaults_showcase.pest (1 hunks)
  • derive/examples/defaults_showcase.rs (1 hunks)
  • derive/examples/function_defaults.pest (1 hunks)
  • derive/examples/function_defaults.rs (1 hunks)
  • derive/examples/simple_enum_derives.rs (1 hunks)
  • derive/examples/simple_struct_derives.rs (1 hunks)
  • derive/src/attributes.rs (6 hunks)
  • derive/src/from_pest/field.rs (3 hunks)
  • examples/csv.rs (2 hunks)
  • src/lib.rs (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
derive/examples/defaults_showcase.rs (4)
src/lib.rs (6)
  • from_pest (76-78)
  • from_pest (85-87)
  • from_pest (94-96)
  • from_pest (103-108)
  • from_pest (115-125)
  • from_pest (132-134)
derive/examples/function_defaults.rs (2)
  • from_pest (36-51)
  • main (89-103)
derive/src/from_pest/mod.rs (1)
  • derive (33-111)
derive/src/attributes.rs (10)
  • parse (178-187)
  • parse (191-202)
  • parse (225-231)
  • parse (243-250)
  • parse (263-280)
  • parse (299-306)
  • parse (318-346)
  • parse (361-368)
  • input (112-112)
  • input (153-153)
derive/src/from_pest/field.rs (3)
src/lib.rs (6)
  • from_pest (76-78)
  • from_pest (85-87)
  • from_pest (94-96)
  • from_pest (103-108)
  • from_pest (115-125)
  • from_pest (132-134)
derive/examples/function_defaults.rs (1)
  • from_pest (36-51)
derive/examples/defaults_showcase.rs (3)
  • from_pest (35-50)
  • from_pest (64-91)
  • from_pest (104-117)
🔇 Additional comments (22)
src/lib.rs (1)

40-43: MSRV bump required for captured-format-string syntax

The inline variable capture used in "{fatalerror}" / "{current_node}" needs Rust 1.58+.
Please verify that the crate’s declared MSRV in Cargo.toml (or CI matrix) is ≥ 1.58, otherwise downstream users on older toolchains will break.

derive/Cargo.toml (1)

3-3: Ensure downstream crates & docs are updated to 0.3.6.

The version bump is fine here, but double-check that:

  1. CHANGELOG.md (if any) and release notes mention 0.3.6.
  2. The root crate (from-pest) and the workspace Cargo.lock are regenerated so that CI tests the new version.

A quick cargo check --workspace after running cargo update -p pest-ast will expose any version-mismatch surprises.

derive/examples/defaults_showcase.pest (1)

6-9: Ambiguity: both type_annotation and initializer are optional.

As written let x ; parses successfully (both options omitted).
If that’s intentional—great. If not, consider enforcing at least one of them:

var_decl = { var_kind ~ id ~ (type_annotation | initializer | type_annotation ~ initializer) ~ ";" }
Cargo.toml (1)

6-6: Confirm crate version bump propagates to published artefact.

from-pest now advertises 0.3.4. Make sure:

  • derive/Cargo.toml dev-dependency range ("0.3") still picks up 0.3.4.
  • The tag v0.3.4 is created when publishing to crates.io.

No code changes needed—just a release-process reminder.

derive/src/from_pest/field.rs (3)

18-18: LGTM! Well-structured enum variant addition.

The Default variant follows the established pattern of including a Span for error reporting and uses syn::Expr to store the default expression, which provides the necessary flexibility for arbitrary default values.


43-45: LGTM! Consistent attribute handling implementation.

The new match arm properly extracts the span and expression from the DefaultAttribute, following the same pattern as other field attribute variants.


94-105: Excellent implementation of the fallback mechanism!

The Default conversion strategy correctly implements the desired behavior:

  1. Attempts standard FromPest::from_pest parsing first
  2. Falls back to the default expression on NoMatch errors
  3. Properly propagates other errors without masking them

This implementation aligns perfectly with the PR objective of allowing default tokens for optional parsing rules.

derive/src/attributes.rs (6)

24-24: LGTM! Proper custom keyword definition.

The default keyword is correctly defined following the established pattern used by other custom keywords in this module.


43-44: LGTM! Well-documented enum variant addition.

The Default variant is properly added to the FieldAttribute enum with appropriate documentation following the established pattern.


86-91: LGTM! Well-structured attribute definition.

The DefaultAttribute struct correctly captures all necessary components for parsing default(expr) syntax: the keyword token, parentheses, and the expression. The structure follows the established pattern used by other attribute structs.


197-198: LGTM! Correct lookahead parsing logic.

The lookahead check for the default keyword is properly integrated into the FieldAttribute parsing logic, following the same pattern as other attribute types.


360-378: LGTM! Complete Parse and ToTokens implementations.

Both implementations are correctly structured:

  • Parse::parse properly extracts the keyword, parentheses, and expression from the input stream
  • ToTokens::to_tokens correctly reconstructs the default(expr) syntax using the stored components

The implementations follow the established patterns used by other attribute types in this file.


219-219: LGTM! Complete ToTokens implementation.

The Default variant is properly handled in the ToTokens implementation, ensuring consistent token conversion for all FieldAttribute variants.

derive/examples/function_defaults.rs (4)

22-52: LGTM! Well-implemented Type enum with proper defaults.

The Type enum is well-structured with:

  • Appropriate use of #[default] on the Void variant
  • Correct FromPest implementation that handles the type_name rule
  • Proper error handling with NoMatch for unrecognized types

The implementation follows the established patterns from other examples in the codebase.


70-81: Excellent demonstration of the default attribute feature!

The Function struct perfectly demonstrates the new #[pest_ast(default(Type::Void))] attribute functionality:

  • Instead of using Option<Type>, it directly uses Type with a sensible default
  • The default value Type::Void is appropriate for functions without explicit return types
  • This eliminates the need for manual post-processing of the AST to handle missing return types

This implementation directly addresses the PR objective of simplifying optional token handling.


89-103: LGTM! Clear and comprehensive demonstration.

The main function provides excellent examples showcasing both scenarios:

  1. Function without return type (demonstrates default behavior)
  2. Function with explicit return type (shows normal parsing)

The error handling and output formatting make it easy to understand the feature's behavior.


105-136: LGTM! Comprehensive test coverage.

The tests effectively verify the default attribute functionality:

  • test_function_with_default_return_type confirms that Type::Void is applied when no return type is specified
  • test_function_with_explicit_return_type ensures explicit values override defaults correctly

The assertions are clear and the test coverage is appropriate for demonstrating the feature.

derive/examples/defaults_showcase.rs (5)

22-51: LGTM! Clean Type enum implementation.

The Type enum and its FromPest implementation are well-structured with proper error handling and clear variant matching logic.


94-118: LGTM! Clean VarKind enum implementation.

The VarKind enum and its FromPest implementation are correctly structured with appropriate error handling for the var_kind rule.


145-162: Excellent comprehensive demonstration of multiple default values!

The VarDecl struct showcases the full power of the default attribute feature:

  • Simple enum default: #[pest_ast(default(VarKind::Let))]
  • Complex struct default: #[pest_ast(default(TypeAnnotation { type_name: Type::Void }))]
  • Nested expression default: #[pest_ast(default(Initializer { expr: Expr::Number(0) }))]

This demonstrates that the default attribute can handle arbitrary Rust expressions, making it very flexible for real-world use cases.


170-202: LGTM! Comprehensive feature demonstration.

The main function provides excellent progressive examples:

  1. Minimal declaration (all defaults applied)
  2. With type annotation (partial defaults)
  3. With initialization (partial defaults)
  4. Fully specified (no defaults)

This clearly shows how the default attribute works in different scenarios.


204-239: LGTM! Thorough test coverage.

The tests effectively verify the default attribute functionality:

  • test_defaults_applied confirms all three default values are applied correctly
  • test_explicit_values_override_defaults ensures explicit values properly override defaults

The comprehensive assertions check all aspects of the parsed AST, providing confidence in the feature's correctness.

@tomtau tomtau merged commit 4f45bb9 into master Aug 4, 2025
6 checks passed
@tomtau tomtau deleted the default-token branch August 4, 2025 07:27
@oxcrow
Copy link

oxcrow commented Aug 4, 2025

@tomtau Thank you for implementing this feature. 🚀

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.

Feature Request: Allow default tokens to be returned, for optional tokens

3 participants