use std::collections::HashMap; use rottlib::arena::Arena; use rottlib::diagnostics::Diagnostic; use rottlib::lexer::{TokenPosition, TokenSpan, TokenizedFile}; use rottlib::parser::Parser; #[derive(Debug, Clone, Copy)] pub struct Fixture { pub code: &'static str, pub label: &'static str, pub source: &'static str, } pub const FIXTURES: &[Fixture] = &[ Fixture { code: "P0001", label: "files/P0001_01.uc", source: "c && ( /*lol*/ ** calc_it())", }, Fixture { code: "P0001", label: "files/P0001_02.uc", source: "\r\na + (\n//AAA\n//BBB\n//CCC\n//DDD\n//EEE\n//FFF\n ]", }, Fixture { code: "P0001", label: "files/P0001_03.uc", source: "(\n// nothing here, bucko", }, Fixture { code: "P0002", label: "files/P0002_01.uc", source: "a + [", }, Fixture { code: "P0002", label: "files/P0002_02.uc", source: "a * \n//some\n//empty lines\n *", }, Fixture { code: "P0002", label: "files/P0002_03.uc", source: "a &&", }, Fixture { code: "P0002", label: "files/P0002_04.uc", source: "a * * *", }, Fixture { code: "P0003", label: "files/P0003_01.uc", source: "(a + b && c / d ^ e @ f", }, Fixture { code: "P0003", label: "files/P0003_02.uc", source: "(a]", }, Fixture { code: "P0003", label: "files/P0003_03.uc", source: "(a\n;", }, ]; pub struct FixtureRun<'src> { pub fixture: &'static Fixture, pub file: TokenizedFile<'src>, pub diagnostics: Vec, } pub struct FixtureRuns<'src> { runs: HashMap<&'static str, FixtureRun<'src>>, } impl<'src> FixtureRuns<'src> { pub fn get(&self, label: &str) -> Option> { self.runs .get(label) .map(|fixture_run| fixture_run.diagnostics.clone()) } pub fn get_any(&self, label: &str) -> Diagnostic { self.runs .get(label) .map(|fixture_run| fixture_run.diagnostics[0].clone()) .unwrap() } pub fn iter(&self) -> impl Iterator)> { self.runs.iter().map(|(label, run)| (*label, run)) } } fn run_fixture(fixture: &'static Fixture) -> FixtureRun<'static> { let arena = Arena::new(); let file = TokenizedFile::tokenize(fixture.source); let mut parser = Parser::new(&file, &arena); let _ = parser.parse_expression(); let diagnostics = parser.diagnostics.clone(); FixtureRun { fixture, file, diagnostics, } } pub fn run_fixtures(code: &str) -> FixtureRuns<'static> { let mut runs = HashMap::new(); for fixture in FIXTURES.iter().filter(|fixture| fixture.code == code) { runs.insert(fixture.label, run_fixture(fixture)); } for (label, run) in runs.iter() { run.diagnostics.iter().for_each(|diag| { diag.render(&run.file, *label); }); println!(); } FixtureRuns { runs } } #[test] fn check_p0001_fixtures() { let runs = run_fixtures("P0001"); assert_eq!(runs.get("files/P0001_01.uc").unwrap().len(), 1); assert_eq!(runs.get("files/P0001_02.uc").unwrap().len(), 1); assert_eq!(runs.get("files/P0001_03.uc").unwrap().len(), 1); assert_eq!( runs.get_any("files/P0001_01.uc").headline(), "expected expression inside parentheses, found `**`" ); assert_eq!( runs.get_any("files/P0001_02.uc").headline(), "expected expression inside parentheses, found `]`" ); assert_eq!( runs.get_any("files/P0001_03.uc").headline(), "expected expression, found end of file" ); assert_eq!(runs.get_any("files/P0001_01.uc").code(), Some("P0001")); assert_eq!(runs.get_any("files/P0001_02.uc").code(), Some("P0001")); assert_eq!(runs.get_any("files/P0001_03.uc").code(), Some("P0001")); assert_eq!( runs.get_any("files/P0001_01.uc") .primary_label() .unwrap() .span, TokenSpan { start: TokenPosition(8), end: TokenPosition(8) } ); assert_eq!( runs.get_any("files/P0001_02.uc") .primary_label() .unwrap() .span, TokenSpan { start: TokenPosition(5), end: TokenPosition(20) } ); assert_eq!( runs.get_any("files/P0001_03.uc") .primary_label() .unwrap() .span, TokenSpan { start: TokenPosition(0), end: TokenPosition(3) } ); assert_eq!( runs.get_any("files/P0001_01.uc") .primary_label() .unwrap() .message, "unexpected `**`" ); assert_eq!( runs.get_any("files/P0001_02.uc") .primary_label() .unwrap() .message, "unexpected `]`" ); assert_eq!( runs.get_any("files/P0001_03.uc") .primary_label() .unwrap() .message, "reached end of file here" ); } #[test] fn check_p0002_fixtures() { let runs = run_fixtures("P0002"); assert_eq!(runs.get("files/P0002_01.uc").unwrap().len(), 1); assert_eq!(runs.get("files/P0002_02.uc").unwrap().len(), 1); assert_eq!(runs.get("files/P0002_03.uc").unwrap().len(), 1); assert_eq!(runs.get("files/P0002_04.uc").unwrap().len(), 1); assert_eq!( runs.get_any("files/P0002_01.uc").headline(), "expected expression after `+`, found `[`" ); assert_eq!( runs.get_any("files/P0002_02.uc").headline(), "expected expression after `*`, found `*`" ); assert_eq!( runs.get_any("files/P0002_03.uc").headline(), "expected expression after `&&`, found end of file" ); assert_eq!( runs.get_any("files/P0002_04.uc").headline(), "expected expression after `*`, found `*`" ); assert_eq!(runs.get_any("files/P0002_01.uc").code(), Some("P0002")); assert_eq!(runs.get_any("files/P0002_02.uc").code(), Some("P0002")); assert_eq!(runs.get_any("files/P0002_03.uc").code(), Some("P0002")); assert_eq!(runs.get_any("files/P0002_04.uc").code(), Some("P0002")); assert_eq!( runs.get_any("files/P0002_01.uc") .primary_label() .unwrap() .span, TokenSpan { start: TokenPosition(4), end: TokenPosition(4), } ); assert_eq!( runs.get_any("files/P0002_02.uc") .primary_label() .unwrap() .span, TokenSpan { start: TokenPosition(10), end: TokenPosition(10), } ); assert_eq!( runs.get_any("files/P0002_03.uc") .primary_label() .unwrap() .span, TokenSpan { start: TokenPosition(3), end: TokenPosition(3), } ); assert_eq!( runs.get_any("files/P0002_04.uc") .primary_label() .unwrap() .span, TokenSpan { start: TokenPosition(4), end: TokenPosition(4), } ); assert_eq!( runs.get_any("files/P0002_01.uc") .primary_label() .unwrap() .message, "unexpected `[`" ); assert_eq!( runs.get_any("files/P0002_02.uc") .primary_label() .unwrap() .message, "unexpected `*`" ); assert_eq!( runs.get_any("files/P0002_03.uc") .primary_label() .unwrap() .message, "reached end of file here" ); assert_eq!( runs.get_any("files/P0002_04.uc") .primary_label() .unwrap() .message, "unexpected `*`" ); } #[test] fn check_p0003_fixtures() { let runs = run_fixtures("P0003"); assert_eq!(runs.get("files/P0003_01.uc").unwrap().len(), 1); assert_eq!(runs.get("files/P0003_02.uc").unwrap().len(), 1); assert_eq!(runs.get("files/P0003_03.uc").unwrap().len(), 1); assert_eq!( runs.get_any("files/P0003_01.uc").headline(), "missing `)` to close parenthesized expression" ); assert_eq!( runs.get_any("files/P0003_02.uc").headline(), "missing `)` to close parenthesized expression" ); assert_eq!( runs.get_any("files/P0003_03.uc").headline(), "missing `)` to close parenthesized expression" ); assert_eq!(runs.get_any("files/P0003_01.uc").code(), Some("P0003")); assert_eq!(runs.get_any("files/P0003_02.uc").code(), Some("P0003")); assert_eq!(runs.get_any("files/P0003_03.uc").code(), Some("P0003")); assert_eq!( runs.get_any("files/P0003_01.uc") .primary_label() .unwrap() .span, TokenSpan { start: TokenPosition(22), end: TokenPosition(22), } ); assert_eq!( runs.get_any("files/P0003_02.uc") .primary_label() .unwrap() .span, TokenSpan { start: TokenPosition(2), end: TokenPosition(2), } ); assert_eq!( runs.get_any("files/P0003_03.uc") .primary_label() .unwrap() .span, TokenSpan { start: TokenPosition(0), end: TokenPosition(3), } ); assert_eq!( runs.get_any("files/P0003_01.uc") .primary_label() .unwrap() .message, "expected `)` before end of file" ); assert_eq!( runs.get_any("files/P0003_02.uc") .primary_label() .unwrap() .message, "expected `)` before `]`" ); assert_eq!( runs.get_any("files/P0003_03.uc") .primary_label() .unwrap() .message, "expected `)` before `;`" ); }