use std::fs;
use std::path::{Path, PathBuf};
use leekscript_rs::{
parse, parse_error_to_miette, parse_expression, parse_recovering, print_syntax_tree,
TreeDisplayOptions,
};
fn main() {
let _ = miette::set_hook(Box::new(
|_| Box::new(miette::GraphicalReportHandler::new()),
));
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let leekscript_dir = manifest_dir.join("examples").join("leekscript");
let valid_dir = leekscript_dir.join("valid");
let invalid_dir = leekscript_dir.join("invalid");
println!("═══ LeekScript parser examples ═══\n");
println!(
"Valid: {} | Invalid: {}\n",
valid_dir.display(),
invalid_dir.display()
);
let program_files = discover_leek_files(&valid_dir);
let tree_display_file = program_files.first().cloned();
for path in &program_files {
let name = path.file_stem().and_then(|s| s.to_str()).unwrap_or("?");
let show_tree = tree_display_file.as_ref() == Some(path);
match fs::read_to_string(path) {
Ok(source) => print_snippet(name, &source, path, show_tree),
Err(e) => println!("─── {} ───\n (read error: {})\n", name, e),
}
}
let expr_dir = valid_dir.join("expressions");
println!("═══ Expression parser (single expression per file) ═══\n");
if expr_dir.is_dir() {
let expr_files = discover_leek_files(&expr_dir);
for path in expr_files {
match fs::read_to_string(&path) {
Ok(source) => {
let source = source.trim();
let name = path.file_name().unwrap_or_default().to_string_lossy();
match parse_expression(source) {
Ok(Some(_)) => println!(" {} → OK", name),
Ok(None) => println!(" {} → (empty)", name),
Err(e) => {
if let Some(report) = parse_error_to_miette(&e, source, name.as_ref()) {
eprintln!(" {} → Error:\n{:?}", name, report);
} else {
println!(" {} → Error: {}", name, e);
}
}
}
}
Err(e) => println!(" {} → read error: {}", path.display(), e),
}
}
} else {
println!(" (no expressions/ directory)");
}
println!("\n═══ Invalid input (recovering parse) ═══\n");
let invalid_files = discover_leek_files(&invalid_dir);
for invalid_path in &invalid_files {
if let Ok(source) = fs::read_to_string(invalid_path) {
let short_name = invalid_path
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("invalid.leek");
println!("File: {}", short_name);
println!("Source: {:?}", source);
match parse(&source) {
Ok(Some(_)) => println!(" parse() succeeded (unexpected)"),
Ok(None) => println!(" parse() returned None"),
Err(e) => {
if let Some(report) = parse_error_to_miette(&e, &source, short_name) {
eprintln!("{:?}\n", report);
} else {
println!(" Error: {}", e);
}
}
}
if let Err((partial, _e)) = parse_recovering(&source) {
println!(
" parse_recovering: consumed {} bytes (see diagnostic above)",
partial.consumed
);
if let Some(root) = partial.syntax_root(source.as_bytes()) {
println!(" Partial tree (structure only):");
print_syntax_tree(&root, &TreeDisplayOptions::structure_only());
}
}
println!();
}
}
if invalid_files.is_empty() {
println!(" (no .leek files in invalid/)");
}
}
fn discover_leek_files(dir: &Path) -> Vec<PathBuf> {
let Ok(rd) = fs::read_dir(dir) else {
return vec![];
};
let mut paths: Vec<PathBuf> = rd
.filter_map(|e| e.ok().map(|e| e.path()))
.filter(|p| p.is_file() && p.extension().map_or(false, |ext| ext == "leek"))
.collect();
paths.sort_by_cached_key(|p| p.file_name().unwrap_or_default().to_owned());
paths
}
fn print_snippet(name: &str, source: &str, path: &Path, show_tree: bool) {
let filename = path
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("<input>");
println!("─── {} ({}) ───", name, filename);
println!(" {}", source.trim().replace('\n', " "));
let diagnostic_name = path
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("<file>");
match parse(source) {
Ok(Some(root)) => {
println!(" ✓ Parsed OK");
if show_tree {
print_syntax_tree(&root, &TreeDisplayOptions::structure_only());
}
}
Ok(None) => println!(" (empty parse)"),
Err(e) => {
if let Some(report) = parse_error_to_miette(&e, source, diagnostic_name) {
eprintln!("{:?}\n", report);
} else {
println!(" ✗ Error: {}", e);
}
}
}
println!();
}