show more compiler errors as diagnostic message that cites the source region

This commit is contained in:
Michael Sippel 2024-05-18 18:54:32 +02:00
parent 8d19767c98
commit f5f22ad65d
Signed by: senvas
GPG key ID: F96CF119C34B64A6
5 changed files with 66 additions and 36 deletions

View file

@ -1,11 +1,17 @@
use std::{
use {
std::{
boxed::Box,
sync::{Arc, RwLock},
sync::{Arc, RwLock}
},
crate::{
lexer::InputRegionTag
}
};
#[derive(Clone, Debug)]
pub enum Statement {
Assignment {
name_region: InputRegionTag,
var_id: String,
val_expr: LTExpr,
},
@ -40,6 +46,7 @@ pub enum LTExpr {
val: tisc::VM_Word,
},
Symbol {
region: InputRegionTag,
typ: Option<TypeTag>,
symbol: String,
},
@ -49,7 +56,7 @@ pub enum LTExpr {
body: Vec<LTExpr>,
},
Abstraction {
args: Vec<(String, Option<TypeTag>)>,
args: Vec<(InputRegionTag, String, Option<TypeTag>)>,
body: Box<LTExpr>,
},
Branch {
@ -66,20 +73,21 @@ pub enum LTExpr {
}
impl LTExpr {
/*
pub fn symbol(str: &str) -> Self {
LTExpr::Symbol {
typ: None, //typectx.write().unwrap().parse("<Ref memory::Word>~Symbol~<Seq Char>").expect("parse typeterm"),
symbol: String::from(str),
}
}
*/
pub fn lit_uint(val: u64) -> Self {
LTExpr::Literal {
typ: None, //typectx.write().unwrap().parse("_2^64~machine::UInt64~machine::Word").expect("parse typeterm"),
val: val as tisc::VM_Word,
}
}
/*
pub fn abstraction(args: Vec<(&str, &str)>, body: LTExpr) -> LTExpr {
LTExpr::Abstraction {
args: args
@ -91,7 +99,7 @@ impl LTExpr {
body: Box::new(body),
}
}
*/
pub fn application(head: LTExpr, body: Vec<LTExpr>) -> Self {
LTExpr::Application {
typ: None,

View file

@ -79,7 +79,7 @@ where
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct InputRegionTag {
pub begin: usize,
pub end: usize

View file

@ -76,7 +76,7 @@ fn print_diagnostic(
}
/* TODO:
* - export / import , load multiple files
* - import function symbols
* - Compiler error reporting
* - parse float literals
* - return type annotation
@ -121,10 +121,14 @@ fn main() {
match parser::parse_expr( &typectx, &mut program_tokens ) {
Ok( ast ) => {
let (exports, bytecode) = ProcedureCompiler::new(&main_scope)
let (exports, diagnostics, bytecode) = ProcedureCompiler::new(&main_scope)
.compile(&ast)
.into_asm(&path);
for (region, message) in diagnostics {
print_diagnostic(path.as_str(), region, message);
}
eprintln!("{} {}", "Compiled".green(), path.bold());
for (name, def) in exports.iter() {
eprintln!("export {}: {:?}", name.yellow().bold(), def);

View file

@ -39,11 +39,11 @@ where It: Iterator<Item = (InputRegionTag, Result<LTIRToken, LexError>)>
/* parse symbol name
*/
pub fn parse_symbol<It>(tokens: &mut Peekable<It>) -> Result<String, (InputRegionTag, ParseError)>
pub fn parse_symbol<It>(tokens: &mut Peekable<It>) -> Result<(InputRegionTag, String), (InputRegionTag, ParseError)>
where It: Iterator<Item = (InputRegionTag, Result<LTIRToken, LexError>)>
{
match tokens.next() {
Some((region, Ok(LTIRToken::Symbol(name)))) => Ok(name),
Some((region, Ok(LTIRToken::Symbol(name)))) => Ok((region, name)),
Some((region, Ok(_))) => Err((region, ParseError::UnexpectedToken)),
Some((region, Err(err))) => Err((region, ParseError::LexError(err))),
None => Err((InputRegionTag::default(), ParseError::UnexpectedEnd)),
@ -79,6 +79,7 @@ where It: Iterator<Item = (InputRegionTag, Result<LTIRToken, LexError>)>
#[derive(Debug, PartialEq, Eq)]
pub enum VariableBinding {
Atomic {
region: InputRegionTag,
symbol: String,
typtag: Option<laddertypes::TypeTerm>
},
@ -89,10 +90,10 @@ pub enum VariableBinding {
}
impl VariableBinding {
pub fn flatten(self) -> Vec<(String, Option<laddertypes::TypeTerm>)> {
pub fn flatten(self) -> Vec<(InputRegionTag, String, Option<laddertypes::TypeTerm>)> {
match self {
VariableBinding::Atomic{ symbol, typtag } =>
vec![ (symbol, typtag) ],
VariableBinding::Atomic{ region, symbol, typtag } =>
vec![ (region, symbol, typtag) ],
VariableBinding::Struct{ members, typtag } =>
members
.into_iter()
@ -122,13 +123,15 @@ where It: Iterator<Item = (InputRegionTag, Result<LTIRToken, LexError>)>
})
}
Ok(LTIRToken::Symbol(_)) => {
let (name_region, name) = parse_symbol(tokens)?;
Ok(VariableBinding::Atomic{
symbol: parse_symbol(tokens)?,
region: name_region,
symbol: name,
typtag: parse_type_tag(typectx, tokens)?
})
}
Err(err) => Err((*region, ParseError::LexError(err.clone()))),
_ => Err((*region, ParseError::UnexpectedToken))
Err(err) => Err((region.clone(), ParseError::LexError(err.clone()))),
_ => Err((region.clone(), ParseError::UnexpectedToken))
}
} else {
Err((InputRegionTag::default(), ParseError::UnexpectedEnd))
@ -184,18 +187,19 @@ where It: Iterator<Item = (InputRegionTag, Result<LTIRToken, LexError>)>
"!" => {
tokens.next();
// todo accept address-expression instead of symbol
let name = parse_symbol(tokens)?;
let (name_region, name) = parse_symbol(tokens)?;
let val_expr = parse_expr(typectx, tokens)?;
let _ = parse_expect(tokens, LTIRToken::StatementSep)?;
Ok(Statement::Assignment {
name_region,
var_id: name,
val_expr,
})
}
"let" => {
tokens.next();
let name = parse_symbol(tokens)?;
let (name_region, name) = parse_symbol(tokens)?;
let typ = parse_type_tag(typectx, tokens)?;
/* todo
let mut variable_bindings = parse_binding_expr(typectx, tokens)?;
@ -281,7 +285,7 @@ pub fn parse_atom<It>(
where It: Iterator<Item = (InputRegionTag, Result<LTIRToken, LexError>)>
{
match tokens.next() {
Some((region, Ok(LTIRToken::Symbol(sym)))) => Ok(LTExpr::symbol(sym.as_str())),
Some((region, Ok(LTIRToken::Symbol(sym)))) => Ok(LTExpr::Symbol{ region, symbol: sym, typ: None }),
Some((region, Ok(LTIRToken::Char(c)))) => Ok(LTExpr::lit_uint(c as u64)),
Some((region, Ok(LTIRToken::Num(n)))) => Ok(LTExpr::lit_uint(n as u64)),
Some((region, Ok(_))) => Err((region, ParseError::UnexpectedToken)),
@ -309,7 +313,7 @@ where It: Iterator<Item = (InputRegionTag, Result<LTIRToken, LexError>)>
let body = parse_expr(typectx, tokens)?;
return Ok(LTExpr::Abstraction {
args: variable_bindings.flatten().into_iter().map(|(s,t)| (s,t.map(|t|Ok(t))) ).collect(),
args: variable_bindings.flatten().into_iter().map(|(r,s,t)| (r,s,t.map(|t|Ok(t))) ).collect(),
body: Box::new(body),
});
} else {

View file

@ -1,5 +1,6 @@
use {
crate::{
lexer::InputRegionTag,
expr::{LTExpr, Statement},
symbols::{Scope, SymbolDef},
},
@ -15,6 +16,8 @@ pub struct ProcedureCompiler {
asm: tisc::Assembler,
linker: tisc::Linker,
result_size: usize,
pub diagnostics: Vec<( InputRegionTag, String )>
}
impl ProcedureCompiler {
@ -24,10 +27,12 @@ impl ProcedureCompiler {
asm: tisc::Assembler::new(),
linker: tisc::Linker::new(),
result_size: 0,
diagnostics: Vec::new()
}
}
pub fn into_asm(mut self, proc_symbol: &String) -> (Vec<(String, SymbolDef)>, Vec<tisc::assembler::AssemblyWord>) {
pub fn into_asm(mut self, proc_symbol: &String) -> (Vec<(String, SymbolDef)>, Vec<(InputRegionTag, String)>, Vec<tisc::assembler::AssemblyWord>) {
let mut symbols =
Arc::try_unwrap(self.symbols).ok().unwrap()
.into_inner().unwrap();
@ -91,7 +96,7 @@ impl ProcedureCompiler {
}
}
let bytecode = superlink.link_relative(proc_symbol).expect("link error");
(symbol_exports, bytecode)
(symbol_exports, self.diagnostics, bytecode)
}
pub fn verify(&self) {
@ -100,7 +105,7 @@ impl ProcedureCompiler {
pub fn compile_statement(mut self, statement: &Statement, enable_export: bool) -> Self {
match statement {
Statement::Assignment { var_id, val_expr } => {
Statement::Assignment { name_region, var_id, val_expr } => {
self = self.compile(val_expr);
match self.symbols.read().unwrap().get(var_id) {
@ -125,7 +130,10 @@ impl ProcedureCompiler {
.inst(tisc::VM_Instruction::Store);
}
None => {
eprintln!("cannot assign undefined symbol '{}'!", var_id);
self.diagnostics.push(
(name_region.clone(),
format!("cannot assign undefined symbol '{}'!", var_id))
);
}
}
}
@ -140,19 +148,18 @@ impl ProcedureCompiler {
.unwrap()
.declare_proc(var_id.clone(), vec![], vec![], enable_export);
let (exports, lambda_procedure) = ProcedureCompiler::new(&self.symbols)
let (exports, mut diagnostics, lambda_procedure) = ProcedureCompiler::new(&self.symbols)
.compile(val_expr)
.into_asm(var_id);
self.diagnostics.append(&mut diagnostics);
self.linker.add_procedure(var_id, lambda_procedure);
let offset = self.linker.get_link_addr(var_id).unwrap();
// forward already exported symbols
if enable_export {
for (name, def) in exports.iter() {
eprintln!("Procedure compiler: export {}", name);
}
self.symbols.write().unwrap().import( exports );
}
}
@ -161,7 +168,9 @@ impl ProcedureCompiler {
.write()
.unwrap()
.declare_var(var_id.clone(), laddertypes::TypeTerm::unit());
self = self.compile_statement(&Statement::Assignment {
name_region: InputRegionTag::default(),
var_id: var_id.clone(),
val_expr: val_expr.clone(),
}, false);
@ -195,7 +204,7 @@ impl ProcedureCompiler {
pub fn compile(mut self, expr: &LTExpr) -> Self {
match expr {
LTExpr::Symbol { typ, symbol } => match self.symbols.read().unwrap().get(symbol) {
LTExpr::Symbol { region, typ, symbol } => match self.symbols.read().unwrap().get(symbol) {
Some(SymbolDef::FrameRef { typ, stack_ref }) => {
self.asm = self.asm.lit(stack_ref).call("data-frame-get");
}
@ -211,7 +220,9 @@ impl ProcedureCompiler {
self.asm = self.asm.call_symbol(link_addr);
}
None => {
eprintln!("undefined symbol '{}'!", symbol);
self.diagnostics.push(
(region.clone(), format!("undefined symbol '{}'!", symbol))
);
}
},
LTExpr::Literal { typ, val } => {
@ -224,7 +235,7 @@ impl ProcedureCompiler {
self = self.compile(head);
}
LTExpr::Abstraction { args, body } => {
for (arg_name, arg_type) in args.iter() {
for (region, arg_name, arg_type) in args.iter() {
if let Some(Ok(typeterm)) = arg_type {
let id = self
.symbols
@ -233,7 +244,10 @@ impl ProcedureCompiler {
.declare_var(arg_name.clone(), typeterm.clone());
self.asm = self.asm.lit(id).call("data-frame-set");
} else {
eprintln!("invalid type {:?} for argument {}", arg_type, arg_name);
self.diagnostics.push((
region.clone(),
format!("invalid type {:?} for argument {}", arg_type, arg_name)
));
}
}
self = self.compile(body);