diff --git a/Cargo.lock b/Cargo.lock index 07cc970..9c3c0d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,6 +165,7 @@ version = "0.1.0" dependencies = [ "ariadne", "chumsky", + "thiserror", ] [[package]] @@ -332,6 +333,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" version = "1.0.14" diff --git a/Cargo.toml b/Cargo.toml index f6b8926..795f267 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ resolver = "2" clap = { version = "4.5.23", features = ["derive"] } shellwords = { version = "1.1.0" } ariadne = { version = "0.5.0", features = ["auto-color"] } +thiserror = "2.0.6" diff --git a/echse/Cargo.toml b/echse/Cargo.toml index 9f64a65..3715887 100644 --- a/echse/Cargo.toml +++ b/echse/Cargo.toml @@ -7,8 +7,6 @@ edition = "2021" [dependencies] chumsky = "0.9.3" -ariadne = {version = "0.5.0", optional = true} +ariadne = {version = "0.5.0"} +thiserror.workspace = true -[features] -default = ["error-report"] -error-report = ["dep:ariadne"] diff --git a/echse/src/instructions/add_instruction.rs b/echse/src/instructions/add_instruction.rs index afe1463..1d4d6d0 100644 --- a/echse/src/instructions/add_instruction.rs +++ b/echse/src/instructions/add_instruction.rs @@ -1,5 +1,6 @@ use crate::instructions::{ - try_load_binary_op, Instruction, InstructionName, ParametersError, TryLoadInstruction, + try_load_binary_op, ExecuteInstructionError, Instruction, InstructionName, InstructionResult, + ParametersError, TryLoadInstruction, }; use crate::machine::{Machine, RegisterIndex, RegisterOrValue}; use std::fmt::Display; @@ -16,9 +17,14 @@ pub struct Addition { } impl Instruction for Addition { - fn execute(&self, machine: &mut Machine) { - let value = self.a.read(machine) + self.b.read(machine); + fn execute(&self, machine: &mut Machine) -> InstructionResult { + let value = self + .a + .read(machine) + .checked_add(self.b.read(machine)) + .ok_or(ExecuteInstructionError::ArithmeticError)?; self.r.write(machine, value); + Ok(()) } } @@ -69,7 +75,8 @@ mod tests { r: r0, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![65, 0]); Instruction::execute( &Addition { @@ -78,7 +85,8 @@ mod tests { r: r1, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![65, 42]); Instruction::execute( &Addition { @@ -87,7 +95,8 @@ mod tests { r: r1, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![65, 1379]); } diff --git a/echse/src/instructions/div_instruction.rs b/echse/src/instructions/div_instruction.rs index ce0b659..e015e2b 100644 --- a/echse/src/instructions/div_instruction.rs +++ b/echse/src/instructions/div_instruction.rs @@ -1,5 +1,6 @@ use crate::instructions::{ - try_load_binary_op, Instruction, InstructionName, ParametersError, TryLoadInstruction, + try_load_binary_op, ExecuteInstructionError, Instruction, InstructionName, InstructionResult, + ParametersError, TryLoadInstruction, }; use crate::machine::{Machine, RegisterIndex, RegisterOrValue}; use std::fmt::{Debug, Display}; @@ -16,9 +17,14 @@ pub struct Division { } impl Instruction for Division { - fn execute(&self, machine: &mut Machine) { - let value = self.a.read(machine) / self.b.read(machine); - self.r.write(machine, value) + fn execute(&self, machine: &mut Machine) -> InstructionResult { + let value = self + .a + .read(machine) + .checked_div(self.b.read(machine)) + .ok_or(ExecuteInstructionError::ArithmeticError)?; + self.r.write(machine, value); + Ok(()) } } @@ -69,7 +75,8 @@ mod tests { r: r0, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![4, 0]); Instruction::execute( &Division { @@ -78,7 +85,8 @@ mod tests { r: r1, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![4, -4]); Instruction::execute( &Division { @@ -87,7 +95,8 @@ mod tests { r: r1, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![4, -334]); } diff --git a/echse/src/instructions/jmp_instruction.rs b/echse/src/instructions/jmp_instruction.rs index 5fc2247..eb72262 100644 --- a/echse/src/instructions/jmp_instruction.rs +++ b/echse/src/instructions/jmp_instruction.rs @@ -1,5 +1,6 @@ use crate::instructions::{ - check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction, + check_param_count, Instruction, InstructionName, InstructionResult, ParametersError, + TryLoadInstruction, }; use crate::machine::{Machine, RegisterOrValue}; use std::fmt::{Debug, Display}; @@ -12,8 +13,9 @@ pub struct Jump { } impl Instruction for Jump { - fn execute(&self, machine: &mut Machine) { + fn execute(&self, machine: &mut Machine) -> InstructionResult { machine.ip = self.to.read(machine) as usize; + Ok(()) } } @@ -55,7 +57,8 @@ mod tests { to: RegisterOrValue::Value(42), }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![0]); assert_eq!(machine.ip, 42); machine.registers[0].write(19); @@ -65,7 +68,8 @@ mod tests { to: RegisterOrValue::Register(r0), }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![19]); assert_eq!(machine.ip, 19); } diff --git a/echse/src/instructions/jze_instruction.rs b/echse/src/instructions/jze_instruction.rs index a34aee1..6e3ca6e 100644 --- a/echse/src/instructions/jze_instruction.rs +++ b/echse/src/instructions/jze_instruction.rs @@ -1,6 +1,8 @@ use crate::instructions::{ - check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction, + check_param_count, Instruction, InstructionName, InstructionResult, ParametersError, + TryLoadInstruction, }; +use crate::isize_to_bool; use crate::machine::{Machine, RegisterOrValue}; use std::fmt::{Debug, Display}; use std::rc::Rc; @@ -12,10 +14,11 @@ pub struct JumpIfZero { } impl Instruction for JumpIfZero { - fn execute(&self, machine: &mut Machine) { - if self.a.read(machine) == 0 { + fn execute(&self, machine: &mut Machine) -> InstructionResult { + if !isize_to_bool(self.a.read(machine)) { machine.ip = self.to.read(machine) as usize; } + Ok(()) } } @@ -62,7 +65,8 @@ mod tests { to: Value(42), }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![0]); assert_eq!(machine.ip, 42); machine.registers[0].write(19); @@ -73,7 +77,8 @@ mod tests { to: RegisterOrValue::Register(r0), }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![19]); assert_eq!(machine.ip, 42); } diff --git a/echse/src/instructions/mod.rs b/echse/src/instructions/mod.rs index d95e290..0b5d097 100644 --- a/echse/src/instructions/mod.rs +++ b/echse/src/instructions/mod.rs @@ -9,7 +9,6 @@ mod put_instruction; mod sub_instruction; mod teq_instruction; mod tlt_instruction; -mod traits; mod try_load; mod tze_instruction; @@ -24,6 +23,29 @@ pub use put_instruction::*; pub use sub_instruction::*; pub use teq_instruction::*; pub use tlt_instruction::*; -pub use traits::*; pub use try_load::*; pub use tze_instruction::*; + +use crate::machine::Machine; +use std::fmt::{Debug, Display}; + +pub trait Instruction: Debug + Display { + // TODO: this needs to be able to return errors, e.g. for division by zero + fn execute(&self, machine: &mut Machine) -> InstructionResult; +} + +type InstructionResult = Result<(), ExecuteInstructionError>; + +#[derive(Debug, thiserror::Error)] +pub enum ExecuteInstructionError { + #[error("A break instruction has reached")] + Break, + #[error("An arithmetic exception occurred")] + ArithmeticError, + #[error("the current instruction pointer does not point to an instruction. Has the end of file been reached?")] + InvalidIp, +} + +pub trait InstructionName { + const INSTRUCTION_NAME: &'static str; +} diff --git a/echse/src/instructions/mod_instruction.rs b/echse/src/instructions/mod_instruction.rs index 596dc6d..3aba4d9 100644 --- a/echse/src/instructions/mod_instruction.rs +++ b/echse/src/instructions/mod_instruction.rs @@ -1,5 +1,6 @@ use crate::instructions::{ - try_load_binary_op, Instruction, InstructionName, ParametersError, TryLoadInstruction, + try_load_binary_op, ExecuteInstructionError, Instruction, InstructionName, InstructionResult, + ParametersError, TryLoadInstruction, }; use crate::machine::{Machine, RegisterIndex, RegisterOrValue}; use std::fmt::{Debug, Display}; @@ -16,9 +17,14 @@ pub struct Modulus { } impl Instruction for Modulus { - fn execute(&self, machine: &mut Machine) { - let value = self.a.read(machine) % self.b.read(machine); - self.r.write(machine, value) + fn execute(&self, machine: &mut Machine) -> InstructionResult { + let value = self + .a + .read(machine) + .checked_rem(self.b.read(machine)) + .ok_or(ExecuteInstructionError::ArithmeticError)?; + self.r.write(machine, value); + Ok(()) } } @@ -69,7 +75,8 @@ mod tests { r: r0, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![19, 0]); Instruction::execute( &Modulus { @@ -78,7 +85,8 @@ mod tests { r: r1, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![19, 19]); Instruction::execute( &Modulus { @@ -87,7 +95,8 @@ mod tests { r: r1, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![19, 7]); } diff --git a/echse/src/instructions/nop_instruction.rs b/echse/src/instructions/nop_instruction.rs index 45f33bc..33defe2 100644 --- a/echse/src/instructions/nop_instruction.rs +++ b/echse/src/instructions/nop_instruction.rs @@ -1,5 +1,6 @@ use crate::instructions::{ - check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction, + check_param_count, Instruction, InstructionName, InstructionResult, ParametersError, + TryLoadInstruction, }; use crate::machine::{Machine, RegisterOrValue}; use std::fmt::{Debug, Display}; @@ -9,8 +10,9 @@ use std::rc::Rc; pub struct NoOperation {} impl Instruction for NoOperation { - fn execute(&self, _: &mut Machine) { + fn execute(&self, _: &mut Machine) -> InstructionResult { // intentionally left empty as this is the no operation instruction + Ok(()) } } diff --git a/echse/src/instructions/put_instruction.rs b/echse/src/instructions/put_instruction.rs index d975ff8..c7b4cce 100644 --- a/echse/src/instructions/put_instruction.rs +++ b/echse/src/instructions/put_instruction.rs @@ -1,5 +1,6 @@ use crate::instructions::{ - check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction, + check_param_count, Instruction, InstructionName, InstructionResult, ParametersError, + TryLoadInstruction, }; use crate::machine::{Machine, RegisterIndex, RegisterOrValue}; use std::fmt::{Debug, Display}; @@ -14,9 +15,10 @@ pub struct Put { } impl Instruction for Put { - fn execute(&self, machine: &mut Machine) { + fn execute(&self, machine: &mut Machine) -> InstructionResult { let value = self.a.read(machine); self.r.write(machine, value); + Ok(()) } } @@ -65,7 +67,8 @@ mod tests { r: r0, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![42, 0]); Instruction::execute( &Put { @@ -73,7 +76,8 @@ mod tests { r: r1, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![42, 42]); } } diff --git a/echse/src/instructions/sub_instruction.rs b/echse/src/instructions/sub_instruction.rs index 2a1e43b..2a2dab4 100644 --- a/echse/src/instructions/sub_instruction.rs +++ b/echse/src/instructions/sub_instruction.rs @@ -1,5 +1,6 @@ use crate::instructions::{ - try_load_binary_op, Instruction, InstructionName, ParametersError, TryLoadInstruction, + try_load_binary_op, ExecuteInstructionError, Instruction, InstructionName, InstructionResult, + ParametersError, TryLoadInstruction, }; use crate::machine::{Machine, RegisterIndex, RegisterOrValue}; use std::fmt::{Debug, Display}; @@ -16,9 +17,14 @@ pub struct Subtraction { } impl Instruction for Subtraction { - fn execute(&self, machine: &mut Machine) { - let value = self.a.read(machine) - self.b.read(machine); + fn execute(&self, machine: &mut Machine) -> InstructionResult { + let value = self + .a + .read(machine) + .checked_sub(self.b.read(machine)) + .ok_or(ExecuteInstructionError::ArithmeticError)?; self.r.write(machine, value); + Ok(()) } } @@ -69,7 +75,8 @@ mod tests { r: r0, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![19, 0]); Instruction::execute( &Subtraction { @@ -78,7 +85,8 @@ mod tests { r: r1, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![19, 42]); Instruction::execute( &Subtraction { @@ -87,7 +95,8 @@ mod tests { r: r1, }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers.read_all(), vec![19, 1295]); } diff --git a/echse/src/instructions/teq_instruction.rs b/echse/src/instructions/teq_instruction.rs index 85012b2..9f8fa66 100644 --- a/echse/src/instructions/teq_instruction.rs +++ b/echse/src/instructions/teq_instruction.rs @@ -1,5 +1,7 @@ +use crate::bool_to_isize; use crate::instructions::{ - check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction, + check_param_count, Instruction, InstructionName, InstructionResult, ParametersError, + TryLoadInstruction, }; use crate::machine::{Machine, RegisterOrValue}; use std::fmt::{Debug, Display}; @@ -12,9 +14,10 @@ pub struct TestEqual { } impl Instruction for TestEqual { - fn execute(&self, machine: &mut Machine) { + fn execute(&self, machine: &mut Machine) -> InstructionResult { let result = self.a.read(machine) == self.b.read(machine); - machine.registers[0].write(if result { 1 } else { 0 }); + machine.registers[0].write(bool_to_isize(result)); + Ok(()) } } @@ -59,7 +62,8 @@ mod tests { b: RegisterOrValue::Value(23), }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers[0].read(), 0); machine.registers[0].write(-23); @@ -69,7 +73,8 @@ mod tests { b: RegisterOrValue::Value(-23), }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers[0].read(), 1); } diff --git a/echse/src/instructions/tlt_instruction.rs b/echse/src/instructions/tlt_instruction.rs index 8f32d06..68d7281 100644 --- a/echse/src/instructions/tlt_instruction.rs +++ b/echse/src/instructions/tlt_instruction.rs @@ -1,5 +1,7 @@ +use crate::bool_to_isize; use crate::instructions::{ - check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction, + check_param_count, Instruction, InstructionName, InstructionResult, ParametersError, + TryLoadInstruction, }; use crate::machine::{Machine, RegisterOrValue}; use std::fmt::{Debug, Display}; @@ -12,9 +14,10 @@ pub struct TestLessThan { } impl Instruction for TestLessThan { - fn execute(&self, machine: &mut Machine) { + fn execute(&self, machine: &mut Machine) -> InstructionResult { let result = self.a.read(machine) < self.b.read(machine); - machine.registers[0].write(if result { 1 } else { 0 }); + machine.registers[0].write(bool_to_isize(result)); + Ok(()) } } @@ -59,7 +62,8 @@ mod tests { b: RegisterOrValue::Value(23), }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers[0].read(), 0); machine.registers[0].write(-24); @@ -69,7 +73,8 @@ mod tests { b: RegisterOrValue::Value(-23), }, &mut machine, - ); + ) + .unwrap(); assert_eq!(machine.registers[0].read(), 1); } diff --git a/echse/src/instructions/traits.rs b/echse/src/instructions/traits.rs deleted file mode 100644 index eb0ad28..0000000 --- a/echse/src/instructions/traits.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::machine::Machine; -use std::fmt::{Debug, Display}; - -pub trait Instruction: Debug + Display { - // TODO: this needs to be able to return errors, e.g. for division by zero - fn execute(&self, machine: &mut Machine); -} - -pub trait InstructionName { - const INSTRUCTION_NAME: &'static str; -} diff --git a/echse/src/instructions/try_load.rs b/echse/src/instructions/try_load.rs index f20d527..bf07517 100644 --- a/echse/src/instructions/try_load.rs +++ b/echse/src/instructions/try_load.rs @@ -5,9 +5,11 @@ pub(crate) trait TryLoadInstruction { fn try_load(params: &[RegisterOrValue]) -> Result, ParametersError>; } -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum ParametersError { + #[error("the instruction expects {expected} parameters, but {found} were provided")] UnexpectedCount { expected: usize, found: usize }, + #[error("unexpected type of parameter at index {index}")] UnexpectedType { index: usize }, } diff --git a/echse/src/instructions/tze_instruction.rs b/echse/src/instructions/tze_instruction.rs index 19ee519..734644d 100644 --- a/echse/src/instructions/tze_instruction.rs +++ b/echse/src/instructions/tze_instruction.rs @@ -1,5 +1,7 @@ +use crate::bool_to_isize; use crate::instructions::{ - check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction, + check_param_count, Instruction, InstructionName, InstructionResult, ParametersError, + TryLoadInstruction, }; use crate::machine::{Machine, RegisterOrValue}; use std::fmt::{Debug, Display}; @@ -11,9 +13,10 @@ pub struct TestZero { } impl Instruction for TestZero { - fn execute(&self, machine: &mut Machine) { + fn execute(&self, machine: &mut Machine) -> InstructionResult { let result = self.a.read(machine) == 0; - machine.registers[0].write(if result { 1 } else { 0 }); + machine.registers[0].write(bool_to_isize(result)); + Ok(()) } } @@ -49,14 +52,15 @@ mod tests { .unwrap(); assert_eq!(0, machine.registers[0].read()); - Instruction::execute(&TestZero { a: Value(42) }, &mut machine); + Instruction::execute(&TestZero { a: Value(42) }, &mut machine).unwrap(); assert_eq!(0, machine.registers[0].read()); Instruction::execute( &TestZero { a: Register(RegisterIndex::R0), }, &mut machine, - ); + ) + .unwrap(); assert_eq!(1, machine.registers[0].read()); } } diff --git a/echse/src/lib.rs b/echse/src/lib.rs index 2e17d8c..732ce04 100644 --- a/echse/src/lib.rs +++ b/echse/src/lib.rs @@ -5,3 +5,15 @@ pub mod parser; pub(crate) mod sealed { pub trait Sealed {} } + +pub fn isize_to_bool(value: isize) -> bool { + value != 0isize +} + +pub fn bool_to_isize(value: bool) -> isize { + if value { + 1isize + } else { + 0isize + } +} diff --git a/echse/src/machine/machine_struct.rs b/echse/src/machine/machine_struct.rs index d1a0a7b..e16c927 100644 --- a/echse/src/machine/machine_struct.rs +++ b/echse/src/machine/machine_struct.rs @@ -1,4 +1,4 @@ -use crate::instructions::Instructions; +use crate::instructions::{ExecuteInstructionError, Instructions}; use crate::machine::Registers; use crate::sealed::Sealed; use std::fmt::Display; @@ -11,15 +11,15 @@ pub struct Machine { } impl Machine { - pub fn step(&mut self) -> bool { + pub fn step(&mut self) -> Result<(), ExecuteInstructionError> { if self.ip >= self.instructions.len() { - return false; + return Err(ExecuteInstructionError::InvalidIp); } let instruction = &self.instructions[self.ip].clone(); - instruction.execute(self); + instruction.execute(self)?; self.ip += 1; - true + Ok(()) } } @@ -115,7 +115,7 @@ pub enum MachineBuilderError { #[cfg(test)] mod tests { - use crate::instructions::{Addition, Instructions}; + use crate::instructions::{Addition, ExecuteInstructionError, Instructions}; use crate::instructions::{Instruction, Put}; use crate::machine::register::{RegisterIndex, RegisterOrValue}; use crate::machine::MachineBuilder; @@ -145,15 +145,21 @@ mod tests { assert_eq!(machine.ip, 0); assert_eq!(reg_a.read(&machine), 0); - assert!(machine.step()); + assert!(matches!(machine.step(), Ok(()))); assert_eq!(machine.ip, 1); assert_eq!(reg_a.read(&machine), 42); - assert!(machine.step()); + assert!(matches!(machine.step(), Ok(()))); assert_eq!(machine.ip, 2); assert_eq!(reg_a.read(&machine), 65); - assert!(!machine.step()); - assert!(!machine.step()); + assert!(matches!( + machine.step(), + Err(ExecuteInstructionError::InvalidIp) + )); + assert!(matches!( + machine.step(), + Err(ExecuteInstructionError::InvalidIp) + )); } } diff --git a/echse/src/parser/ei.rs b/echse/src/parser/ei.rs index 27ce327..c49c4de 100644 --- a/echse/src/parser/ei.rs +++ b/echse/src/parser/ei.rs @@ -4,10 +4,10 @@ use crate::instructions::{ TryLoadInstruction, }; use crate::parser; -use crate::parser::ei_ast::{Line}; +use crate::parser::ei_ast::Line; +use crate::parser::ParseError; use std::io::Write; use std::rc::Rc; -use crate::parser::ParseError; pub struct EiParser { file_name: String, @@ -84,10 +84,7 @@ impl EiParser { illegal => return Err(TryLoadErrorReason::IllegalInstruction(illegal.to_string())), }) } -} -#[cfg(feature = "error-report")] -impl EiParser { pub fn write_error_report( &self, ei_parser_error: EiParserError, @@ -115,7 +112,7 @@ impl EiParser { use ariadne::{Color, Label, Report, ReportKind, Source}; let report = Report::build(ReportKind::Error, (&self.file_name, e.line.span.clone())) - .with_message("Loading of instructions failed") + .with_message(format!("{e}")) .with_config(Self::get_ariadne_config()); let report = match e.reason { @@ -142,14 +139,12 @@ impl EiParser { .with_color(Color::Red) .with_message("not enough parameters") }; - report.with_label(label).with_note(format!( - "the instruction expects {expected} parameters, but {found} were provided." - )) + report.with_label(label) } ParametersError::UnexpectedType { index } => { let label = Label::new((&self.file_name, e.line.parameters[index].1.clone())) .with_color(Color::Red) - .with_message("unexpected type of parameter"); + .with_message(format!("{param_err}")); report.with_label(label).with_help( "check the parameter order and which ones can values and/or registers", ) @@ -162,7 +157,6 @@ impl EiParser { .write((&self.file_name, Source::from(&self.file_content)), w) } - #[cfg(feature = "error-report")] pub fn write_parse_error_report( &self, e: ParseError, @@ -246,21 +240,19 @@ pub enum EiParserError { InstructionLoadError(TryLoadError), } -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] +#[error("The loading of the instruction at index {index} failed: {reason}")] pub struct TryLoadError { pub index: usize, pub line: Line, + #[source] pub reason: TryLoadErrorReason, } -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum TryLoadErrorReason { + #[error("Illegal instruction \"{0}\"")] IllegalInstruction(String), - InvalidParameters(ParametersError), -} - -impl From for TryLoadErrorReason { - fn from(reason: ParametersError) -> Self { - TryLoadErrorReason::InvalidParameters(reason) - } + #[error(transparent)] + InvalidParameters(#[from] ParametersError), } diff --git a/echse_cute/src/main.rs b/echse_cute/src/main.rs index 3281f37..0d5bbaf 100644 --- a/echse_cute/src/main.rs +++ b/echse_cute/src/main.rs @@ -14,7 +14,7 @@ struct Cli { fn main() { let args: Cli = clap::Parser::parse(); - let parser = EiParser::from_file(&args.ei_file).unwrap(); + let parser = EiParser::from_file(&args.ei_file).expect("failed to parse input file"); let instructions = match parser.parse() { Ok(i) => i, Err(e) => { @@ -33,11 +33,19 @@ fn main() { println!("done building machine: \n{machine}"); - while machine.step() { - if args.verbose { - println!("executed instruction: {machine:?}"); + loop { + match machine.step() { + Ok(_) => { + if args.verbose { + println!("executed instruction: {machine:?}"); + } + } + Err(e) => { + eprintln!("{e}"); + break; + } } } - println!("final state: \n{machine}"); + println!("final state: \n{}", machine.registers); } diff --git a/krabbeltier/src/main.rs b/krabbeltier/src/main.rs index e05f1df..4c541ad 100644 --- a/krabbeltier/src/main.rs +++ b/krabbeltier/src/main.rs @@ -1,3 +1,4 @@ +use echse::instructions::ExecuteInstructionError; use echse::machine::{Machine, MachineBuilder}; use echse::parser::EiParser; use std::collections::HashSet; @@ -129,7 +130,10 @@ impl App { "{:03}: {}\n", self.machine.ip, self.machine.instructions[self.machine.ip] ); - self.machine.step(); + match self.machine.step() { + Ok(_) => {} + Err(e) => Self::print_exec_err(e), + } } else { println!("reached end of program\n"); } @@ -153,7 +157,12 @@ impl App { } DebugCommand::Continue => { let mut count = 0; - while self.machine.step() { + loop { + match self.machine.step() { + Ok(_) => {} + Err(e) => eprintln!("{e}"), + } + count += 1; if self.breakpoints.contains(&self.machine.ip) { println!( @@ -163,6 +172,7 @@ impl App { break; } } + println!( "stepped {count} instructions, ip now at {}\n", self.machine.ip @@ -188,7 +198,7 @@ impl App { fn main() { let args: Cli = clap::Parser::parse(); - let parser = EiParser::from_file(&args.ei_file).unwrap(); + let parser = EiParser::from_file(&args.ei_file).expect("failed to parse input file"); let instructions = match parser.parse() { Ok(i) => i, Err(e) => { diff --git a/all_instructions.ei b/samples/all_instructions.ei similarity index 100% rename from all_instructions.ei rename to samples/all_instructions.ei diff --git a/the_answer.ei b/samples/the_answer.ei similarity index 100% rename from the_answer.ei rename to samples/the_answer.ei