mirror of
https://github.com/kaesaecracker/echse.git
synced 2025-02-22 16:57:11 +01:00
improve error handling
This commit is contained in:
parent
5ad6baa30d
commit
a4a027b448
21
Cargo.lock
generated
21
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -5,9 +5,11 @@ pub(crate) trait TryLoadInstruction {
|
|||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, 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 },
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<W: Write>(
|
||||
&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<W: std::io::Write>(
|
||||
&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<ParametersError> for TryLoadErrorReason {
|
||||
fn from(reason: ParametersError) -> Self {
|
||||
TryLoadErrorReason::InvalidParameters(reason)
|
||||
}
|
||||
#[error(transparent)]
|
||||
InvalidParameters(#[from] ParametersError),
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
Loading…
Reference in a new issue