diff --git a/lib/examples/showoff.pasm b/lib/examples/showoff.pasm index 730b797..bb6b6a5 100755 --- a/lib/examples/showoff.pasm +++ b/lib/examples/showoff.pasm @@ -6,6 +6,9 @@ ldm r0, #206 ldm r1, #5 call read mov acc, 206 +ldm r0, #206 +ldm r1, #5 +call print end // procedure to print from linear memory diff --git a/lib/src/compile.rs b/lib/src/compile.rs index 7e6fd17..1c33cb2 100644 --- a/lib/src/compile.rs +++ b/lib/src/compile.rs @@ -20,13 +20,14 @@ use bincode::{Decode, Encode}; #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[derive(Debug)] struct CompiledInst { + pub id: u64, pub inst: String, pub op: Op, } impl CompiledInst { - pub fn new(inst: String, op: Op) -> Self { - Self { inst, op } + pub fn new(id: u64, inst: String, op: Op) -> Self { + Self { id, inst, op } } } @@ -60,10 +61,11 @@ impl CompiledProg { let prog = self .prog .into_iter() - .map(|(addr, CompiledInst { inst, op })| { + .map(|(addr, CompiledInst { inst, op, id })| { ( addr, ExecInst::new( + id, inst.parse::() .unwrap_or_else(|s| panic!("{s}")) .as_func_ptr(), @@ -92,14 +94,14 @@ where let prog = prog .into_iter() - .map(|(addr, ExecInst { func, op })| { - let str_inst = match T::from_func_ptr(func) { + .map(|(addr, ExecInst { op, id, .. })| { + let str_inst = match T::from_id(id) { Ok(inst) => inst, Err(e) => panic!("{e}"), } .to_string(); - (addr, CompiledInst::new(str_inst, op)) + (addr, CompiledInst::new(id, str_inst, op)) }) .collect(); diff --git a/lib/src/exec/error.rs b/lib/src/exec/error.rs index 64a6362..b43a8b0 100644 --- a/lib/src/exec/error.rs +++ b/lib/src/exec/error.rs @@ -65,6 +65,11 @@ impl Source { writeln!(write, "Runtime Error:")?; writeln!(write)?; + if self.0.is_empty() { + writeln!(write, "(source empty, error at position {pos})")?; + return writeln!(write, "message: {err}"); + } + for (i, s) in self.0.iter().enumerate() { if pos == i { if let Some(prev) = self.0.get(i - 1) { diff --git a/lib/src/exec/inst.rs b/lib/src/exec/inst.rs index 8e2e553..da471b7 100644 --- a/lib/src/exec/inst.rs +++ b/lib/src/exec/inst.rs @@ -14,13 +14,15 @@ pub type ExecFunc = fn(&mut Context, &Op) -> RtResult; /// Runtime representation of an instruction #[derive(Clone)] pub struct ExecInst { + /// Identifies the instruction with an integer, fixes rust-lang/rfcs#3535 + pub id: u64, pub func: ExecFunc, pub op: Op, } impl ExecInst { - pub fn new(inst: ExecFunc, op: Op) -> Self { - Self { func: inst, op } + pub fn new(id: u64, inst: ExecFunc, op: Op) -> Self { + Self { func: inst, op, id } } } diff --git a/lib/src/exec/memory.rs b/lib/src/exec/memory.rs index f3851f6..34619bd 100644 --- a/lib/src/exec/memory.rs +++ b/lib/src/exec/memory.rs @@ -44,6 +44,14 @@ impl Memory { } } +impl<'a> IntoIterator for &'a Memory { + type IntoIter = std::collections::btree_map::Iter<'a, usize, usize>; + type Item = (&'a usize, &'a usize); + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + impl From for Memory where T: Into>, diff --git a/lib/src/exec/mod.rs b/lib/src/exec/mod.rs index 700decc..5d9d1f8 100644 --- a/lib/src/exec/mod.rs +++ b/lib/src/exec/mod.rs @@ -262,7 +262,7 @@ impl Display for Context { writeln!(f, "{:>6}: Memory {{", "mem")?; - for (addr, entry) in self.mem.iter() { + for (addr, entry) in &self.mem { writeln!(f, "{addr:>8}: {entry},")?; } @@ -334,7 +334,7 @@ impl Executor { trace!( "Executing instruction {} {}", - T::from_func_ptr(inst.func).unwrap_or_else(|msg| panic!("{msg}")), + T::from_id(inst.id).unwrap_or_else(|msg| panic!("{msg}")), inst.op ); @@ -375,7 +375,7 @@ impl Executor { } } - pub fn display(&self) -> Result::Err> + pub fn display_with_opcodes(&self) -> Result::Err> where T: InstSet, ::Err: Display, @@ -388,8 +388,8 @@ impl Executor { writeln!(s, "Executor {{").unwrap(); - for (addr, ExecInst { op, func }) in &self.prog { - writeln!(s, "{addr:>6}: {func} {op}", func = T::from_func_ptr(*func)?).unwrap(); + for (addr, ExecInst { id, op, .. }) in &self.prog { + writeln!(s, "{addr:>6}: {func} {op}", func = T::from_id(*id)?).unwrap(); } s.push('}'); @@ -435,12 +435,12 @@ fn exec() { let prog: BTreeMap = BTreeMap::from( // Division algorithm from examples/division.pasm [ - (0, ExecInst::new(arith::inc, "202".into())), - (1, ExecInst::new(arith::add, "203,201".into())), - (2, ExecInst::new(cmp::cmp, "203,204".into())), - (3, ExecInst::new(cmp::jpn, "0".into())), - (4, ExecInst::new(mov::ldd, "202".into())), - (5, ExecInst::new(io::end, "".into())), + (0, ExecInst::new(0, arith::inc, "202".into())), + (1, ExecInst::new(0, arith::add, "203,201".into())), + (2, ExecInst::new(0, cmp::cmp, "203,204".into())), + (3, ExecInst::new(0, cmp::jpn, "0".into())), + (4, ExecInst::new(0, mov::ldd, "202".into())), + (5, ExecInst::new(0, io::end, "".into())), ], ); diff --git a/lib/src/inst.rs b/lib/src/inst.rs index 97a797b..4489eda 100644 --- a/lib/src/inst.rs +++ b/lib/src/inst.rs @@ -166,7 +166,8 @@ where ::Err: Display, { fn as_func_ptr(&self) -> ExecFunc; - fn from_func_ptr(_: ExecFunc) -> Result::Err>; + fn id(&self) -> u64; + fn from_id(_: u64) -> Result::Err>; } /// Macro to generate an instruction set @@ -179,6 +180,8 @@ macro_rules! inst_set { }; ($(#[$outer:meta])* $vis:vis $name:ident $using:item { $( $inst:ident => $func:expr,)+ }) => { $(#[$outer])* + #[repr(u64)] + #[derive(Clone, Copy)] $vis enum $name { $($inst,)+ } @@ -190,7 +193,7 @@ macro_rules! inst_set { fn from_str(s: &str) -> Result { match s.to_uppercase().as_str() { $( stringify!($inst) => Ok(Self::$inst),)+ - _ => Err(format!("{s} is not an operation")), + _ => Err(format!("{s} is not an instruction")), } } } @@ -213,15 +216,14 @@ macro_rules! inst_set { } } - fn from_func_ptr(f: $crate::exec::ExecFunc) -> Result { - $using - $( - const $inst: $crate::exec::ExecFunc = $func; - )+ + fn id(&self) -> u64 { + *self as u64 + } - match f { - $($inst => Ok(Self::$inst),)+ - _ => Err(format!("0x{:X} is not a valid function pointer", f as usize)), + fn from_id(id: u64) -> Result { + match id { + $(x if x == Self::$inst as u64 => Ok(Self::$inst),)+ + _ => Err(format!("0x{:X} is not a valid instruction ID", id)), } } } @@ -231,6 +233,8 @@ macro_rules! inst_set { /// Macro to extend an instruction set /// /// For an example, go to this [file](https://github.com/SaadiSave/cambridge-asm/blob/main/cambridge-asm/tests/int_test.rs) +/// +/// Due to language limitations, do not use this macro within the same file twice #[macro_export] macro_rules! extend { ($(#[$outer:meta])* $vis:vis $name:ident extends $parent:ident { $( $inst:ident => $func:expr,)+ }) => { @@ -238,9 +242,119 @@ macro_rules! extend { }; ($(#[$outer:meta])* $vis:vis $name:ident extends $parent:ident $using:item { $( $inst:ident => $func:expr,)+ }) => { $(#[$outer])* - $vis enum $name { - $($inst,)+ - Parent($parent) + $vis struct $name { + __private: extend_priv::Combined<$parent>, + } + + pub(crate) mod extend_priv { + use $crate::inst::InstSet; + use super::$parent; + #[repr(u64)] + #[derive(Clone, Copy)] + pub enum $name { + $($inst,)+ + #[allow(non_camel_case_types)] + LAST_INST_MARKER, + } + + impl std::str::FromStr for $name { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_uppercase().as_str() { + $( stringify!($inst) => Ok(Self::$inst),)+ + _ => Err(String::new()), + } + } + } + + impl $name { + fn id(self) -> u64 { + self as u64 + } + + fn as_func_ptr(&self) -> $crate::exec::ExecFunc { + $using + match self { + $(Self::$inst => $func,)+ + Self::LAST_INST_MARKER => panic!("This should never happen, report this as a bug"), + } + } + + fn from_id(id: u64) -> Result { + match id { + $(x if x == Self::$inst as u64 => Ok(Self::$inst),)+ + _ => Err(format!("0x{id:X} is not a valid instruction ID")), + } + } + } + + impl std::fmt::Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + $(Self::$inst => f.write_str(stringify!($inst)),)+ + Self::LAST_INST_MARKER => panic!("This should never happen, report this as a bug"), + } + } + } + + pub enum Combined + where + T: $crate::inst::InstSet, + ::Err: std::fmt::Display, + { + Extension($name), + Parent(T), + } + + impl Combined<$parent> { + const LAST_INST_MARKER: u64 = $name::LAST_INST_MARKER as u64; + + pub fn id(&self) -> u64 { + match self { + Self::Extension(ext) => ext.id(), + Self::Parent(parent) => Self::LAST_INST_MARKER + parent.id() + } + } + + pub fn from_id(id: u64) -> Result { + if id >= $name::LAST_INST_MARKER as u64 { + Ok(Combined::Parent($parent::from_id(id - Self::LAST_INST_MARKER)?)) + } else { + Ok(Combined::Extension($name::from_id(id)?)) + } + } + + pub fn as_func_ptr(&self) -> $crate::exec::ExecFunc { + match self { + Self::Extension(e) => e.as_func_ptr(), + Self::Parent(p) => p.as_func_ptr(), + } + } + } + + impl std::str::FromStr for Combined<$parent> { + type Err = String; + + fn from_str(s: &str) -> Result { + if let Ok(res) = s.parse::<$name>() { + Ok(Combined::Extension(res)) + } else if let Ok(res) = s.parse::<$parent>() { + Ok(Combined::Parent(res)) + } else { + Err(format!("{s} is not an instruction")) + } + } + } + + impl std::fmt::Display for Combined<$parent> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Extension(e) => write!(f, "{e}"), + Self::Parent(p) => write!(f, "{p}"), + } + } + } } $(#[$outer])* @@ -248,43 +362,29 @@ macro_rules! extend { type Err = String; fn from_str(s: &str) -> Result { - match s.to_uppercase().as_str() { - $( stringify!($inst) => Ok(Self::$inst),)+ - s => Ok(Self::Parent(s.parse::()?)), - } + Ok($name { __private: s.to_uppercase().as_str().parse::>()? }) } } $(#[$outer])* impl std::fmt::Display for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - $(Self::$inst => f.write_str(stringify!($inst)),)+ - Self::Parent(p) => write!(f, "{}", p), - } + write!(f, "{}", self.__private) } } $(#[$outer])* impl $crate::inst::InstSet for $name { fn as_func_ptr(&self) -> $crate::exec::ExecFunc { - $using - match self { - $(Self::$inst => $func,)+ - Self::Parent(p) => p.as_func_ptr() - } + self.__private.as_func_ptr() } - fn from_func_ptr(f: $crate::exec::ExecFunc) -> Result { - $using - $( - const $inst: $crate::exec::ExecFunc = $func; - )+ + fn id(&self) -> u64 { + self.__private.id() + } - match f { - $($inst => Ok(Self::$inst),)+ - f => Ok(Self::Parent($parent::from_func_ptr(f)?)), - } + fn from_id(id: u64) -> Result { + Ok( Self { __private: extend_priv::Combined::from_id(id)? }) } } }; @@ -296,6 +396,7 @@ where T: InstSet, ::Err: Display, { + pub id: u64, pub inst: T, pub op: Op, } @@ -306,10 +407,14 @@ where ::Err: Display, { pub fn new(inst: T, op: Op) -> Self { - Self { inst, op } + Self { + id: inst.id(), + op, + inst, + } } pub fn to_exec_inst(self) -> ExecInst { - ExecInst::new(self.inst.as_func_ptr(), self.op) + ExecInst::new(self.id, self.inst.as_func_ptr(), self.op) } } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 62d3c7d..427882a 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -56,6 +56,6 @@ const PROGRAMS: [(&str, usize, &[u8], &[u8]); 5] = [ include_str!("../examples/showoff.pasm"), 68, b"DIANA", - b"HELLO\n", + b"HELLO\nDIANA\n", ), ]; diff --git a/lib/src/parse/mod.rs b/lib/src/parse/mod.rs index 5c0676e..09997da 100644 --- a/lib/src/parse/mod.rs +++ b/lib/src/parse/mod.rs @@ -3,6 +3,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#![allow(clippy::upper_case_acronyms)] + use crate::{ exec::{Context, DebugInfo, ExecInst, Executor, Io, Memory, Source}, extend, @@ -158,7 +160,11 @@ where ); info!("Executor created"); - debug!("{}\n", exe.display::().unwrap_or_else(|s| panic!("{s}"))); + debug!( + "{}\n", + exe.display_with_opcodes::() + .unwrap_or_else(|s| panic!("{s}")) + ); debug!("The initial context:\n{}\n", exe.ctx); Ok(exe) diff --git a/lib/src/parse/parser.rs b/lib/src/parse/parser.rs index 5059b34..3113d84 100755 --- a/lib/src/parse/parser.rs +++ b/lib/src/parse/parser.rs @@ -29,6 +29,7 @@ type Line = Vec>; #[derive(Clone)] pub struct Parser<'a, I> { + #[allow(dead_code)] pub src: &'a str, lines: Vec, err: ErrorMap, @@ -101,8 +102,8 @@ where let mut ops = rest .iter() - .cloned() .filter(|t| !matches!(t, Token::Comma)) + .cloned() .map(Op::from) .collect::>(); @@ -182,7 +183,7 @@ where .filter(|v| !v.is_empty()) .collect::>(); - assert!((blocks.len() >= 2), "Unable to parse. Your source may not contain blank line(s) between the program and the memory, or the memory might be absent"); + assert!(blocks.len() >= 2, "Unable to parse. Your source may not contain blank line(s) between the program and the memory, or the memory might be absent"); let mems = blocks .pop() @@ -267,11 +268,7 @@ where } } - let mut ir = insts - .into_iter() - .enumerate() - .map(|(idx, inst)| (idx, inst)) - .collect::>(); + let mut ir = insts.into_iter().enumerate().collect::>(); for (to, from, multiop_idx) in links { match &ir[from].1.op { @@ -466,7 +463,7 @@ where pub fn new(addr: usize, opcode: I, op: Op) -> Self { Self { addr, - inst: inst::Inst { inst: opcode, op }, + inst: inst::Inst::new(opcode, op), } } } diff --git a/lib/test_stdio.rs b/lib/test_stdio.rs index 7a0509f..12a79f4 100644 --- a/lib/test_stdio.rs +++ b/lib/test_stdio.rs @@ -40,9 +40,9 @@ impl io::Read for TestStdio { if r_lock.is_empty() { return Err(Error::new(UnexpectedEof, "Input is empty")); - } else { - buf.write(&r_lock)? } + + buf.write(&r_lock)? }; self.0.write().unwrap().drain(0..written); diff --git a/lib/tests/int_test.rs b/lib/tests/int_test.rs index aa392f7..4827262 100644 --- a/lib/tests/int_test.rs +++ b/lib/tests/int_test.rs @@ -16,7 +16,7 @@ mod extension { } extend! { - Ext extends Core { + Ext extends Core use super::*; { EXT => ext, } }