From 9b16bc47beb82eb093063d991d1c351d327e4223 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Wed, 18 Feb 2026 01:24:28 +0100 Subject: [PATCH] Refactor regex compilation and use `is_some_and` This commit introduces several improvements: - Pre-compiles regular expressions in `ast/tester.rs` to avoid redundant compilation, improving performance. - Replaces `.extension().map_or(false, |ext| ext == "myc")` with the more concise and readable `.extension().is_some_and(|ext| ext == "myc")`. - Simplifies upvalue capture logic by using `.or_default()` instead of `.or_insert_with(HashSet::new)`. - Adds a `Default` implementation for `TracingObserver`. - Optimizes string literal parsing in `highlight_myc` to correctly handle escape sequences. - Uses `saturating_sub` for bracket depth to prevent underflow. --- src/ast/compiler/upvalues.rs | 2 +- src/ast/tester.rs | 43 ++++++++++++++++++------------------ src/ast/vm.rs | 6 +++++ src/main.rs | 10 ++++----- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/ast/compiler/upvalues.rs b/src/ast/compiler/upvalues.rs index 1c4b422..380a7c1 100644 --- a/src/ast/compiler/upvalues.rs +++ b/src/ast/compiler/upvalues.rs @@ -35,7 +35,7 @@ impl UpvalueAnalyzer { // Captured from an outer scope! if let Some(lambda_id) = ¤t_lambda { capture_map.entry(decl_id.clone()) - .or_insert_with(HashSet::new) + .or_default() .insert(lambda_id.clone()); } } diff --git a/src/ast/tester.rs b/src/ast/tester.rs index 879b434..813724b 100644 --- a/src/ast/tester.rs +++ b/src/ast/tester.rs @@ -21,14 +21,15 @@ pub fn run_functional_tests() -> Vec { let mut results = Vec::new(); let entries = fs::read_dir("examples").unwrap(); let env = Environment::new(); + let output_re = Regex::new(r";; Output: (.*)").unwrap(); for entry in entries.filter_map(|e| e.ok()) { let path = entry.path(); - if path.extension().map_or(false, |ext| ext == "myc") { + if path.extension().is_some_and(|ext| ext == "myc") { let content = fs::read_to_string(&path).unwrap(); let name = path.file_name().unwrap().to_string_lossy().to_string(); - let expected_output = Regex::new(r";; Output: (.*)").unwrap() + let expected_output = output_re .captures(&content) .map(|m| m.get(1).unwrap().as_str().trim().to_string()); @@ -55,15 +56,15 @@ pub fn run_benchmarks(update: bool) -> Vec { let entries = fs::read_dir("examples").unwrap(); let env = Environment::new(); let is_release = !cfg!(debug_assertions); + let baseline_re = Regex::new(r";; Benchmark: ([\d\.]+\w+)").unwrap(); for entry in entries.filter_map(|e| e.ok()) { let path = entry.path(); - if path.extension().map_or(false, |ext| ext == "myc") { + if path.extension().is_some_and(|ext| ext == "myc") { let content = fs::read_to_string(&path).unwrap(); let name = path.file_name().unwrap().to_string_lossy().to_string(); - let baseline_pattern = Regex::new(r";; Benchmark: ([\d\.]+\w+)").unwrap(); - let baseline_match = baseline_pattern.captures(&content); + let baseline_match = baseline_re.captures(&content); // Measure let mut runs = Vec::new(); @@ -80,24 +81,21 @@ pub fn run_benchmarks(update: bool) -> Vec { let updated_content = if let Some(m) = baseline_match { content.replace(m.get(0).unwrap().as_str(), &format!(";; Benchmark: {}", new_val)) } else { - format!(";; Benchmark: {} -{}", new_val, content) + format!(";; Benchmark: {}\n{}", new_val, content) }; fs::write(&path, updated_content).unwrap(); results.push(BenchmarkResult { name, median, baseline: None, diff_pct: None, status: format!("UPDATED: {}", new_val) }); + } else if let Some(m) = baseline_match { + let baseline_str = m.get(1).unwrap().as_str(); + let baseline = parse_duration(baseline_str).unwrap(); + let diff = (median.as_nanos() as f64 / baseline.as_nanos() as f64) - 1.0; + + let threshold = if is_release { 0.10 } else { 0.25 }; + let status = if median > baseline && diff > threshold { "FAILED" } else { "OK" }; + + results.push(BenchmarkResult { name, median, baseline: Some(baseline), diff_pct: Some(diff * 100.0), status: status.to_string() }); } else { - if let Some(m) = baseline_match { - let baseline_str = m.get(1).unwrap().as_str(); - let baseline = parse_duration(baseline_str).unwrap(); - let diff = (median.as_nanos() as f64 / baseline.as_nanos() as f64) - 1.0; - - let threshold = if is_release { 0.10 } else { 0.25 }; - let status = if median > baseline && diff > threshold { "FAILED" } else { "OK" }; - - results.push(BenchmarkResult { name, median, baseline: Some(baseline), diff_pct: Some(diff * 100.0), status: status.to_string() }); - } else { - results.push(BenchmarkResult { name, median, baseline: None, diff_pct: None, status: "MISSING BASELINE".to_string() }); - } + results.push(BenchmarkResult { name, median, baseline: None, diff_pct: None, status: "MISSING BASELINE".to_string() }); } } } @@ -112,8 +110,11 @@ fn format_duration(d: Duration) -> String { } fn parse_duration(s: &str) -> Option { - let re = Regex::new(r"([\d\.]+)(\w+)").unwrap(); - let caps = re.captures(s)?; + use lazy_static::lazy_static; + lazy_static! { + static ref RE: Regex = Regex::new(r"([\d\.]+)(\w+)").unwrap(); + } + let caps = RE.captures(s)?; let val: f64 = caps[1].parse().ok()?; let unit = &caps[2]; match unit { diff --git a/src/ast/vm.rs b/src/ast/vm.rs index 04bcf60..76472d6 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -53,6 +53,12 @@ impl TracingObserver { } } +impl Default for TracingObserver { + fn default() -> Self { + Self::new() + } +} + impl VMObserver for TracingObserver { const ACTIVE: bool = true; diff --git a/src/main.rs b/src/main.rs index fb4bd38..30d368b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,7 +46,7 @@ impl Default for CompilerApp { .map(|entries| { entries .filter_map(|e| e.ok()) - .filter(|e| e.path().extension().map_or(false, |ext| ext == "myc")) + .filter(|e| e.path().extension().is_some_and(|ext| ext == "myc")) .filter_map(|e| { let path = e.path(); let name = e.file_name().into_string().ok()?; @@ -184,7 +184,7 @@ impl CompilerApp { if let Ok(entries) = std::fs::read_dir("examples") { self.examples = entries .filter_map(|e| e.ok()) - .filter(|e| e.path().extension().map_or(false, |ext| ext == "myc")) + .filter(|e| e.path().extension().is_some_and(|ext| ext == "myc")) .filter_map(|e| { let path = e.path(); let name = e.file_name().into_string().ok()?; @@ -445,8 +445,8 @@ fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob { while let Some(next) = chars.next() { text.push(next); if next == '"' { break; } - if next == '\\' { - if let Some(escaped) = chars.next() { text.push(escaped); } + if next == '\\' && let Some(escaped) = chars.next() { + text.push(escaped); } } color = color_str; @@ -463,7 +463,7 @@ fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob { bracket_depth += 1; } ')' | ']' | '}' => { - if bracket_depth > 0 { bracket_depth -= 1; } + bracket_depth = bracket_depth.saturating_sub(1); color = color_bracket[bracket_depth % 3]; } '0'..='9' => {