Errors now use Location instead of line numbers

This commit is contained in:
2022-06-07 21:00:27 -05:00
parent 4049ca072e
commit fa5dc84ad2
3 changed files with 108 additions and 53 deletions
+44 -14
View File
@@ -2,12 +2,42 @@ use std::fmt::{Display, Formatter};
use vcore::opcodes::InvalidMnemonic; use vcore::opcodes::InvalidMnemonic;
#[derive(Debug, PartialEq, Clone)]
pub struct Location {
pub line_num: usize,
pub file: String,
}
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum ParseError { pub enum ParseError {
LineParseFailure, LineParseFailure,
InvalidInstruction(String), InvalidInstruction(String),
} }
impl Display for Location {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.file, self.line_num)
}
}
impl Default for Location {
fn default() -> Self {
Location {
line_num: 0,
file: "<none>".to_string(),
}
}
}
impl From<usize> for Location {
fn from(line: usize) -> Self {
Self {
line_num: line,
file: "<none>".to_string(),
}
}
}
impl<'a> From<InvalidMnemonic<'a>> for ParseError { impl<'a> From<InvalidMnemonic<'a>> for ParseError {
fn from(err: InvalidMnemonic<'a>) -> Self { fn from(err: InvalidMnemonic<'a>) -> Self {
Self::InvalidInstruction(err.0.into()) Self::InvalidInstruction(err.0.into())
@@ -26,43 +56,43 @@ impl Display for ParseError {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum AssembleError { pub enum AssembleError {
ParseError(usize, ParseError), ParseError(Location, ParseError),
EquResolveError(usize, String, EvalError), EquResolveError(Location, String, EvalError),
EquDuplicateError(usize, String), EquDuplicateError(Location, String),
OrgResolveError(usize, EvalError), OrgResolveError(Location, EvalError),
ArgError(usize, EvalError), ArgError(Location, EvalError),
NoCode, NoCode,
IncludeError(usize, String), IncludeError(Location, String),
FileError(String), FileError(String),
MacroError(usize), MacroError(Location),
} }
impl Display for AssembleError { impl Display for AssembleError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
AssembleError::EquResolveError(line, name, err) => { AssembleError::EquResolveError(line, name, err) => {
write!(f, "Cannot resolve .equ {} on line {}: {}", name, line, err) write!(f, "Cannot resolve .equ {} at {}: {}", name, line, err)
} }
AssembleError::EquDuplicateError(line, name) => { AssembleError::EquDuplicateError(line, name) => {
write!(f, "Duplicate .equ {} on line {}", name, line) write!(f, "Duplicate .equ {} at {}", name, line)
} }
AssembleError::OrgResolveError(line, err) => { AssembleError::OrgResolveError(line, err) => {
write!(f, "Cannot resolve .org on line {}: {}", line, err) write!(f, "Cannot resolve .org at {}: {}", line, err)
} }
AssembleError::ArgError(line, err) => { AssembleError::ArgError(line, err) => {
write!(f, "Cannot calculate argument on line {}: {}", line, err) write!(f, "Cannot calculate argument at {}: {}", line, err)
} }
AssembleError::NoCode => { AssembleError::NoCode => {
write!(f, "No output would be generated by this code") write!(f, "No output would be generated by this code")
} }
AssembleError::ParseError(line, err) => { AssembleError::ParseError(line, err) => {
write!(f, "Parse error on line {}: {}", line, err) write!(f, "Parse error at {}: {}", line, err)
} }
AssembleError::IncludeError(line, file) => { AssembleError::IncludeError(line, file) => {
write!(f, "Cannot read \"{}\" on line {}", file, line) write!(f, "Cannot read \"{}\" at {}", file, line)
} }
AssembleError::MacroError(line) => { AssembleError::MacroError(line) => {
write!(f, "Malformed macro control structure on line {}", line) write!(f, "Malformed macro control structure at {}", line)
} }
AssembleError::FileError(file) => { AssembleError::FileError(file) => {
write!(f, "File read error in {}", file) write!(f, "File read error in {}", file)
+24 -17
View File
@@ -1,5 +1,5 @@
use crate::ast::{Label, Scope, VASMLine}; use crate::ast::{Label, Scope, VASMLine};
use crate::parse_error::AssembleError; use crate::parse_error::{AssembleError, Location};
use crate::vasm_evaluator::eval; use crate::vasm_evaluator::eval;
use crate::vasm_preprocessor::{Line, LineSource}; use crate::vasm_preprocessor::{Line, LineSource};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@@ -13,11 +13,15 @@ fn solve_equs(lines: &[Line]) -> Result<Scope, AssembleError> {
let line_nums: BTreeMap<usize, i32> = BTreeMap::new(); let line_nums: BTreeMap<usize, i32> = BTreeMap::new();
for (line_num, line) in lines.iter().enumerate() { for (line_num, line) in lines.iter().enumerate() {
if let VASMLine::Equ(Label(name), expr) = &line.line { if let VASMLine::Equ(Label(name), expr) = &line.line {
let value = eval(expr, line_num, &line_nums, &scope) let value = eval(expr, line_num, &line_nums, &scope).map_err(|e| {
.map_err(|e| AssembleError::EquResolveError(line_num, name.to_string(), e))?; AssembleError::EquResolveError(line.location.clone(), name.to_string(), e)
})?;
if let Some(_old_value) = scope.insert(name.clone(), value) { if let Some(_old_value) = scope.insert(name.clone(), value) {
return Err(AssembleError::EquDuplicateError(line_num, name.to_string())); return Err(AssembleError::EquDuplicateError(
line.location.clone(),
name.to_string(),
));
} }
} }
} }
@@ -101,7 +105,7 @@ fn place_labels(
for (line_num, line) in lines.iter().enumerate() { for (line_num, line) in lines.iter().enumerate() {
if let VASMLine::Org(_, expr) = &line.line { if let VASMLine::Org(_, expr) = &line.line {
address = eval(expr, line_num, &addresses, &scope) address = eval(expr, line_num, &addresses, &scope)
.map_err(|err| AssembleError::OrgResolveError(line_num, err))?; .map_err(|err| AssembleError::OrgResolveError(line.location.clone(), err))?;
addresses.insert(line_num, address); addresses.insert(line_num, address);
} }
@@ -167,9 +171,9 @@ pub fn assemble_snippet<T: IntoIterator<Item = String>>(
lines: T, lines: T,
) -> Result<Vec<u8>, AssembleError> { ) -> Result<Vec<u8>, AssembleError> {
let line_results: Vec<Result<Line, AssembleError>> = let line_results: Vec<Result<Line, AssembleError>> =
LineSource::new("_snippet", lines, |_file| { LineSource::new("<none>", lines, |_file| {
Err(AssembleError::IncludeError( Err(AssembleError::IncludeError(
0, Location::default(),
"Including is not supported in assembling snippets".to_string(), "Including is not supported in assembling snippets".to_string(),
)) ))
}) })
@@ -239,7 +243,7 @@ fn generate_code<T: IntoIterator<Item = Line>>(lines: T) -> Result<Vec<u8>, Asse
} }
VASMLine::Instruction(_, opcode, Some(arg)) => { VASMLine::Instruction(_, opcode, Some(arg)) => {
let arg = eval(arg, line_num, &line_addresses, &scope) let arg = eval(arg, line_num, &line_addresses, &scope)
.map_err(|err| AssembleError::ArgError(line_num, err))?; .map_err(|err| AssembleError::ArgError(line.location.clone(), err))?;
let len = line_lengths[&line_num] - 1; let len = line_lengths[&line_num] - 1;
let instr = (u8::from(*opcode) << 2) + len as u8; let instr = (u8::from(*opcode) << 2) + len as u8;
code[current_addr - start] = instr; code[current_addr - start] = instr;
@@ -255,7 +259,7 @@ fn generate_code<T: IntoIterator<Item = Line>>(lines: T) -> Result<Vec<u8>, Asse
} }
VASMLine::Db(_, arg) => { VASMLine::Db(_, arg) => {
let arg = eval(arg, line_num, &line_addresses, &scope) let arg = eval(arg, line_num, &line_addresses, &scope)
.map_err(|err| AssembleError::ArgError(line_num, err))?; .map_err(|err| AssembleError::ArgError(line.location.clone(), err))?;
poke_word(&mut code, current_addr - start, arg); poke_word(&mut code, current_addr - start, arg);
current_addr += 3; current_addr += 3;
} }
@@ -280,7 +284,6 @@ fn generate_code<T: IntoIterator<Item = Line>>(lines: T) -> Result<Vec<u8>, Asse
mod test { mod test {
use super::AssembleError::*; use super::AssembleError::*;
use super::*; use super::*;
use crate::ast::VASMLine;
use crate::parse_error; use crate::parse_error;
use crate::parse_error::EvalError::*; use crate::parse_error::EvalError::*;
use crate::vasm_parser::parse_vasm_line; use crate::vasm_parser::parse_vasm_line;
@@ -288,7 +291,11 @@ mod test {
fn parse<'a, T: IntoIterator<Item = &'a str>>(lines: T) -> Vec<Line> { fn parse<'a, T: IntoIterator<Item = &'a str>>(lines: T) -> Vec<Line> {
lines lines
.into_iter() .into_iter()
.map(|line| parse_vasm_line(line).unwrap().into()) .enumerate()
.map(|(line_num, line)| Line {
line: parse_vasm_line(line).unwrap(),
location: line_num.into(),
})
.collect() .collect()
} }
@@ -336,7 +343,7 @@ mod test {
assert_eq!( assert_eq!(
solve_equs(&parse(["blah: .equ 5", "foo: .equ banana"])), solve_equs(&parse(["blah: .equ 5", "foo: .equ banana"])),
Err(EquResolveError( Err(EquResolveError(
1, 1.into(),
"foo".into(), "foo".into(),
MissingLabel("banana".into()) MissingLabel("banana".into())
)) ))
@@ -344,14 +351,14 @@ mod test {
assert_eq!( assert_eq!(
solve_equs(&parse(["blah: .equ foo+3", "foo: .equ 7"])), solve_equs(&parse(["blah: .equ foo+3", "foo: .equ 7"])),
Err(EquResolveError( Err(EquResolveError(
0, 0.into(),
"blah".into(), "blah".into(),
MissingLabel("foo".into()) MissingLabel("foo".into())
)) ))
); );
assert_eq!( assert_eq!(
solve_equs(&parse(["blah: .equ 3", "blah: .equ 7"])), solve_equs(&parse(["blah: .equ 3", "blah: .equ 7"])),
Err(EquDuplicateError(1, "blah".into())) Err(EquDuplicateError(1.into(), "blah".into()))
); );
} }
@@ -443,11 +450,11 @@ mod test {
fn test_unresolvable_orgs() { fn test_unresolvable_orgs() {
assert_eq!( assert_eq!(
place_labels_pass([".org 0xffffff - blah"]), place_labels_pass([".org 0xffffff - blah"]),
Err(OrgResolveError(0, MissingLabel("blah".into()))) Err(OrgResolveError(0.into(), MissingLabel("blah".into())))
); );
assert_eq!( assert_eq!(
place_labels_pass(["blah: .org blah"]), place_labels_pass(["blah: .org blah"]),
Err(OrgResolveError(0, MissingLabel("blah".into()))) Err(OrgResolveError(0.into(), MissingLabel("blah".into())))
); );
} }
@@ -494,7 +501,7 @@ mod test {
assert_eq!( assert_eq!(
assemble_snippet(["apple"].map(String::from)), assemble_snippet(["apple"].map(String::from)),
Err(ParseError( Err(ParseError(
1, 1.into(),
parse_error::ParseError::InvalidInstruction("apple".into()) parse_error::ParseError::InvalidInstruction("apple".into())
)) ))
); );
+28 -10
View File
@@ -1,5 +1,5 @@
use crate::ast::{Macro, VASMLine}; use crate::ast::{Macro, VASMLine};
use crate::parse_error::AssembleError; use crate::parse_error::{AssembleError, Location};
use crate::vasm_parser::parse_vasm_line; use crate::vasm_parser::parse_vasm_line;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::iter::Enumerate; use std::iter::Enumerate;
@@ -7,8 +7,7 @@ use std::iter::Enumerate;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Line { pub struct Line {
pub line: VASMLine, pub line: VASMLine,
pub line_num: usize, pub location: Location,
pub file: String,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@@ -48,8 +47,10 @@ impl From<VASMLine> for Line {
fn from(other: VASMLine) -> Self { fn from(other: VASMLine) -> Self {
Line { Line {
line: other, line: other,
location: Location {
line_num: 0, line_num: 0,
file: "<none>".to_string(), file: "<none>".to_string(),
},
} }
} }
} }
@@ -76,7 +77,7 @@ impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>>
// Try and parse it // Try and parse it
return match parse_vasm_line(line.as_str()) { return match parse_vasm_line(line.as_str()) {
// We failed to parse it // We failed to parse it
Err(err) => Some(Err(AssembleError::ParseError(self.current_line, err))), Err(err) => Some(Err(AssembleError::ParseError(self.current_location(), err))),
// It's a macro, so do it and then try again // It's a macro, so do it and then try again
Ok(VASMLine::Macro(mac)) => { Ok(VASMLine::Macro(mac)) => {
@@ -92,8 +93,10 @@ impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>>
Ok(normal_line) => Some(Ok(Line { Ok(normal_line) => Some(Ok(Line {
line: normal_line, line: normal_line,
location: Location {
line_num: self.current_line, line_num: self.current_line,
file: self.filename_stack.last().unwrap().clone(), file: self.filename_stack.last().unwrap().clone(),
},
})), })),
}; };
} }
@@ -122,8 +125,10 @@ impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>>
fn emit(&mut self, line: String) { fn emit(&mut self, line: String) {
self.generated_lines.push_back(Line { self.generated_lines.push_back(Line {
line: parse_vasm_line(line.as_str()).unwrap(), line: parse_vasm_line(line.as_str()).unwrap(),
location: Location {
line_num: self.current_line, line_num: self.current_line,
file: self.filename_stack.last().unwrap().clone(), file: self.filename_stack.last().unwrap().clone(),
},
}) })
} }
@@ -132,6 +137,13 @@ impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>>
format!("__gensym_{}", self.current_sym) format!("__gensym_{}", self.current_sym)
} }
fn current_location(&self) -> Location {
Location {
line_num: self.current_line,
file: self.filename_stack.last().unwrap().clone(),
}
}
fn handle_macro(&mut self, mac: Macro) -> Option<AssembleError> { fn handle_macro(&mut self, mac: Macro) -> Option<AssembleError> {
match mac { match mac {
Macro::Include(file) => { Macro::Include(file) => {
@@ -166,7 +178,7 @@ impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>>
self.emit(format!("jmpr @{}", new_end)); self.emit(format!("jmpr @{}", new_end));
self.emit(format!("{}:", old_end)); self.emit(format!("{}:", old_end));
} else { } else {
return Some(AssembleError::MacroError(self.current_line)); return Some(AssembleError::MacroError(self.current_location()));
} }
} }
@@ -188,7 +200,7 @@ impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>>
}; };
self.emit(format!("{} @{}", instr, after)); self.emit(format!("{} @{}", instr, after));
} else { } else {
return Some(AssembleError::MacroError(self.current_line)); return Some(AssembleError::MacroError(self.current_location()));
} }
} }
@@ -198,7 +210,7 @@ impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>>
self.emit(format!("jmpr @{}", start)); self.emit(format!("jmpr @{}", start));
self.emit(format!("{}:", after)); self.emit(format!("{}:", after));
} }
_ => return Some(AssembleError::MacroError(self.current_line)), _ => return Some(AssembleError::MacroError(self.current_location())),
}, },
} }
None None
@@ -229,9 +241,11 @@ mod tests {
assert_eq!( assert_eq!(
src.next(), src.next(),
Some(Ok(Line { Some(Ok(Line {
line: VASMLine::Instruction(None, Add, None),
location: Location {
line_num: 1, line_num: 1,
file: "blah".to_string(), file: "blah".to_string(),
line: VASMLine::Instruction(None, Add, None) }
})) }))
); );
assert_eq!(src.next(), None); assert_eq!(src.next(), None);
@@ -246,14 +260,18 @@ mod tests {
src.collect::<Vec<Result<Line, AssembleError>>>(), src.collect::<Vec<Result<Line, AssembleError>>>(),
vec![ vec![
Ok(Line { Ok(Line {
line: VASMLine::Instruction(None, Sub, None),
location: Location {
line_num: 1, line_num: 1,
file: "foo".to_string(), file: "foo".to_string(),
line: VASMLine::Instruction(None, Sub, None) }
}), }),
Ok(Line { Ok(Line {
line: VASMLine::Instruction(None, Add, None),
location: Location {
line_num: 2, line_num: 2,
file: "blah".to_string(), file: "blah".to_string(),
line: VASMLine::Instruction(None, Add, None) }
}) })
] ]
); );