First part of reporting locations in error messages

This commit is contained in:
2023-08-20 01:38:42 -05:00
parent 4f0524f78c
commit 3db79cc103
3 changed files with 129 additions and 85 deletions
+20 -2
View File
@@ -1,5 +1,23 @@
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub struct Location {
pub line: usize,
pub col: usize
}
impl From<(usize, usize)> for Location {
fn from(value: (usize, usize)) -> Self {
Self { line: value.0, col: value.1 }
}
}
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub struct Program(pub Vec<Declaration>); pub struct Located<T> {
pub location: Location,
pub ast: T
}
#[derive(PartialEq, Clone, Debug)]
pub struct Program(pub Vec<Located<Declaration>>);
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub enum Declaration { pub enum Declaration {
@@ -22,7 +40,7 @@ pub struct Const {
} }
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub struct Block(pub Vec<Statement>); pub struct Block(pub Vec<Located<Statement>>);
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub struct Function { pub struct Function {
+39 -35
View File
@@ -157,20 +157,23 @@ impl State {
trait Compilable { trait Compilable {
// Every AST node we visit can see the global compiler state (to make unique // Every AST node we visit can see the global compiler state (to make unique
// symbols and reach for global names) as well as optionally the current // symbols and reach for global names) as well as optionally the current
// function (for AST nodes within functions) // function (for AST nodes within functions). Additionally se send a Location:
// This is the closest ancestor's location, in case we need to emit a compile
// error
fn process( fn process(
self, self,
state: &mut State, state: &mut State,
function: Option<&mut CompiledFn>, function: Option<&mut CompiledFn>,
location: Location
) -> Result<(), CompileError>; ) -> Result<(), CompileError>;
} }
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
impl Compilable for Program { impl Compilable for Program {
fn process(self, state: &mut State, _: Option<&mut CompiledFn>) -> Result<(), CompileError> { fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
for decl in self.0 { for decl in self.0 {
decl.process(state, None)? decl.ast.process(state, None, decl.location)?
} }
Ok(()) Ok(())
} }
@@ -179,11 +182,11 @@ impl Compilable for Program {
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
impl Compilable for Declaration { impl Compilable for Declaration {
fn process(self, state: &mut State, _: Option<&mut CompiledFn>) -> Result<(), CompileError> { fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
match self { match self {
Declaration::Function(f) => f.process(state, None), Declaration::Function(f) => f.process(state, None, loc),
Declaration::Global(g) => g.process(state, None), Declaration::Global(g) => g.process(state, None, loc),
Declaration::Const(c) => c.process(state, None), Declaration::Const(c) => c.process(state, None, loc),
} }
} }
} }
@@ -191,7 +194,7 @@ impl Compilable for Declaration {
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
impl Compilable for Function { impl Compilable for Function {
fn process(self, state: &mut State, _: Option<&mut CompiledFn>) -> Result<(), CompileError> { fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
// The signature for this function, which will eventually get added to the state // The signature for this function, which will eventually get added to the state
let mut sig = CompiledFn { let mut sig = CompiledFn {
label: state.gensym(), label: state.gensym(),
@@ -213,21 +216,22 @@ impl Compilable for Function {
// Compile each statement: // Compile each statement:
for stmt in self.body.0 { for stmt in self.body.0 {
match stmt { let loc = stmt.location;
match stmt.ast {
Statement::Return(_) => {} Statement::Return(_) => {}
Statement::Assignment(assign) => assign.process(state, Some(&mut sig))?, Statement::Assignment(assign) => assign.process(state, Some(&mut sig), loc)?,
Statement::Expr(expr) => { Statement::Expr(expr) => {
expr.process(state, Some(&mut sig))?; expr.process(state, Some(&mut sig), loc)?;
// Every expr leaves a single-word return value on the stack. In an rvalue this // Every expr leaves a single-word return value on the stack. In an rvalue this
// is useful but in a statement it's garbage (because nothing else is about to // is useful but in a statement it's garbage (because nothing else is about to
// pick it up) so, drop it: // pick it up) so, drop it:
sig.emit("pop") sig.emit("pop")
} }
Statement::VarDecl(vardecl) => vardecl.process(state, Some(&mut sig))?, Statement::VarDecl(vardecl) => vardecl.process(state, Some(&mut sig), loc)?,
Statement::Asm(Asm { args, body}) => { Statement::Asm(Asm { args, body}) => {
// Process all the args, if any // Process all the args, if any
for a in args { for a in args {
(*a.0).process(state, Some(&mut sig))? (*a.0).process(state, Some(&mut sig), loc)?
} }
// Emit the body // Emit the body
sig.emit(body.as_str()) sig.emit(body.as_str())
@@ -259,12 +263,12 @@ fn lookup<'a>(name: &str, global_scope: &'a Scope, local_scope: &'a Scope) -> Op
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
impl Compilable for Assignment { impl Compilable for Assignment {
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>) -> Result<(), CompileError> { fn process(self, state: &mut State, sig: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
let Assignment { lvalue, rvalue } = self; let Assignment { lvalue, rvalue } = self;
let sig = sig.expect("Assignment outside function"); let sig = sig.expect("Assignment outside function");
// First the value, then the address we'll storew it to // First the value, then the address we'll storew it to
rvalue.process(state, Some(sig))?; rvalue.process(state, Some(sig), loc)?;
lvalue.process(state, Some(sig))?; lvalue.process(state, Some(sig), loc)?;
sig.emit("storew"); sig.emit("storew");
Ok(()) Ok(())
} }
@@ -273,7 +277,7 @@ impl Compilable for Assignment {
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
impl Compilable for VarDecl { impl Compilable for VarDecl {
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>) -> Result<(), CompileError> { fn process(self, state: &mut State, sig: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
let sig = sig.expect("Var declaration outside function"); let sig = sig.expect("Var declaration outside function");
if self.size.is_some() { if self.size.is_some() {
todo!("Arrays are not yet supported") todo!("Arrays are not yet supported")
@@ -281,11 +285,11 @@ impl Compilable for VarDecl {
if let Some(initial) = self.initial { if let Some(initial) = self.initial {
// If it's got an initial value, we have to compile that before we add // If it's got an initial value, we have to compile that before we add
// the name to scope, or else UB will ensue if it refers to itself: // the name to scope, or else UB will ensue if it refers to itself:
initial.process(state, Some(sig))?; initial.process(state, Some(sig), loc)?;
// But then add it to scope and assign: // But then add it to scope and assign:
sig.add_local(&self.name)?; sig.add_local(&self.name)?;
// We'll just whip up an lvalue real quick... // We'll just whip up an lvalue real quick...
Lvalue::from(self.name).process(state, Some(sig))?; Lvalue::from(self.name).process(state, Some(sig), loc)?;
sig.emit("storew"); // And store the initial value there sig.emit("storew"); // And store the initial value there
} else { } else {
// Otherwise, just add it to scope and leave garbage in there: // Otherwise, just add it to scope and leave garbage in there:
@@ -299,7 +303,7 @@ impl Compilable for VarDecl {
/// Evaluate an lvalue and leave its address on the stack (ready to be consumed by storew) /// Evaluate an lvalue and leave its address on the stack (ready to be consumed by storew)
impl Compilable for Lvalue { impl Compilable for Lvalue {
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>) -> Result<(), CompileError> { fn process(self, state: &mut State, sig: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
let global_scope = &state.global_scope; let global_scope = &state.global_scope;
let sig = sig.expect("lvalue outside a function"); let sig = sig.expect("lvalue outside a function");
@@ -346,7 +350,7 @@ impl Compilable for Lvalue {
} }
// Derefs are just evaluating the expr and leaving its value (an address) on the stack // Derefs are just evaluating the expr and leaving its value (an address) on the stack
Expr::Deref(BoxExpr(expr)) => { Expr::Deref(BoxExpr(expr)) => {
(*expr).process(state, Some(sig)) (*expr).process(state, Some(sig), loc)
} }
Expr::Subscript(_, _) => { Expr::Subscript(_, _) => {
Err(CompileError(0, 0, String::from("Arrays are not yet supported"))) Err(CompileError(0, 0, String::from("Arrays are not yet supported")))
@@ -360,7 +364,7 @@ impl Compilable for Lvalue {
/// Evaluate an expression in the context of a local scope. The runtime brother to eval_const. /// Evaluate an expression in the context of a local scope. The runtime brother to eval_const.
/// This recursively evaluates a Node and leaves its value on the stack. /// This recursively evaluates a Node and leaves its value on the stack.
impl Compilable for Expr { impl Compilable for Expr {
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>) -> Result<(), CompileError> { fn process(self, state: &mut State, sig: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
let mut sig = sig.expect("Non-const expression outside a function"); let mut sig = sig.expect("Non-const expression outside a function");
let global_scope = &state.global_scope; let global_scope = &state.global_scope;
@@ -412,21 +416,21 @@ impl Compilable for Expr {
} }
} }
Expr::Neg(e) => { Expr::Neg(e) => {
(*e.0).process(state, Some(sig))?; (*e.0).process(state, Some(sig), loc)?;
// To arithmetically negate something, invert and increment (2s complement) // To arithmetically negate something, invert and increment (2s complement)
sig.emit("xor -1"); sig.emit("xor -1");
sig.emit("add 1"); sig.emit("add 1");
Ok(()) Ok(())
} }
Expr::Not(e) => { Expr::Not(e) => {
(*e.0).process(state, Some(sig))?; (*e.0).process(state, Some(sig), loc)?;
sig.emit("not"); sig.emit("not");
Ok(()) Ok(())
} }
// Handling addresses is very easy because processing an lvalue leaves the address on the stack // Handling addresses is very easy because processing an lvalue leaves the address on the stack
Expr::Address(lvalue) => lvalue.process(state, Some(sig)), Expr::Address(lvalue) => lvalue.process(state, Some(sig), loc),
Expr::Deref(BoxExpr(e)) => { Expr::Deref(BoxExpr(e)) => {
(*e).process(state, Some(sig))?; (*e).process(state, Some(sig), loc)?;
sig.emit("loadw"); sig.emit("loadw");
Ok(()) Ok(())
} }
@@ -439,8 +443,8 @@ impl Compilable for Expr {
Expr::Subscript(_, _) => todo!("Structs and arrays are not yen supported"), Expr::Subscript(_, _) => todo!("Structs and arrays are not yen supported"),
Expr::Infix(lhs, op, rhs) => { Expr::Infix(lhs, op, rhs) => {
// Recurse on expressions, handling operators // Recurse on expressions, handling operators
(*lhs.0).process(state, Some(&mut sig))?; (*lhs.0).process(state, Some(&mut sig), loc)?;
(*rhs.0).process(state, Some(&mut sig))?; (*rhs.0).process(state, Some(&mut sig), loc)?;
match op { match op {
// Basic math // Basic math
Operator::Add => sig.emit("add"), Operator::Add => sig.emit("add"),
@@ -493,7 +497,7 @@ impl Compilable for Expr {
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
impl Compilable for Global { impl Compilable for Global {
fn process(self, state: &mut State, _: Option<&mut CompiledFn>) -> Result<(), CompileError> { fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
if self.size.is_some() { if self.size.is_some() {
todo!("Arrays are not yet supported") todo!("Arrays are not yet supported")
} }
@@ -504,7 +508,7 @@ impl Compilable for Global {
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
impl Compilable for Const { impl Compilable for Const {
fn process(self, state: &mut State, _: Option<&mut CompiledFn>) -> Result<(), CompileError> { fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
let var = if self.string.is_some() { let var = if self.string.is_some() {
// If it's a string, add it to the string table // If it's a string, add it to the string table
Variable::DirectLabel(state.add_string(&self.string.unwrap())) Variable::DirectLabel(state.add_string(&self.string.unwrap()))
@@ -625,7 +629,7 @@ mod test {
let mut state = State::default(); let mut state = State::default();
parse("const foo = 17 + 3;") parse("const foo = 17 + 3;")
.unwrap() .unwrap()
.process(&mut state, None) .process(&mut state, None, (0, 0).into())
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
state.global_scope, state.global_scope,
@@ -638,7 +642,7 @@ mod test {
let mut state = State::default(); let mut state = State::default();
parse("const foo = \"bar\";") parse("const foo = \"bar\";")
.unwrap() .unwrap()
.process(&mut state, None) .process(&mut state, None, (0, 0).into())
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
state.global_scope, state.global_scope,
@@ -651,7 +655,7 @@ mod test {
let mut state = State::default(); let mut state = State::default();
parse("global a;") parse("global a;")
.unwrap() .unwrap()
.process(&mut state, None) .process(&mut state, None, (0, 0).into())
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
state.global_scope, state.global_scope,
@@ -664,7 +668,7 @@ mod test {
let mut state = State::default(); let mut state = State::default();
assert!(parse("const a = 7; global a;") assert!(parse("const a = 7; global a;")
.unwrap() .unwrap()
.process(&mut state, None) .process(&mut state, None, (0, 0).into())
.is_err()); .is_err());
} }
@@ -672,7 +676,7 @@ mod test {
let mut state = State::default(); let mut state = State::default();
parse(src) parse(src)
.unwrap() .unwrap()
.process(&mut state, None) .process(&mut state, None, (0, 0).into())
.expect("Failed to compile"); .expect("Failed to compile");
state state
} }
+70 -48
View File
@@ -126,6 +126,13 @@ impl From<pest::error::Error<Rule>> for ParseError {
trait AstNode: Sized { trait AstNode: Sized {
const RULE: Rule; const RULE: Rule;
fn from_pair(pair: Pair) -> Self; fn from_pair(pair: Pair) -> Self;
fn from_pair_located(pair: Pair) -> Located<Self> {
let loc = pair.line_col();
Located {
location: loc.into(),
ast: Self::from_pair(pair)
}
}
} }
trait Parseable: Sized { trait Parseable: Sized {
@@ -307,7 +314,7 @@ impl AstNode for Block {
const RULE: Rule = Rule::block; const RULE: Rule = Rule::block;
fn from_pair(pair: Pair) -> Self { fn from_pair(pair: Pair) -> Self {
Self(pair.into_inner().map(Statement::from_pair).collect()) Self(pair.into_inner().map(Statement::from_pair_located).collect())
} }
} }
@@ -447,7 +454,7 @@ impl AstNode for Program {
Self( Self(
pair.into_inner() pair.into_inner()
.filter(|p| p.as_rule() != Rule::EOI) .filter(|p| p.as_rule() != Rule::EOI)
.map(Declaration::from_pair) .map(Declaration::from_pair_located)
.collect(), .collect(),
) )
} }
@@ -770,66 +777,79 @@ mod test {
#[test] #[test]
fn parse_block() { fn parse_block() {
let block = Block::from_str("{ foo(); bar(); }").unwrap();
let statements: Vec<_> = block.0.into_iter().map(|s| s.ast).collect();
assert_eq!( assert_eq!(
Block::from_str("{ foo(); bar(); }"), statements,
Ok(Block(vec![ vec![
Statement::Expr(Expr::Call("foo".into(), vec![])), Statement::Expr(Expr::Call("foo".into(), vec![])),
Statement::Expr(Expr::Call("bar".into(), vec![])), Statement::Expr(Expr::Call("bar".into(), vec![])),
])) ]
); );
} }
fn dislocate<T>(block: Vec<Located<T>>) -> Vec<T> {
block.into_iter().map(|l| l.ast).collect()
}
fn dislocated_block(src: &str) -> Vec<Statement> {
dislocate(Block::from_str(src).unwrap().0)
}
#[test] #[test]
fn parse_conditional() { fn parse_conditional() {
assert_eq!( if let Ok(Statement::Conditional(Conditional { condition, body, alternative })) =
Statement::from_str("if(cond) { foo(); }"), Statement::from_str("if(cond) { foo(); }") {
Ok(Statement::Conditional(Conditional { assert_eq!(condition, Expr::from_str("cond").unwrap());
condition: Expr::from_str("cond").unwrap(), assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
body: Block::from_str("{ foo(); }").unwrap(), assert_eq!(alternative, None);
alternative: None, } else {
})) panic!()
); }
assert_eq!( if let Ok(Statement::Conditional(Conditional { condition, body, alternative })) =
Statement::from_str("if(cond) { foo(); } else { bar(); }"), Statement::from_str("if(cond) { foo(); } else { bar(); }") {
Ok(Statement::Conditional(Conditional { assert_eq!(condition, Expr::from_str("cond").unwrap());
condition: Expr::from_str("cond").unwrap(), assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
body: Block::from_str("{ foo(); }").unwrap(), assert_eq!(alternative.unwrap().0[0].ast, Block::from_str("{ bar(); }").unwrap().0[0].ast);
alternative: Some(Block::from_str("{ bar(); }").unwrap()), } else {
})) panic!()
); }
} }
#[test] #[test]
fn parse_while_loops() { fn parse_while_loops() {
assert_eq!( assert!(match Statement::from_str("while(cond) { foo(); }") {
Statement::from_str("while(cond) { foo(); }"), Ok(Statement::WhileLoop(WhileLoop { condition, body })) => {
Ok(Statement::WhileLoop(WhileLoop { assert_eq!(condition, Expr::from_str("cond").unwrap());
condition: Expr::from_str("cond").unwrap(), assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
body: Block::from_str("{ foo(); }").unwrap(), true
})) }
); _ => false
});
} }
#[test] #[test]
fn parse_repeat_loops() { fn parse_repeat_loops() {
assert_eq!( assert!(match Statement::from_str("repeat(10) x { foo(x); }") {
Statement::from_str("repeat(10) x { foo(x); }"), Ok(Statement::RepeatLoop(RepeatLoop { count, name, body })) => {
Ok(Statement::RepeatLoop(RepeatLoop { assert_eq!(count, 10.into());
count: 10.into(), assert_eq!(name, Some("x".into()));
name: Some("x".into()), assert_eq!(dislocate(body.0), dislocated_block("{ foo(x); }"));
body: Block::from_str("{ foo(x); }").unwrap(), true
})) },
); _ => false
});
assert_eq!( assert!(match Statement::from_str("repeat(10) { foo(); }") {
Statement::from_str("repeat(10) { foo(); }"), Ok(Statement::RepeatLoop(RepeatLoop { count, name, body })) => {
Ok(Statement::RepeatLoop(RepeatLoop { assert_eq!(count, 10.into());
count: 10.into(), assert_eq!(name, None);
name: None, assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
body: Block::from_str("{ foo(); }").unwrap(), true
})) },
); _ => false
});
} }
#[test] #[test]
@@ -847,12 +867,14 @@ mod test {
#[test] #[test]
fn parse_program() { fn parse_program() {
let prog = Program::from_str("global foo; const blah = 3;").unwrap();
let decls: Vec<_> = dislocate(prog.0);
assert_eq!( assert_eq!(
Program::from_str("global foo; fn blah(x) { return x + 3; }"), decls,
Ok(Program(vec![ vec![
Declaration::from_str("global foo;").unwrap(), Declaration::from_str("global foo;").unwrap(),
Declaration::from_str("fn blah(x) { return x + 3; }").unwrap(), Declaration::from_str("const blah = 3;").unwrap(),
])) ]
) )
} }
} }