Refactor lambda binding and parameter handling
Introduce `BoundKind::Parameter` to represent function parameters. Modify `Binder` to correctly handle parameters within lambda definitions, ensuring they are only defined within function scopes. Update `LambdaCollector` to register the body of lambdas as templates for global definitions. Adjust `Dumper` to accurately represent lambda parameters. Update `Specializer` and `TCO` to handle the new `BoundKind::Parameter`. Refactor `Call` and `TailCall` to use a single `args` node, often a tuple. Adjust type signatures in RTL to use `StaticType::Tuple` for function parameters.
This commit is contained in:
+88
-32
@@ -1,7 +1,7 @@
|
||||
use crate::ast::environment::Environment;
|
||||
use regex::Regex;
|
||||
use std::fs;
|
||||
use std::time::{Duration, Instant};
|
||||
use regex::Regex;
|
||||
|
||||
pub struct TestResult {
|
||||
pub name: String,
|
||||
@@ -28,7 +28,7 @@ pub fn run_functional_tests() -> Vec<TestResult> {
|
||||
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 = output_re
|
||||
.captures(&content)
|
||||
.map(|m| m.get(1).unwrap().as_str().trim().to_string());
|
||||
@@ -38,12 +38,24 @@ pub fn run_functional_tests() -> Vec<TestResult> {
|
||||
Ok(val) => {
|
||||
let val_str = format!("{}", val);
|
||||
if val_str == expected {
|
||||
results.push(TestResult { name, success: true, message: format!("OK: {}", val_str) });
|
||||
results.push(TestResult {
|
||||
name,
|
||||
success: true,
|
||||
message: format!("OK: {}", val_str),
|
||||
});
|
||||
} else {
|
||||
results.push(TestResult { name, success: false, message: format!("Expected {}, got {}", expected, val_str) });
|
||||
results.push(TestResult {
|
||||
name,
|
||||
success: false,
|
||||
message: format!("Expected {}, got {}", expected, val_str),
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(e) => results.push(TestResult { name, success: false, message: format!("Error: {}", e) }),
|
||||
Err(e) => results.push(TestResult {
|
||||
name,
|
||||
success: false,
|
||||
message: format!("Error: {}", e),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,9 +75,9 @@ pub fn run_benchmarks(update: bool) -> Vec<BenchmarkResult> {
|
||||
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_match = baseline_re.captures(&content);
|
||||
|
||||
|
||||
// Prepare: Compile and Link once outside the measurement loop
|
||||
let linked_node = match env.compile(&content).map(|c| env.link(c)) {
|
||||
Ok(node) => node,
|
||||
@@ -85,13 +97,25 @@ pub fn run_benchmarks(update: bool) -> Vec<BenchmarkResult> {
|
||||
if update {
|
||||
let new_val = format_duration(median);
|
||||
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 {
|
||||
format!(";; Benchmark: {}
|
||||
{}", new_val, content)
|
||||
format!(
|
||||
";; Benchmark: {}
|
||||
{}",
|
||||
new_val, content
|
||||
)
|
||||
};
|
||||
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();
|
||||
@@ -108,13 +132,29 @@ pub fn run_benchmarks(update: bool) -> Vec<BenchmarkResult> {
|
||||
}
|
||||
|
||||
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() });
|
||||
|
||||
let threshold = if is_release { 0.15 } else { 0.5 };
|
||||
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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,17 +162,22 @@ pub fn run_benchmarks(update: bool) -> Vec<BenchmarkResult> {
|
||||
}
|
||||
|
||||
fn format_duration(d: Duration) -> String {
|
||||
if d.as_nanos() < 1000 { format!("{}ns", d.as_nanos()) }
|
||||
else if d.as_micros() < 1000 { format!("{:.1}us", d.as_nanos() as f64 / 1000.0) }
|
||||
else if d.as_millis() < 1000 { format!("{:.1}ms", d.as_micros() as f64 / 1000.0) }
|
||||
else { format!("{:.1}s", d.as_millis() as f64 / 1000.0) }
|
||||
if d.as_nanos() < 1000 {
|
||||
format!("{}ns", d.as_nanos())
|
||||
} else if d.as_micros() < 1000 {
|
||||
format!("{:.1}us", d.as_nanos() as f64 / 1000.0)
|
||||
} else if d.as_millis() < 1000 {
|
||||
format!("{:.1}ms", d.as_micros() as f64 / 1000.0)
|
||||
} else {
|
||||
format!("{:.1}s", d.as_millis() as f64 / 1000.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_duration(s: &str) -> Option<Duration> {
|
||||
use std::sync::OnceLock;
|
||||
static RE: OnceLock<Regex> = OnceLock::new();
|
||||
let re = RE.get_or_init(|| Regex::new(r"([\d\.]+)(\w+)").unwrap());
|
||||
|
||||
|
||||
let caps = re.captures(s)?;
|
||||
let val: f64 = caps[1].parse().ok()?;
|
||||
let unit = &caps[2];
|
||||
@@ -156,29 +201,40 @@ mod tests {
|
||||
|
||||
for _ in 0..5 {
|
||||
let results = run_benchmarks(false);
|
||||
let failures: Vec<_> = results.iter()
|
||||
.filter(|r| r.status == "FAILED")
|
||||
.collect();
|
||||
let failures: Vec<_> = results.iter().filter(|r| r.status == "FAILED").collect();
|
||||
|
||||
if failures.is_empty() {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
last_failures = failures.iter()
|
||||
.map(|r| format!("{}: Median {:?}, Baseline {:?}, Diff {:.2}%",
|
||||
r.name, r.median, r.baseline.unwrap_or_default(), r.diff_pct.unwrap_or(0.0)))
|
||||
last_failures = failures
|
||||
.iter()
|
||||
.map(|r| {
|
||||
format!(
|
||||
"{}: Median {:?}, Baseline {:?}, Diff {:.2}%",
|
||||
r.name,
|
||||
r.median,
|
||||
r.baseline.unwrap_or_default(),
|
||||
r.diff_pct.unwrap_or(0.0)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("
|
||||
");
|
||||
.join(
|
||||
"
|
||||
",
|
||||
);
|
||||
|
||||
// Give the system a tiny bit of breath between retries
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
}
|
||||
|
||||
if !success {
|
||||
panic!("Performance regression detected after 5 attempts:
|
||||
{}", last_failures);
|
||||
panic!(
|
||||
"Performance regression detected after 5 attempts:
|
||||
{}",
|
||||
last_failures
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user