feat: add diagnostic system and improve CLI with check command

Implement comprehensive diagnostic reporting system:
- Add Diagnostic struct with severity levels and span-based error tracking
- Add diagnostic rendering with source context and caret positioning
- Replace ParseError with diagnostic collection in lexer and parser
- Add LexResult and ParseResult types to carry diagnostics

Enhance driver crate with frontend output:
- Replace CompileResult with FrontendOutput containing diagnostics
- Add has_errors() and render_diagnostics() methods
- Add AstSummary for
This commit is contained in:
2026-04-06 17:07:50 +02:00
parent 0da224325a
commit dfd2f10234
12 changed files with 1315 additions and 502 deletions

View File

@@ -5,6 +5,10 @@ edition.workspace = true
license.workspace = true
authors.workspace = true
[[bin]]
name = "nxc"
path = "src/main.rs"
[[bin]]
name = "nexacore"
path = "src/main.rs"

View File

@@ -20,15 +20,28 @@ fn run() -> Result<(), String> {
};
match command.as_str() {
"build" => {
"check" | "build" => {
let Some(path) = args.next() else {
return Err("usage: nexacore build <file.nx>".to_string());
return Err(format!("usage: {} {command} <file.nx>", executable_name()));
};
let result = nxc_driver::compile_file(Path::new(&path))
.map_err(format_compile_error)?;
println!("compiled {path}");
println!("tokens: {}", result.tokens.len());
println!("items: {}", result.module.items.len());
let output =
nxc_driver::check_file(Path::new(&path)).map_err(format_driver_error)?;
if output.has_errors() {
eprintln!("{}", output.render_diagnostics());
return Err(format!(
"check failed with {} diagnostic(s)",
output.diagnostics.len()
));
}
let summary = output.summary();
println!("checked {}", output.path.display());
println!("tokens: {}", output.tokens.len());
println!("items: {}", summary.items);
println!("functions: {}", summary.functions);
println!("structs: {}", summary.structs);
Ok(())
}
"run" => Err("runtime execution is not implemented yet".to_string()),
@@ -41,25 +54,34 @@ fn run() -> Result<(), String> {
}
}
fn format_compile_error(error: nxc_driver::CompileError) -> String {
fn executable_name() -> String {
env::args()
.next()
.and_then(|path| {
Path::new(&path)
.file_name()
.map(|name| name.to_string_lossy().to_string())
})
.unwrap_or_else(|| "nxc".to_string())
}
fn format_driver_error(error: nxc_driver::DriverError) -> String {
match error {
nxc_driver::CompileError::Io(io) => format!("io error: {io}"),
nxc_driver::CompileError::Parse(parse) => format!(
"parse error at line {}, column {}: {}",
parse.span.line, parse.span.column, parse.message
),
nxc_driver::DriverError::Io(io) => format!("io error: {io}"),
}
}
fn print_help() {
let name = executable_name();
println!("NexaCore CLI");
println!("usage:");
println!(" nexacore build <file.nx>");
println!(" nexacore run <file.nx>");
println!(" nexacore new <name>");
println!(" nexacore test");
println!(" nexacore fmt");
println!(" nexacore add <package>");
println!(" nexacore doc");
println!(" {name} check <file.nx>");
println!(" {name} build <file.nx>");
println!(" {name} run <file.nx>");
println!(" {name} new <name>");
println!(" {name} test");
println!(" {name} fmt");
println!(" {name} add <package>");
println!(" {name} doc");
}