143 lines
3.7 KiB
Rust
143 lines
3.7 KiB
Rust
#![allow(
|
|
clippy::all,
|
|
clippy::pedantic,
|
|
clippy::nursery,
|
|
clippy::cargo,
|
|
clippy::restriction
|
|
)]
|
|
|
|
use rottlib::arena::Arena;
|
|
use rottlib::diagnostics::Diagnostic;
|
|
use rottlib::lexer::TokenizedFile;
|
|
use rottlib::parser::Parser;
|
|
|
|
/// Lexer-focused fixtures.
|
|
///
|
|
/// Keep these small: the goal is to inspect lexer diagnostics and delimiter
|
|
/// recovery behavior, not full parser behavior.
|
|
const TEST_CASES: &[(&str, &str)] = &[
|
|
// L0001: invalid or unknown token
|
|
(
|
|
"files/L0001_01.uc",
|
|
"`",
|
|
),
|
|
|
|
// L0002: unexpected closing delimiter
|
|
(
|
|
"files/L0002_01.uc",
|
|
"]",
|
|
),
|
|
|
|
// L0003: unclosed delimiter before later closing delimiter
|
|
//
|
|
// The `}` can still recover by matching the earlier `{`.
|
|
(
|
|
"files/L0003_01.uc",
|
|
"{\n foo(\n}\n",
|
|
),
|
|
|
|
// L0004: mismatched closing delimiter
|
|
(
|
|
"files/L0004_01.uc",
|
|
"(]",
|
|
),
|
|
|
|
// L0005: unclosed delimiter at end of file
|
|
(
|
|
"files/L0005_01.uc",
|
|
"foo(",
|
|
),
|
|
|
|
// Mixed recovery case:
|
|
//
|
|
// `)` recovers by matching `(` after treating `[` as unclosed;
|
|
// the following `]` is then unexpected.
|
|
(
|
|
"files/L_mixed_01.uc",
|
|
"([)]",
|
|
),
|
|
];
|
|
|
|
/// If true, also run the parser after tokenization.
|
|
///
|
|
/// For lexer-focused fixtures this is usually noisy, so keep it off unless you
|
|
/// want to inspect how parser recovery behaves after lexer diagnostics.
|
|
const RUN_PARSER: bool = false;
|
|
|
|
/// If true, print the parsed expression using Debug formatting.
|
|
const PRINT_PARSED_EXPR: bool = false;
|
|
|
|
/// If true, print diagnostics even when parsing returned a value.
|
|
const ALWAYS_PRINT_DIAGNOSTICS: bool = true;
|
|
|
|
fn main() {
|
|
let arena = Arena::new();
|
|
|
|
let mut had_any_problem = false;
|
|
|
|
for (idx, (label, source)) in TEST_CASES.iter().enumerate() {
|
|
println!("============================================================");
|
|
println!("Case {}: {}", idx + 1, label);
|
|
println!("------------------------------------------------------------");
|
|
|
|
let tf = TokenizedFile::tokenize(source);
|
|
|
|
let lexer_diagnostics = tf.diagnostics();
|
|
|
|
if lexer_diagnostics.is_empty() {
|
|
println!("Lexer diagnostics: none");
|
|
} else {
|
|
had_any_problem = true;
|
|
|
|
if ALWAYS_PRINT_DIAGNOSTICS {
|
|
println!("Lexer diagnostics:");
|
|
for diag in lexer_diagnostics {
|
|
render_diagnostic(diag, &tf, Some(label), false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if RUN_PARSER {
|
|
let mut parser = Parser::new(&tf, &arena);
|
|
let expr = parser.parse_expression();
|
|
|
|
if PRINT_PARSED_EXPR {
|
|
println!("Parsed expression:");
|
|
println!("{expr:#?}");
|
|
}
|
|
|
|
if parser.diagnostics.is_empty() {
|
|
println!("Parser diagnostics: none");
|
|
} else {
|
|
had_any_problem = true;
|
|
|
|
if ALWAYS_PRINT_DIAGNOSTICS {
|
|
println!("Parser diagnostics:");
|
|
for diag in &parser.diagnostics {
|
|
render_diagnostic(diag, &tf, Some(label), false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
println!();
|
|
}
|
|
|
|
println!("============================================================");
|
|
|
|
if had_any_problem {
|
|
println!("Done. At least one case had lexer or parser diagnostics.");
|
|
std::process::exit(1);
|
|
} else {
|
|
println!("Done. All cases completed without diagnostics.");
|
|
}
|
|
}
|
|
|
|
fn render_diagnostic(
|
|
diag: &Diagnostic,
|
|
file: &TokenizedFile<'_>,
|
|
file_name: Option<&str>,
|
|
_colors: bool,
|
|
) {
|
|
diag.render(file, file_name.unwrap_or("<default>"));
|
|
} |