diff --git a/vasm_core/src/parse_error.rs b/vasm_core/src/parse_error.rs index 7fce027..49f5b32 100644 --- a/vasm_core/src/parse_error.rs +++ b/vasm_core/src/parse_error.rs @@ -2,12 +2,42 @@ use std::fmt::{Display, Formatter}; use vcore::opcodes::InvalidMnemonic; +#[derive(Debug, PartialEq, Clone)] +pub struct Location { + pub line_num: usize, + pub file: String, +} + #[derive(Debug, PartialEq, Clone)] pub enum ParseError { LineParseFailure, 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: "".to_string(), + } + } +} + +impl From for Location { + fn from(line: usize) -> Self { + Self { + line_num: line, + file: "".to_string(), + } + } +} + impl<'a> From> for ParseError { fn from(err: InvalidMnemonic<'a>) -> Self { Self::InvalidInstruction(err.0.into()) @@ -26,43 +56,43 @@ impl Display for ParseError { #[derive(Debug, Clone, PartialEq)] pub enum AssembleError { - ParseError(usize, ParseError), - EquResolveError(usize, String, EvalError), - EquDuplicateError(usize, String), - OrgResolveError(usize, EvalError), - ArgError(usize, EvalError), + ParseError(Location, ParseError), + EquResolveError(Location, String, EvalError), + EquDuplicateError(Location, String), + OrgResolveError(Location, EvalError), + ArgError(Location, EvalError), NoCode, - IncludeError(usize, String), + IncludeError(Location, String), FileError(String), - MacroError(usize), + MacroError(Location), } impl Display for AssembleError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { 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) => { - write!(f, "Duplicate .equ {} on line {}", name, line) + write!(f, "Duplicate .equ {} at {}", name, line) } 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) => { - write!(f, "Cannot calculate argument on line {}: {}", line, err) + write!(f, "Cannot calculate argument at {}: {}", line, err) } AssembleError::NoCode => { write!(f, "No output would be generated by this code") } AssembleError::ParseError(line, err) => { - write!(f, "Parse error on line {}: {}", line, err) + write!(f, "Parse error at {}: {}", line, err) } AssembleError::IncludeError(line, file) => { - write!(f, "Cannot read \"{}\" on line {}", file, line) + write!(f, "Cannot read \"{}\" at {}", file, line) } AssembleError::MacroError(line) => { - write!(f, "Malformed macro control structure on line {}", line) + write!(f, "Malformed macro control structure at {}", line) } AssembleError::FileError(file) => { write!(f, "File read error in {}", file) diff --git a/vasm_core/src/vasm_assembler.rs b/vasm_core/src/vasm_assembler.rs index 115f0a2..86adcc2 100644 --- a/vasm_core/src/vasm_assembler.rs +++ b/vasm_core/src/vasm_assembler.rs @@ -1,5 +1,5 @@ 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_preprocessor::{Line, LineSource}; use std::collections::BTreeMap; @@ -13,11 +13,15 @@ fn solve_equs(lines: &[Line]) -> Result { let line_nums: BTreeMap = BTreeMap::new(); for (line_num, line) in lines.iter().enumerate() { if let VASMLine::Equ(Label(name), expr) = &line.line { - let value = eval(expr, line_num, &line_nums, &scope) - .map_err(|e| AssembleError::EquResolveError(line_num, name.to_string(), e))?; + let value = eval(expr, line_num, &line_nums, &scope).map_err(|e| { + AssembleError::EquResolveError(line.location.clone(), name.to_string(), e) + })?; 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() { if let VASMLine::Org(_, expr) = &line.line { 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); } @@ -167,9 +171,9 @@ pub fn assemble_snippet>( lines: T, ) -> Result, AssembleError> { let line_results: Vec> = - LineSource::new("_snippet", lines, |_file| { + LineSource::new("", lines, |_file| { Err(AssembleError::IncludeError( - 0, + Location::default(), "Including is not supported in assembling snippets".to_string(), )) }) @@ -239,7 +243,7 @@ fn generate_code>(lines: T) -> Result, Asse } VASMLine::Instruction(_, opcode, Some(arg)) => { 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 instr = (u8::from(*opcode) << 2) + len as u8; code[current_addr - start] = instr; @@ -255,7 +259,7 @@ fn generate_code>(lines: T) -> Result, Asse } VASMLine::Db(_, arg) => { 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); current_addr += 3; } @@ -280,7 +284,6 @@ fn generate_code>(lines: T) -> Result, Asse mod test { use super::AssembleError::*; use super::*; - use crate::ast::VASMLine; use crate::parse_error; use crate::parse_error::EvalError::*; use crate::vasm_parser::parse_vasm_line; @@ -288,7 +291,11 @@ mod test { fn parse<'a, T: IntoIterator>(lines: T) -> Vec { lines .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() } @@ -336,7 +343,7 @@ mod test { assert_eq!( solve_equs(&parse(["blah: .equ 5", "foo: .equ banana"])), Err(EquResolveError( - 1, + 1.into(), "foo".into(), MissingLabel("banana".into()) )) @@ -344,14 +351,14 @@ mod test { assert_eq!( solve_equs(&parse(["blah: .equ foo+3", "foo: .equ 7"])), Err(EquResolveError( - 0, + 0.into(), "blah".into(), MissingLabel("foo".into()) )) ); assert_eq!( 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() { assert_eq!( place_labels_pass([".org 0xffffff - blah"]), - Err(OrgResolveError(0, MissingLabel("blah".into()))) + Err(OrgResolveError(0.into(), MissingLabel("blah".into()))) ); assert_eq!( 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!( assemble_snippet(["apple"].map(String::from)), Err(ParseError( - 1, + 1.into(), parse_error::ParseError::InvalidInstruction("apple".into()) )) ); diff --git a/vasm_core/src/vasm_preprocessor.rs b/vasm_core/src/vasm_preprocessor.rs index f35f713..15b7ed7 100644 --- a/vasm_core/src/vasm_preprocessor.rs +++ b/vasm_core/src/vasm_preprocessor.rs @@ -1,5 +1,5 @@ use crate::ast::{Macro, VASMLine}; -use crate::parse_error::AssembleError; +use crate::parse_error::{AssembleError, Location}; use crate::vasm_parser::parse_vasm_line; use std::collections::VecDeque; use std::iter::Enumerate; @@ -7,8 +7,7 @@ use std::iter::Enumerate; #[derive(Debug, PartialEq, Clone)] pub struct Line { pub line: VASMLine, - pub line_num: usize, - pub file: String, + pub location: Location, } #[derive(Debug, Clone, PartialEq)] @@ -48,8 +47,10 @@ impl From for Line { fn from(other: VASMLine) -> Self { Line { line: other, - line_num: 0, - file: "".to_string(), + location: Location { + line_num: 0, + file: "".to_string(), + }, } } } @@ -76,7 +77,7 @@ impl, F: Fn(String) -> Result> // Try and parse it return match parse_vasm_line(line.as_str()) { // 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 Ok(VASMLine::Macro(mac)) => { @@ -92,8 +93,10 @@ impl, F: Fn(String) -> Result> Ok(normal_line) => Some(Ok(Line { line: normal_line, - line_num: self.current_line, - file: self.filename_stack.last().unwrap().clone(), + location: Location { + line_num: self.current_line, + file: self.filename_stack.last().unwrap().clone(), + }, })), }; } @@ -122,8 +125,10 @@ impl, F: Fn(String) -> Result> fn emit(&mut self, line: String) { self.generated_lines.push_back(Line { line: parse_vasm_line(line.as_str()).unwrap(), - line_num: self.current_line, - file: self.filename_stack.last().unwrap().clone(), + location: Location { + line_num: self.current_line, + file: self.filename_stack.last().unwrap().clone(), + }, }) } @@ -132,6 +137,13 @@ impl, F: Fn(String) -> Result> 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 { match mac { Macro::Include(file) => { @@ -166,7 +178,7 @@ impl, F: Fn(String) -> Result> self.emit(format!("jmpr @{}", new_end)); self.emit(format!("{}:", old_end)); } else { - return Some(AssembleError::MacroError(self.current_line)); + return Some(AssembleError::MacroError(self.current_location())); } } @@ -188,7 +200,7 @@ impl, F: Fn(String) -> Result> }; self.emit(format!("{} @{}", instr, after)); } else { - return Some(AssembleError::MacroError(self.current_line)); + return Some(AssembleError::MacroError(self.current_location())); } } @@ -198,7 +210,7 @@ impl, F: Fn(String) -> Result> self.emit(format!("jmpr @{}", start)); self.emit(format!("{}:", after)); } - _ => return Some(AssembleError::MacroError(self.current_line)), + _ => return Some(AssembleError::MacroError(self.current_location())), }, } None @@ -229,9 +241,11 @@ mod tests { assert_eq!( src.next(), Some(Ok(Line { - line_num: 1, - file: "blah".to_string(), - line: VASMLine::Instruction(None, Add, None) + line: VASMLine::Instruction(None, Add, None), + location: Location { + line_num: 1, + file: "blah".to_string(), + } })) ); assert_eq!(src.next(), None); @@ -246,14 +260,18 @@ mod tests { src.collect::>>(), vec![ Ok(Line { - line_num: 1, - file: "foo".to_string(), - line: VASMLine::Instruction(None, Sub, None) + line: VASMLine::Instruction(None, Sub, None), + location: Location { + line_num: 1, + file: "foo".to_string(), + } }), Ok(Line { - line_num: 2, - file: "blah".to_string(), - line: VASMLine::Instruction(None, Add, None) + line: VASMLine::Instruction(None, Add, None), + location: Location { + line_num: 2, + file: "blah".to_string(), + } }) ] );