show more compiler errors as diagnostic message that cites the source region
This commit is contained in:
parent
8d19767c98
commit
f5f22ad65d
5 changed files with 66 additions and 36 deletions
22
src/expr.rs
22
src/expr.rs
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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: <Expr) -> Self {
|
pub fn compile(mut self, expr: <Expr) -> 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,20 +220,22 @@ 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 } => {
|
||||||
self.asm = self.asm.lit(*val);
|
self.asm = self.asm.lit(*val);
|
||||||
}
|
}
|
||||||
LTExpr::Application { typ, head, body } => {
|
LTExpr::Application { typ, head, body } => {
|
||||||
for arg in body.iter().rev() {
|
for arg in body.iter().rev() {
|
||||||
self = self.compile(arg);
|
self = self.compile(arg);
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
|
Loading…
Reference in a new issue