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.
This commit is contained in:
Michael Schimmel
2026-02-18 01:24:28 +01:00
parent 1d1b54ed16
commit 9b16bc47be
4 changed files with 34 additions and 27 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ impl UpvalueAnalyzer {
// Captured from an outer scope! // Captured from an outer scope!
if let Some(lambda_id) = &current_lambda { if let Some(lambda_id) = &current_lambda {
capture_map.entry(decl_id.clone()) capture_map.entry(decl_id.clone())
.or_insert_with(HashSet::new) .or_default()
.insert(lambda_id.clone()); .insert(lambda_id.clone());
} }
} }
+22 -21
View File
@@ -21,14 +21,15 @@ pub fn run_functional_tests() -> Vec<TestResult> {
let mut results = Vec::new(); let mut results = Vec::new();
let entries = fs::read_dir("examples").unwrap(); let entries = fs::read_dir("examples").unwrap();
let env = Environment::new(); let env = Environment::new();
let output_re = Regex::new(r";; Output: (.*)").unwrap();
for entry in entries.filter_map(|e| e.ok()) { for entry in entries.filter_map(|e| e.ok()) {
let path = entry.path(); 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 content = fs::read_to_string(&path).unwrap();
let name = path.file_name().unwrap().to_string_lossy().to_string(); 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) .captures(&content)
.map(|m| m.get(1).unwrap().as_str().trim().to_string()); .map(|m| m.get(1).unwrap().as_str().trim().to_string());
@@ -55,15 +56,15 @@ pub fn run_benchmarks(update: bool) -> Vec<BenchmarkResult> {
let entries = fs::read_dir("examples").unwrap(); let entries = fs::read_dir("examples").unwrap();
let env = Environment::new(); let env = Environment::new();
let is_release = !cfg!(debug_assertions); 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()) { for entry in entries.filter_map(|e| e.ok()) {
let path = entry.path(); 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 content = fs::read_to_string(&path).unwrap();
let name = path.file_name().unwrap().to_string_lossy().to_string(); 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_re.captures(&content);
let baseline_match = baseline_pattern.captures(&content);
// Measure // Measure
let mut runs = Vec::new(); let mut runs = Vec::new();
@@ -80,24 +81,21 @@ pub fn run_benchmarks(update: bool) -> Vec<BenchmarkResult> {
let updated_content = if let Some(m) = baseline_match { let updated_content = if let Some(m) = baseline_match {
content.replace(m.get(0).unwrap().as_str(), &format!(";; Benchmark: {}", new_val)) content.replace(m.get(0).unwrap().as_str(), &format!(";; Benchmark: {}", new_val))
} else { } else {
format!(";; Benchmark: {} format!(";; Benchmark: {}\n{}", new_val, content)
{}", new_val, content)
}; };
fs::write(&path, updated_content).unwrap(); fs::write(&path, updated_content).unwrap();
results.push(BenchmarkResult { name, median, baseline: None, diff_pct: None, status: format!("UPDATED: {}", new_val) }); 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 { } else {
if let Some(m) = baseline_match { results.push(BenchmarkResult { name, median, baseline: None, diff_pct: None, status: "MISSING BASELINE".to_string() });
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() });
}
} }
} }
} }
@@ -112,8 +110,11 @@ fn format_duration(d: Duration) -> String {
} }
fn parse_duration(s: &str) -> Option<Duration> { fn parse_duration(s: &str) -> Option<Duration> {
let re = Regex::new(r"([\d\.]+)(\w+)").unwrap(); use lazy_static::lazy_static;
let caps = re.captures(s)?; 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 val: f64 = caps[1].parse().ok()?;
let unit = &caps[2]; let unit = &caps[2];
match unit { match unit {
+6
View File
@@ -53,6 +53,12 @@ impl TracingObserver {
} }
} }
impl Default for TracingObserver {
fn default() -> Self {
Self::new()
}
}
impl VMObserver for TracingObserver { impl VMObserver for TracingObserver {
const ACTIVE: bool = true; const ACTIVE: bool = true;
+5 -5
View File
@@ -46,7 +46,7 @@ impl Default for CompilerApp {
.map(|entries| { .map(|entries| {
entries entries
.filter_map(|e| e.ok()) .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| { .filter_map(|e| {
let path = e.path(); let path = e.path();
let name = e.file_name().into_string().ok()?; let name = e.file_name().into_string().ok()?;
@@ -184,7 +184,7 @@ impl CompilerApp {
if let Ok(entries) = std::fs::read_dir("examples") { if let Ok(entries) = std::fs::read_dir("examples") {
self.examples = entries self.examples = entries
.filter_map(|e| e.ok()) .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| { .filter_map(|e| {
let path = e.path(); let path = e.path();
let name = e.file_name().into_string().ok()?; 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() { while let Some(next) = chars.next() {
text.push(next); text.push(next);
if next == '"' { break; } if next == '"' { break; }
if next == '\\' { if next == '\\' && let Some(escaped) = chars.next() {
if let Some(escaped) = chars.next() { text.push(escaped); } text.push(escaped);
} }
} }
color = color_str; color = color_str;
@@ -463,7 +463,7 @@ fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob {
bracket_depth += 1; bracket_depth += 1;
} }
')' | ']' | '}' => { ')' | ']' | '}' => {
if bracket_depth > 0 { bracket_depth -= 1; } bracket_depth = bracket_depth.saturating_sub(1);
color = color_bracket[bracket_depth % 3]; color = color_bracket[bracket_depth % 3];
} }
'0'..='9' => { '0'..='9' => {