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

View file

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

View file

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

View file

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

View file

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