From 2d8b35c5b203a5cecc82493b880a582aa5b4f9cd Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 1 May 2024 16:17:24 -0700 Subject: [PATCH] refactor(vm): generic impls for generic Objects (not implimented with special logic) Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- ic10emu/src/vm/mod.rs | 1 + ic10emu/src/vm/object/errors.rs | 28 ++ ic10emu/src/vm/object/macros.rs | 177 ++++++++-- ic10emu/src/vm/object/mod.rs | 59 ++-- ic10emu/src/vm/object/stationpedia.rs | 4 + ic10emu/src/vm/object/stationpedia/generic.rs | 323 ++++++++++++++++++ ic10emu/src/vm/object/traits.rs | 34 +- 7 files changed, 573 insertions(+), 53 deletions(-) create mode 100644 ic10emu/src/vm/object/errors.rs create mode 100644 ic10emu/src/vm/object/stationpedia/generic.rs diff --git a/ic10emu/src/vm/mod.rs b/ic10emu/src/vm/mod.rs index 0c9fabd..39f8ce6 100644 --- a/ic10emu/src/vm/mod.rs +++ b/ic10emu/src/vm/mod.rs @@ -54,6 +54,7 @@ pub struct VM { /// list of device id's touched on the last operation operation_modified: RefCell>, + #[allow(unused)] objects: Vec, } diff --git a/ic10emu/src/vm/object/errors.rs b/ic10emu/src/vm/object/errors.rs new file mode 100644 index 0000000..87b73f9 --- /dev/null +++ b/ic10emu/src/vm/object/errors.rs @@ -0,0 +1,28 @@ +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use crate::grammar::{LogicType, SlotLogicType}; + +#[derive(Error, Debug, Serialize, Deserialize)] +pub enum LogicError { + #[error("can't read LogicType {0}")] + CantRead(LogicType), + #[error("can't read slot {1} SlotLogicType {0}")] + CantSlotRead(SlotLogicType, usize), + #[error("can't write LogicType {0}")] + CantWrite(LogicType), + #[error("can't write slot {1} SlotLogicType {0}")] + CantSlotWrite(SlotLogicType, usize), + #[error("slot id {0} is out of range 0..{1}")] + SlotIndexOutOfRange(usize, usize) +} + +#[derive(Error, Debug, Serialize, Deserialize)] +pub enum MemoryError { + #[error("stack underflow: {0} < range [0..{1})")] + StackUnderflow(i32, usize), + #[error("stack overflow: {0} > range [0..{1})")] + StackOverflow(i32, usize), + #[error("memory unit not present")] + NotPresent, +} diff --git a/ic10emu/src/vm/object/macros.rs b/ic10emu/src/vm/object/macros.rs index cdb3fef..52278ca 100644 --- a/ic10emu/src/vm/object/macros.rs +++ b/ic10emu/src/vm/object/macros.rs @@ -1,15 +1,16 @@ macro_rules! object_trait { (@intf {$trait_name:ident $trt:path}) => { paste::paste! { - #[allow(missing_docs)] + #[allow(missing_docs, unused)] pub type [<$trt Ref>]<'a, T> = &'a dyn $trt::ID>; - #[allow(missing_docs)] + #[allow(missing_docs, unused)] pub type [<$trt RefMut>]<'a, T> = &'a mut dyn $trt::ID>; } }; (@body $trait_name:ident $($trt:path),*) => { type ID; fn id(&self) -> &Self::ID; + fn prefab(&self) -> &crate::vm::object::Name; fn type_name(&self) -> &str; @@ -46,29 +47,21 @@ pub(crate) use object_trait; macro_rules! ObjectInterface { { - #[custom(implements($trait_name:ident {$($trt:path),*}))] - $( #[$attr:meta] )* - $viz:vis struct $struct:ident { - $( - $(#[#field1:meta])* - $field1_viz:vis - $field1_name:ident : $field1_ty:ty, - ),* - #[custom(object_id)] - $(#[$id_attr:meta])* - $id_viz:vis $field_id:ident: $id_typ:ty, - $( - $(#[#field2:meta])* - $field2_viz:vis - $field2_name:ident : $field2_ty:ty, - ),* - } + @final + @trt $trait_name:ident; $struct:ident; + @impls $($trt:path),*; + @id $id_field:ident: $id_typ:ty; + @prefab $prefab_field:ident: $prefab_typ:ty; } => { impl $trait_name for $struct { type ID = $id_typ; fn id(&self) -> &Self::ID { - &self.$field_id + &self.$id_field + } + + fn prefab(&self) -> &crate::vm::object::Name { + &self.$prefab_field } fn type_name(&self) -> &str { @@ -87,18 +80,158 @@ macro_rules! ObjectInterface { paste::paste!{$( #[inline(always)] - fn [](&self) -> Option<[<$trt Ref>]> { + fn [](&self) -> Option<[<$trt Ref>]> { Some(self) } #[inline(always)] - fn [](&mut self) -> Option<[<$trt RefMut>]> { + fn [](&mut self) -> Option<[<$trt RefMut>]> { Some(self) } )*} } + }; + { + @body_id + @trt $trait_name:ident; $struct:ident; + @impls $($trt:path),*; + @id $id_field:ident: $id_typ:ty; + #[custom(object_prefab)] + $(#[$prefab_attr:meta])* + $prefab_viz:vis $prefab_field:ident: $prefab_typ:ty, + $( $rest:tt )* + } => { + $crate::vm::object::macros::ObjectInterface!{ + @final + @trt $trait_name; $struct; + @impls $($trt),*; + @id $id_field: $id_typ; + @prefab $prefab_field: $prefab_typ; + } + }; + { + @body_id + @trt $trait_name:ident; $struct:ident; + @impls $($trt:path),*; + @id $id_field:ident: $id_typ:ty; + $(#[#field:meta])* + $field_viz:vis + $field_name:ident : $field_ty:ty, + $( $rest:tt )* + } => { + $crate::vm::object::macros::ObjectInterface!{ + @body_id + @trt $trait_name; $struct; + @impls $($trt),*; + @id $id_field: $id_typ; + $( $rest )* + } + }; + { + @body_prefab + @trt $trait_name:ident; $struct:ident; + @impls $($trt:path),*; + @prefab $prefab_field:ident: $prefab_typ:ty; + #[custom(object_id)] + $(#[$id_attr:meta])* + $id_viz:vis $id_field:ident: $id_typ:ty, + $( $rest:tt )* + } => { + $crate::vm::object::macros::ObjectInterface!{ + @final + @trt $trait_name; $struct; + @impls $($trt),*; + @id $id_field: $id_typ; + @prefab $prefab_field: $prefab_typ; + } + }; + { + @body_prefab + @trt $trait_name:ident; $struct:ident; + @impls $($trt:path),*; + @prefab $prefab_field:ident: $prefab_typ:ty; + $(#[#field:meta])* + $field_viz:vis + $field_name:ident : $field_ty:ty, + $( $rest:tt )* + + } => { + $crate::vm::object::macros::ObjectInterface!{ + @body_prefab + @trt $trait_name; $struct; + @impls $($trt),*; + @prefab $prefab_field: $prefab_typ; + $( $rest )* + } + }; + { + @body + @trt $trait_name:ident; $struct:ident; + @impls $($trt:path),*; + #[custom(object_prefab)] + $(#[$prefab_attr:meta])* + $prefab_viz:vis $prefab_field:ident: $prefab_typ:ty, + $( $rest:tt )* + + } => { + $crate::vm::object::macros::ObjectInterface!{ + @body_prefab + @trt $trait_name; $struct; + @impls $($trt),*; + @prefab $prefab_field: $prefab_typ; + $( $rest )* + } + }; + { + @body + @trt $trait_name:ident; $struct:ident; + @impls $($trt:path),*; + #[custom(object_id)] + $(#[$id_attr:meta])* + $id_viz:vis $id_field:ident: $id_typ:ty, + $( $rest:tt )* + + } => { + $crate::vm::object::macros::ObjectInterface!{ + @body_id + @trt $trait_name; $struct; + @impls $($trt),*; + @id $id_field: $id_typ; + $( $rest )* + } + }; + { + @body + @trt $trait_name:ident; $struct:ident; + @impls $($trt:path),*; + $(#[#field:meta])* + $field_viz:vis + $field_name:ident : $field_ty:ty, + $( $rest:tt )* + + } => { + $crate::vm::object::macros::ObjectInterface!{ + @body + @trt $trait_name; $struct; + @impls $($trt),*; + $( $rest )* + } + }; + { + #[custom(implements($trait_name:ident {$($trt:path),*}))] + $( #[$attr:meta] )* + $viz:vis struct $struct:ident { + $( $body:tt )* + } + } => { + $crate::vm::object::macros::ObjectInterface!{ + @body + @trt $trait_name; $struct; + @impls $($trt),*; + $( $body )* + } }; } pub(crate) use ObjectInterface; diff --git a/ic10emu/src/vm/object/mod.rs b/ic10emu/src/vm/object/mod.rs index 2dd70d2..3b1b29e 100644 --- a/ic10emu/src/vm/object/mod.rs +++ b/ic10emu/src/vm/object/mod.rs @@ -1,38 +1,55 @@ use macro_rules_attribute::derive; +use serde::{Deserialize, Serialize}; mod macros; mod traits; mod stationpedia; +mod errors; -use macros::ObjectInterface; use traits::*; +use crate::{device::SlotType, grammar::SlotLogicType}; + pub type ObjectID = u32; pub type BoxedObject = Box>; - -#[derive(ObjectInterface!)] -#[custom(implements(Object { Memory }))] -pub struct Generic { - mem1: Vec, - - #[custom(object_id)] - id: ObjectID, - - mem2: Vec, +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct Name { + pub value: String, + pub hash: i32, } -impl Memory for Generic { - fn get_memory(&self) -> &Vec { - &self.mem1 +#[allow(unused)] +impl Name { + pub fn new(name: &str) -> Self { + Name { + value: name.to_owned(), + hash: const_crc32::crc32(name.as_bytes()) as i32, + } } - - fn set_memory(&mut self, index: usize, val: f64) { - self.mem2[index] = val; - } - - fn size(&self) -> usize { - self.mem1.len() + pub fn set(&mut self, name: &str) { + self.value = name.to_owned(); + self.hash = const_crc32::crc32(name.as_bytes()) as i32; } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum FieldType { + Read, + Write, + ReadWrite, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LogicField { + pub field_type: FieldType, + pub value: f64, +} + + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct Slot { + pub typ: SlotType, + pub enabeled_logic: Vec, + pub occupant: Option, +} diff --git a/ic10emu/src/vm/object/stationpedia.rs b/ic10emu/src/vm/object/stationpedia.rs index 861f708..b55bbf6 100644 --- a/ic10emu/src/vm/object/stationpedia.rs +++ b/ic10emu/src/vm/object/stationpedia.rs @@ -7,11 +7,13 @@ use crate::vm::object::BoxedObject; include!(concat!(env!("OUT_DIR"), "/stationpedia_prefabs.rs")); +#[allow(unused)] pub enum PrefabTemplate { Hash(i32), Name(String), } +#[allow(unused)] pub fn object_from_prefab_template(template: &PrefabTemplate) -> Option { let prefab = match template { PrefabTemplate::Hash(hash) => StationpediaPrefab::from_repr(*hash), @@ -24,3 +26,5 @@ pub fn object_from_prefab_template(template: &PrefabTemplate) -> Option None, } } + +mod generic; diff --git a/ic10emu/src/vm/object/stationpedia/generic.rs b/ic10emu/src/vm/object/stationpedia/generic.rs new file mode 100644 index 0000000..b922005 --- /dev/null +++ b/ic10emu/src/vm/object/stationpedia/generic.rs @@ -0,0 +1,323 @@ +use crate::{ + grammar::{LogicType, SlotLogicType}, + vm::{ + object::{ + errors::{LogicError, MemoryError}, + macros::ObjectInterface, + traits::*, + FieldType, LogicField, Name, ObjectID, Slot, + }, + VM, + }, +}; +use macro_rules_attribute::derive; +use std::{collections::BTreeMap, usize}; +use strum::IntoEnumIterator; + +pub trait GWLogicable { + fn name(&self) -> &Option; + fn fields(&self) -> &BTreeMap; + fn fields_mut(&mut self) -> &mut BTreeMap; + fn slots(&self) -> &Vec; + fn slots_mut(&mut self) -> &mut Vec; +} +macro_rules! GWLogicable { + ( + $( #[$attr:meta] )* + $viz:vis struct $struct:ident { + $($body:tt)* + } + ) => { + impl GWLogicable for $struct { + fn name(&self) -> &Option { + &self.name + } + fn fields(&self) -> &BTreeMap { + &self.fields + } + fn fields_mut(&mut self) -> &mut BTreeMap { + &mut self.fields + } + fn slots(&self) -> &Vec { + &self.slots + } + fn slots_mut(&mut self) -> &mut Vec { + &mut self.slots + } + } + }; +} +pub trait GWMemory { + fn memory_size(&self) -> usize; +} +macro_rules! GWMemory { + ( + $( #[$attr:meta] )* + $viz:vis struct $struct:ident { + $($body:tt)* + } + ) => { + impl GWMemory for $struct { + fn memory_size(&self) -> usize { + self.memory.len() + } + } + }; +} + +pub trait GWMemoryReadable: GWMemory { + fn memory(&self) -> &Vec; +} +macro_rules! GWMemoryReadable { + ( + $( #[$attr:meta] )* + $viz:vis struct $struct:ident { + $($body:tt)* + } + ) => { + impl GWMemoryReadable for $struct { + fn memory(&self) -> &Vec { + &self.memory + } + } + }; +} +pub trait GWMemoryWritable: GWMemory + GWMemoryReadable { + fn memory_mut(&mut self) -> &mut Vec; +} +macro_rules! GWMemoryWritable { + ( + $( #[$attr:meta] )* + $viz:vis struct $struct:ident { + $($body:tt)* + } + ) => { + impl GWMemoryWritable for $struct { + fn memory_mut(&mut self) -> &mut Vec { + &mut self.memory + } + } + }; +} + +pub trait GWDevice: GWLogicable {} +macro_rules! GWDevice { + ( + $( #[$attr:meta] )* + $viz:vis struct $struct:ident { + $($body:tt)* + } + ) => { + impl GWDevice for $struct {} + }; +} + +pub trait GWCircuitHolder: GWLogicable {} + +impl Logicable for T { + fn prefab_hash(&self) -> i32 { + self.prefab().hash + } + fn name_hash(&self) -> i32 { + self.name().as_ref().map(|name| name.hash).unwrap_or(0) + } + fn is_logic_readable(&self) -> bool { + LogicType::iter().any(|lt| self.can_logic_read(lt)) + } + fn is_logic_writeable(&self) -> bool { + LogicType::iter().any(|lt| self.can_logic_write(lt)) + } + fn can_logic_read(&self, lt: LogicType) -> bool { + self.fields() + .get(<) + .map(|field| matches!(field.field_type, FieldType::Read | FieldType::ReadWrite)) + .unwrap_or(false) + } + fn can_logic_write(&self, lt: LogicType) -> bool { + self.fields() + .get(<) + .map(|field| matches!(field.field_type, FieldType::Write | FieldType::ReadWrite)) + .unwrap_or(false) + } + fn get_logic(&self, lt: LogicType) -> Result { + self.fields() + .get(<) + .and_then(|field| match field.field_type { + FieldType::Read | FieldType::ReadWrite => Some(field.value), + _ => None, + }) + .ok_or(LogicError::CantRead(lt)) + } + fn set_logic(&mut self, lt: LogicType, value: f64, force: bool) -> Result<(), LogicError> { + self.fields_mut() + .get_mut(<) + .ok_or(LogicError::CantWrite(lt)) + .and_then(|field| match field.field_type { + FieldType::Write | FieldType::ReadWrite => { + field.value = value; + Ok(()) + } + _ if force => { + field.value = value; + Ok(()) + } + _ => Err(LogicError::CantWrite(lt)), + }) + } + fn slots_count(&self) -> usize { + self.slots().len() + } + fn get_slot(&self, index: usize) -> Option<&Slot> { + self.slots().get(index) + } + fn get_slot_mut(&mut self, index: usize) -> Option<&mut Slot> { + self.slots_mut().get_mut(index) + } + fn can_slot_logic_read(&self, slt: SlotLogicType, index: usize) -> bool { + self.get_slot(index) + .map(|slot| slot.enabeled_logic.contains(&slt)) + .unwrap_or(false) + } + fn get_slot_logic( + &self, + slt: SlotLogicType, + index: usize, + _vm: &VM, + ) -> Result { + self.get_slot(index) + .ok_or_else(|| LogicError::SlotIndexOutOfRange(index, self.slots().len())) + .and_then(|slot| { + if slot.enabeled_logic.contains(&slt) { + match slot.occupant { + Some(_id) => { + // FIXME: impliment by accessing VM to get occupant + Ok(0.0) + } + None => Ok(0.0), + } + } else { + Err(LogicError::CantSlotRead(slt, index)) + } + }) + } +} + +impl Memory for T { + fn memory_size(&self) -> usize { + self.memory_size() + } +} +impl MemoryReadable for T { + fn get_memory(&self, index: i32) -> Result { + if index < 0 { + Err(MemoryError::StackUnderflow(index, self.memory().len())) + } else if index as usize >= self.memory().len() { + Err(MemoryError::StackOverflow(index, self.memory().len())) + } else { + Ok(self.memory()[index as usize]) + } + } +} +impl MemoryWritable for T { + fn set_memory(&mut self, index: i32, val: f64) -> Result<(), MemoryError> { + if index < 0 { + Err(MemoryError::StackUnderflow(index, self.memory().len())) + } else if index as usize >= self.memory().len() { + Err(MemoryError::StackOverflow(index, self.memory().len())) + } else { + self.memory_mut()[index as usize] = val; + Ok(()) + } + } + fn clear_memory(&mut self) -> Result<(), MemoryError> { + self.memory_mut().fill(0.0); + Ok(()) + } +} + +impl Device for T {} + +#[derive(ObjectInterface!)] +#[custom(implements(Object { }))] +pub struct Generic { + #[custom(object_id)] + id: ObjectID, + #[custom(object_prefab)] + prefab: Name, +} + +#[derive(ObjectInterface!, GWLogicable!)] +#[custom(implements(Object { Logicable }))] +pub struct GenericLogicable { + #[custom(object_id)] + id: ObjectID, + #[custom(object_prefab)] + prefab: Name, + name: Option, + fields: BTreeMap, + slots: Vec, +} + +#[derive(ObjectInterface!, GWLogicable!, GWDevice!)] +#[custom(implements(Object { Logicable, Device }))] +pub struct GenericLogicableDevice { + #[custom(object_id)] + id: ObjectID, + #[custom(object_prefab)] + prefab: Name, + name: Option, + fields: BTreeMap, + slots: Vec, +} + +#[derive(ObjectInterface!, GWLogicable!, GWMemory!, GWMemoryReadable!)] +#[custom(implements(Object { Logicable, Memory, MemoryReadable }))] +pub struct GenericLogicableMemoryReadable { + #[custom(object_id)] + id: ObjectID, + #[custom(object_prefab)] + prefab: Name, + name: Option, + fields: BTreeMap, + slots: Vec, + memory: Vec, +} + +#[derive(ObjectInterface!, GWLogicable!, GWMemory!, GWMemoryReadable!, GWMemoryWritable!)] +#[custom(implements(Object { Logicable, Memory, MemoryReadable, MemoryWritable }))] +pub struct GenericLogicableMemoryReadWritable { + #[custom(object_id)] + id: ObjectID, + #[custom(object_prefab)] + prefab: Name, + name: Option, + fields: BTreeMap, + slots: Vec, + memory: Vec, +} + +#[derive(ObjectInterface!, GWLogicable!, GWDevice!, GWMemory!, GWMemoryReadable!, GWMemoryWritable!)] +#[custom(implements(Object { Logicable, Device, Memory, MemoryReadable }))] +pub struct GenericLogicableDeviceMemoryReadable { + #[custom(object_id)] + id: ObjectID, + #[custom(object_prefab)] + prefab: Name, + name: Option, + fields: BTreeMap, + slots: Vec, + memory: Vec, +} + +#[derive(ObjectInterface!, GWLogicable!, GWDevice!, GWMemory!, GWMemoryReadable!, GWMemoryWritable!)] +#[custom(implements(Object { Logicable, Device, Memory, MemoryReadable, MemoryWritable }))] +pub struct GenericLogicableDeviceMemoryReadWriteablable { + #[custom(object_id)] + id: ObjectID, + #[custom(object_prefab)] + prefab: Name, + name: Option, + fields: BTreeMap, + slots: Vec, + memory: Vec, +} diff --git a/ic10emu/src/vm/object/traits.rs b/ic10emu/src/vm/object/traits.rs index d71eb90..9569ec7 100644 --- a/ic10emu/src/vm/object/traits.rs +++ b/ic10emu/src/vm/object/traits.rs @@ -1,42 +1,52 @@ use std::fmt::Debug; -use crate::{grammar, vm::{object::{macros::tag_object_traits, ObjectID}, VM}}; +use crate::{grammar, vm::{object::{errors::{LogicError, MemoryError}, macros::tag_object_traits, ObjectID, Slot}, VM}}; tag_object_traits! { #![object_trait(Object: Debug)] pub trait Memory { - fn size(&self) -> usize; + fn memory_size(&self) -> usize; } pub trait MemoryWritable: Memory { - fn set_memory(&mut self, index: usize, val: f64); - fn clear_memory(&mut self); + fn set_memory(&mut self, index: i32, val: f64) -> Result<(), MemoryError>; + fn clear_memory(&mut self) -> Result<(), MemoryError>; } pub trait MemoryReadable: Memory { - fn get_memory(&self) -> &Vec; + fn get_memory(&self, index: i32) -> Result; } pub trait Logicable { + fn prefab_hash(&self) -> i32; + /// returns 0 if not set + fn name_hash(&self) -> i32; fn is_logic_readable(&self) -> bool; fn is_logic_writeable(&self) -> bool; - fn set_logic(&mut self, lt: grammar::LogicType, value: f64); - fn get_logic(&self, lt: grammar::LogicType) -> Option; + fn can_logic_read(&self, lt: grammar::LogicType) -> bool; + fn can_logic_write(&self, lt: grammar::LogicType) -> bool; + fn set_logic(&mut self, lt: grammar::LogicType, value: f64, force: bool) -> Result<(), LogicError>; + fn get_logic(&self, lt: grammar::LogicType) -> Result; fn slots_count(&self) -> usize; - // fn get_slot(&self, index: usize) -> Slot; - fn set_slot_logic(&mut self, slt: grammar::SlotLogicType, value: f64); - fn get_slot_logic(&self, slt: grammar::SlotLogicType) -> Option; + fn get_slot(&self, index: usize) -> Option<&Slot>; + fn get_slot_mut(&mut self, index: usize) -> Option<&mut Slot>; + fn can_slot_logic_read(&self, slt: grammar::SlotLogicType, index: usize) -> bool; + fn get_slot_logic(&self, slt: grammar::SlotLogicType, index: usize, vm: &VM) -> Result; } pub trait CircuitHolder: Logicable { fn clear_error(&mut self); fn set_error(&mut self, state: i32); fn get_logicable_from_index(&self, device: usize, vm: &VM) -> Option>; + fn get_logicable_from_index_mut(&self, device: usize, vm: &VM) -> Option>; fn get_logicable_from_id(&self, device: ObjectID, vm: &VM) -> Option>; + fn get_logicable_from_id_mut(&self, device: ObjectID, vm: &VM) -> Option>; fn get_source_code(&self) -> String; fn set_source_code(&self, code: String); + fn get_batch(&self) -> Vec>; + fn get_batch_mut(&self) -> Vec>; } pub trait SourceCode { @@ -53,6 +63,10 @@ tag_object_traits! { // fn logic_stack(&self) -> LogicStack; } + pub trait Device: Logicable { + + } + } impl Debug for dyn Object {