use std::num::IntErrorKind;
use crate::{Function, Optimizer as _, Passes, StandardOptimizer, compile};
pub type TestCase = (&'static str, &'static str);
pub type CompilationTestCase = TestCase;
pub fn read_compilation_test_cases(
source: &'static str
) -> Vec<CompilationTestCase>
{
let mut test_cases = Vec::new();
let blocks = source.split("\n\n");
for block in blocks
{
let parts = block.split("\n=\n").collect::<Vec<_>>();
for part in parts[..].chunks(2)
{
let source = part[0].trim();
let expected = part[1].trim();
test_cases.push((source, expected));
}
}
test_cases
}
pub fn compile_valid(source: &str) -> Function
{
match compile(source)
{
Ok(function) => function,
Err(e) => panic!("compilation error: {e}")
}
}
pub fn optimize(function: Function, passes: Passes) -> Function
{
StandardOptimizer::new(passes).optimize(function).unwrap()
}
pub type EvaluationTestCase = (
&'static str,
Vec<i32>,
Vec<(&'static str, i32)>,
&'static str
);
pub fn read_evaluation_test_cases(
source: &'static str
) -> Vec<EvaluationTestCase>
{
let mut test_cases = Vec::new();
let blocks = source.split("\n\n");
for block in blocks
{
let parts: Vec<&str> = block.splitn(2, "\n=\n").collect();
let source = parts[0].trim();
let cases: Vec<&str> = parts[1].trim().split("\n=\n").collect();
for case in cases
{
let mut lines: Vec<&str> = case.lines().collect();
let expected = lines.pop().expect("missing expected result");
let expected = expected.trim();
let mut args = Vec::new();
let mut externs = Vec::new();
for line in lines
{
let line = line.trim();
if let Some(line) = line.strip_prefix("args:")
{
args = line
.split(',')
.map(|s| match s.trim().parse::<i32>()
{
Ok(i) => i,
Err(e)
if e.kind() == &IntErrorKind::PosOverflow =>
{
i32::MAX
},
Err(e)
if e.kind() == &IntErrorKind::NegOverflow =>
{
i32::MIN
},
_ => unreachable!()
})
.collect();
}
else if let Some(line) = line.strip_prefix("externs:")
{
externs = line
.split(',')
.map(|s| {
let parts: Vec<&str> =
s.trim().splitn(2, '=').collect();
(parts[0].trim(), parts[1].trim().parse().unwrap())
})
.collect();
}
}
test_cases.push((source, args, externs, expected));
}
}
test_cases
}
pub type HistogramTestCase = (
&'static str,
Vec<i32>,
Vec<(&'static str, i32)>,
Vec<(i32, usize)>
);
pub fn read_histogram_test_cases(source: &'static str)
-> Vec<HistogramTestCase>
{
let mut test_cases = Vec::new();
let blocks = source.split("\n\n");
for block in blocks
{
let parts: Vec<&str> = block.splitn(2, "\n=\n").collect();
let source = parts[0].trim();
let cases: Vec<&str> = parts[1].trim().split("\n=\n").collect();
for case in cases
{
let mut expected = Vec::new();
let mut args = Vec::new();
let mut externs = Vec::new();
for line in case.lines()
{
let line = line.trim();
if let Some(line) = line.strip_prefix("args:")
{
args = line
.split(',')
.map(|s| match s.trim().parse::<i32>()
{
Ok(i) => i,
Err(e)
if e.kind() == &IntErrorKind::PosOverflow =>
{
i32::MAX
},
Err(e)
if e.kind() == &IntErrorKind::NegOverflow =>
{
i32::MIN
},
_ => unreachable!()
})
.collect();
}
else if let Some(line) = line.strip_prefix("externs:")
{
externs = line
.split(',')
.map(|s| {
let parts: Vec<&str> =
s.trim().splitn(2, '=').collect();
(parts[0].trim(), parts[1].trim().parse().unwrap())
})
.collect();
}
else
{
let parts: Vec<&str> = line.splitn(2, ':').collect();
let outcome = parts[0].trim().parse::<i32>().unwrap();
let count = parts[1].trim().parse::<usize>().unwrap();
expected.push((outcome, count));
}
}
test_cases.push((source, args, externs, expected));
}
}
test_cases
}
#[cfg(test)]
#[derive(Debug)]
pub struct ExpectedPlaceholder
{
pub span: (usize, usize),
pub description: &'static str,
pub valid_kinds: Vec<&'static str>
}
#[cfg(test)]
#[derive(Debug)]
pub struct ExpectedSuggestion
{
pub corrected_source: &'static str,
pub placeholders: Vec<ExpectedPlaceholder>
}
#[cfg(test)]
#[derive(Debug)]
pub struct ExpectedDiagnostic
{
pub kind: &'static str,
pub span: (usize, usize),
pub message: &'static str,
pub suggestions: Vec<ExpectedSuggestion>
}
#[cfg(test)]
#[derive(Debug)]
pub struct ErrorTestCase
{
pub source: &'static str,
pub expected_diagnostics: Vec<ExpectedDiagnostic>
}
#[cfg(test)]
pub fn read_error_test_cases(source: &'static str) -> Vec<ErrorTestCase>
{
let mut test_cases = Vec::new();
for block in source.split("\n\n")
{
let block = block.trim();
if block.is_empty()
{
continue;
}
let parts: Vec<&str> = block.splitn(2, "\n=\n").collect();
assert!(parts.len() == 2, "malformed test case block: {:?}", block);
let test_source = match parts[0]
{
"<empty>" => "",
other => other
};
let diagnostics_text = parts[1];
let mut expected_diagnostics = Vec::new();
for diag_block in diagnostics_text.split("\n---\n")
{
expected_diagnostics
.push(parse_expected_diagnostic(diag_block.trim()));
}
test_cases.push(ErrorTestCase {
source: test_source,
expected_diagnostics
});
}
test_cases
}
#[cfg(test)]
fn parse_expected_diagnostic(text: &'static str) -> ExpectedDiagnostic
{
let mut kind = None;
let mut span = None;
let mut message = None;
let mut suggestions: Vec<ExpectedSuggestion> = Vec::new();
let mut current_suggestion: Option<&'static str> = None;
let mut current_placeholders: Vec<ExpectedPlaceholder> = Vec::new();
for line in text.lines()
{
let line = line.trim();
if let Some(rest) = line.strip_prefix("kind:")
{
kind = Some(rest.trim());
}
else if let Some(rest) = line.strip_prefix("span:")
{
let parts: Vec<&str> = rest.trim().split("..").collect();
span = Some((
parts[0].parse::<usize>().unwrap(),
parts[1].parse::<usize>().unwrap()
));
}
else if let Some(rest) = line.strip_prefix("message:")
{
message = Some(rest.trim());
}
else if let Some(rest) = line.strip_prefix("suggestion:")
{
if let Some(src) = current_suggestion
{
suggestions.push(ExpectedSuggestion {
corrected_source: src,
placeholders: std::mem::take(&mut current_placeholders)
});
}
current_suggestion = Some(rest.trim());
}
else if let Some(rest) = line.strip_prefix("placeholder:")
{
current_placeholders.push(parse_expected_placeholder(rest.trim()));
}
}
if let Some(src) = current_suggestion
{
suggestions.push(ExpectedSuggestion {
corrected_source: src,
placeholders: std::mem::take(&mut current_placeholders)
});
}
ExpectedDiagnostic {
kind: kind.expect("missing kind"),
span: span.expect("missing span"),
message: message.expect("missing message"),
suggestions
}
}
#[cfg(test)]
fn parse_expected_placeholder(text: &'static str) -> ExpectedPlaceholder
{
let span_end = text.find(' ').unwrap();
let span_parts: Vec<&str> = text[..span_end].split("..").collect();
let span = (
span_parts[0].parse::<usize>().unwrap(),
span_parts[1].parse::<usize>().unwrap()
);
let rest = text[span_end..].trim();
let desc_start = rest.find('"').unwrap() + 1;
let desc_end = rest[desc_start..].find('"').unwrap() + desc_start;
let description = &rest[desc_start..desc_end];
let kinds_start = rest.find('[').unwrap() + 1;
let kinds_end = rest.find(']').unwrap();
let valid_kinds: Vec<&str> = rest[kinds_start..kinds_end]
.split(',')
.map(|s| s.trim())
.collect();
ExpectedPlaceholder {
span,
description,
valid_kinds
}
}