diff --git a/Cargo.toml b/Cargo.toml index 891d662..91dcb5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,11 +9,11 @@ keywords = ["fortran", "formatter", "linter", "code-quality"] categories = ["development-tools", "command-line-utilities"] [dependencies] -regex = "1" clap = { version = "4", features = ["derive"] } rayon = "1" ignore = "0.4" toml = "0.8" serde = { version = "1", features = ["derive"] } serde_json = "1" +lazy-regex = "3.3.0" diff --git a/src/classifier.rs b/src/classifier.rs index 61fa816..299946f 100644 --- a/src/classifier.rs +++ b/src/classifier.rs @@ -1,5 +1,4 @@ -use regex::Regex; -use std::sync::OnceLock; +use lazy_regex::{regex, regex_is_match}; /// Classification of a logical line. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -22,22 +21,11 @@ pub enum LineKind { Blank, } -/// Helper to get or compile a case-insensitive regex, cached in a OnceLock. -macro_rules! re { - ($lock:ident, $pat:expr) => {{ - static $lock: OnceLock = OnceLock::new(); - $lock.get_or_init(|| Regex::new($pat).unwrap()) - }}; -} - /// Classify a Fypp line (starts with `#:` or `#!`). fn classify_fypp(trimmed: &str) -> LineKind { - let re_block_open = re!(FYPP_OPEN, r"(?i)^#:\s*(if|for|def|call|block|mute)\b"); - let re_block_close = re!( - FYPP_CLOSE, - r"(?i)^#:\s*(endif|endfor|enddef|endcall|endblock|endmute)\b" - ); - let re_continuation = re!(FYPP_CONT, r"(?i)^#:\s*(elif|else)\b"); + let re_block_open = regex!(r"(?i)^#:\s*(if|for|def|call|block|mute)\b"); + let re_block_close = regex!(r"(?i)^#:\s*(endif|endfor|enddef|endcall|endblock|endmute)\b"); + let re_continuation = regex!(r"(?i)^#:\s*(elif|else)\b"); if re_block_close.is_match(trimmed) { LineKind::FyppBlockClose @@ -53,10 +41,10 @@ fn classify_fypp(trimmed: &str) -> LineKind { /// Classify a preprocessor line (starts with `#` but not `#:` or `#!`). fn classify_preprocessor(trimmed: &str) -> LineKind { - let re_close = re!(CPP_CLOSE, r"(?i)^#\s*endif\b"); - let re_cont = re!(CPP_CONT, r"(?i)^#\s*else\b"); + let re_close = regex!(r"(?i)^#\s*endif\b"); + let re_cont = regex!(r"(?i)^#\s*else\b"); // Note: #else comes before this, so we won't match #elif as #else - let re_elif = re!(CPP_ELIF, r"(?i)^#\s*elif\b"); + let re_elif = regex!(r"(?i)^#\s*elif\b"); if re_close.is_match(trimmed) { LineKind::PreprocessorClose @@ -145,7 +133,7 @@ fn classify_fortran(trimmed: &str) -> LineKind { // so the underlying statement is classified. The label is kept in the output; // stripping happens for classification only. let (trimmed, had_numeric_label) = { - let re_num_label = re!(NUM_LABEL, r"^\d+\s+"); + let re_num_label = regex!(r"^\d+\s+"); if let Some(m) = re_num_label.find(trimmed) { (trimmed[m.end()..].trim_start(), true) } else { @@ -155,7 +143,7 @@ fn classify_fortran(trimmed: &str) -> LineKind { // Strip optional label prefix like "outer: " let line = { - let re_label = re!(LABEL, r"(?i)^\w+\s*:\s*"); + let re_label = regex!(r"(?i)^\w+\s*:\s*"); // Only strip if it looks like a construct label (not a keyword: like "type:") // A label must start with a letter and be followed by ":" if let Some(m) = re_label.find(trimmed) { @@ -220,8 +208,7 @@ fn classify_fortran(trimmed: &str) -> LineKind { let lower = line.to_ascii_lowercase(); // --- Block closers (check first since "end" prefix is distinctive) --- - let re_end_block = re!( - END_BLOCK, + let re_end_block = regex!( r"(?i)^end\s*(if|do|select|subroutine|function|module|submodule|program|interface|type|block|associate|where|forall|enum|critical|team)\b" ); if let Some(m) = re_end_block.find(line) { @@ -232,7 +219,7 @@ fn classify_fortran(trimmed: &str) -> LineKind { // A numeric-labeled "end do" may terminate a non-block labeled DO // ("do 10 ... 10 end do"); since "do