Repository navigation
Replace Regex with lazy-regex - #7
Conversation
sbryngelson
left a comment
There was a problem hiding this comment.
Verified this as behavior-preserving:
cargo build,cargo test(336 tests),cargo fmt --check, andcargo clippy --all-targets -- -D warningsall clean.- Mechanically extracted every raw-string regex literal from
classifier.rs,formatter.rs,keyword_norm.rs, andwhitespace.rsonmastervs. this branch and diffed them: identical, no pattern altered by the rewrap. - Confirmed
lazy_regex::regex_replace!isreplacen(..., 1)(leftmost-only), matching the oldRegex::replaceinremove_fypp_macro_paren_space— aregex_replace_all!there would have been a behavior change; it isn't. - Differential test: release binaries from both branches run over 900 real Fortran files plus all repo fixtures — byte-identical output on every one.
- Builds and passes against the minimum declared
lazy-regex 3.3.0and with thelitefeature forced on.
Worth noting the perf angle: this removes several Regex::new(...).unwrap() calls that were recompiling their regex on every call (is_end_procedure, is_procedure_start, is_major_end_block, is_procedure_opener, separate_declarations_from_code), which is a real hot-path win beyond the boilerplate cleanup.
Two non-blocking comments inline, both in keyword_norm.rs; the second one subsumes the first.
|
Thanks @sbryngelson, that's much cleaner! I can also confirm that there's no performance hit from doing it that way either |
There was a problem hiding this comment.
🟢 Approval recommended
The migration preserves existing regex behavior while eliminating repeated compilation costs.
Pull request overview
Replaces manual regex construction/caching with compile-time-checked, lazily initialized lazy-regex macros to improve formatter performance.
Changes:
- Migrates regex usage across formatting, classification, keyword, and whitespace logic.
- Replaces the
regexdependency withlazy-regex.
File summaries
| File | Description |
|---|---|
Cargo.toml |
Updates the regex dependency. |
src/classifier.rs |
Migrates classification patterns to lazy macros. |
src/formatter.rs |
Lazily initializes formatting regexes. |
src/keyword_norm.rs |
Migrates keyword-normalization patterns. |
src/whitespace.rs |
Migrates whitespace-normalization patterns. |
Review details
- Files reviewed: 5/5 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Nice — verified this locally and it does what it says. Building both branches in release on an M-series Mac: Behavioral equivalence. I ran both binaries over MFC's Performance.
~16x, consistent with what you measured. Other checks. 47 tests pass; The macro swaps read as faithful to me: Three non-blocking nits:
None of those block. LGTM. |
|
rebase against master please |
There was a problem hiding this comment.
🟡 Changes recommended
A regex_replace! result has the wrong return type, preventing compilation.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 5/5 changed files
- Comments generated: 1
- Review effort level: Balanced
The uses of `Regex` in `formatter.rs` were not being constructed lazily or with `OnceLock`, meaning that the cost of constructing them was being paid every single time the containing function was called. Constructing them lazily gives a significant performance enhancement (~15x in some cases). Using the [`lazy-regex`](https://docs.rs/lazy-regex/latest/lazy_regex/) crate simplifies the construction of lazy+static regexes, and has some other benefits (compile time checking, for instance).
The only `use` in this module sat below the Token/OpKind enums, and #7 added the lazy-regex import alongside it to match. Every other module puts its imports at line 1; this brings whitespace.rs in line. Pure move: no code changes, output byte-identical across the MFC corpus.
The uses of
Regexinformatter.rswere not being constructed lazily or withOnceLock, meaning that the cost of constructing them was being paid every single time the containing function was called.Constructing them lazily gives a significant performance enhancement (~15x in some cases).
Using the
lazy-regexcrate simplifies the construction of lazy+static regexes, and has some other benefits (compile time checking, for instance).Improvement is most noticeable on large files (>1k lines), where running
ffmt --checkcan go from ~800ms to ~50ms.