refactor(vm): generic impls for generic Objects (not implimented with special logic)

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
Rachel Powers
2024-05-01 16:17:24 -07:00
parent c3182035ae
commit 2d8b35c5b2
7 changed files with 573 additions and 53 deletions

View File

@@ -54,6 +54,7 @@ pub struct VM {
/// list of device id's touched on the last operation
operation_modified: RefCell<Vec<u32>>,
#[allow(unused)]
objects: Vec<object::BoxedObject>,
}

View File

@@ -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,
}

View File

@@ -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 = <T as $trait_name>::ID>;
#[allow(missing_docs)]
#[allow(missing_docs, unused)]
pub type [<$trt RefMut>]<'a, T> = &'a mut dyn $trt<ID = <T as $trait_name>::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 [<as_ $trt:lower>](&self) -> Option<[<$trt Ref>]<Self>> {
fn [<as_ $trt:snake>](&self) -> Option<[<$trt Ref>]<Self>> {
Some(self)
}
#[inline(always)]
fn [<as_ $trt:lower _mut>](&mut self) -> Option<[<$trt RefMut>]<Self>> {
fn [<as_ $trt:snake _mut>](&mut self) -> Option<[<$trt RefMut>]<Self>> {
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;

View File

@@ -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<dyn Object<ID = ObjectID>>;
#[derive(ObjectInterface!)]
#[custom(implements(Object { Memory }))]
pub struct Generic {
mem1: Vec<f64>,
#[custom(object_id)]
id: ObjectID,
mem2: Vec<f64>,
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Name {
pub value: String,
pub hash: i32,
}
impl Memory for Generic {
fn get_memory(&self) -> &Vec<f64> {
&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<SlotLogicType>,
pub occupant: Option<ObjectID>,
}

View File

@@ -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<BoxedObject> {
let prefab = match template {
PrefabTemplate::Hash(hash) => StationpediaPrefab::from_repr(*hash),
@@ -24,3 +26,5 @@ pub fn object_from_prefab_template(template: &PrefabTemplate) -> Option<BoxedObj
_ => None,
}
}
mod generic;

View File

@@ -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<Name>;
fn fields(&self) -> &BTreeMap<LogicType, LogicField>;
fn fields_mut(&mut self) -> &mut BTreeMap<LogicType, LogicField>;
fn slots(&self) -> &Vec<Slot>;
fn slots_mut(&mut self) -> &mut Vec<Slot>;
}
macro_rules! GWLogicable {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWLogicable for $struct {
fn name(&self) -> &Option<Name> {
&self.name
}
fn fields(&self) -> &BTreeMap<LogicType, LogicField> {
&self.fields
}
fn fields_mut(&mut self) -> &mut BTreeMap<LogicType, LogicField> {
&mut self.fields
}
fn slots(&self) -> &Vec<Slot> {
&self.slots
}
fn slots_mut(&mut self) -> &mut Vec<Slot> {
&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<f64>;
}
macro_rules! GWMemoryReadable {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWMemoryReadable for $struct {
fn memory(&self) -> &Vec<f64> {
&self.memory
}
}
};
}
pub trait GWMemoryWritable: GWMemory + GWMemoryReadable {
fn memory_mut(&mut self) -> &mut Vec<f64>;
}
macro_rules! GWMemoryWritable {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWMemoryWritable for $struct {
fn memory_mut(&mut self) -> &mut Vec<f64> {
&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<T: GWLogicable + Object> 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(&lt)
.map(|field| matches!(field.field_type, FieldType::Read | FieldType::ReadWrite))
.unwrap_or(false)
}
fn can_logic_write(&self, lt: LogicType) -> bool {
self.fields()
.get(&lt)
.map(|field| matches!(field.field_type, FieldType::Write | FieldType::ReadWrite))
.unwrap_or(false)
}
fn get_logic(&self, lt: LogicType) -> Result<f64, LogicError> {
self.fields()
.get(&lt)
.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(&lt)
.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<f64, LogicError> {
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<T: GWMemory + Object> Memory for T {
fn memory_size(&self) -> usize {
self.memory_size()
}
}
impl<T: GWMemoryReadable + Memory + Object> MemoryReadable for T {
fn get_memory(&self, index: i32) -> Result<f64, 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 {
Ok(self.memory()[index as usize])
}
}
}
impl<T: GWMemoryWritable + Memory + Object> 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<T: GWDevice + Object> 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<Name>,
fields: BTreeMap<LogicType, LogicField>,
slots: Vec<Slot>,
}
#[derive(ObjectInterface!, GWLogicable!, GWDevice!)]
#[custom(implements(Object { Logicable, Device }))]
pub struct GenericLogicableDevice {
#[custom(object_id)]
id: ObjectID,
#[custom(object_prefab)]
prefab: Name,
name: Option<Name>,
fields: BTreeMap<LogicType, LogicField>,
slots: Vec<Slot>,
}
#[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<Name>,
fields: BTreeMap<LogicType, LogicField>,
slots: Vec<Slot>,
memory: Vec<f64>,
}
#[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<Name>,
fields: BTreeMap<LogicType, LogicField>,
slots: Vec<Slot>,
memory: Vec<f64>,
}
#[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<Name>,
fields: BTreeMap<LogicType, LogicField>,
slots: Vec<Slot>,
memory: Vec<f64>,
}
#[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<Name>,
fields: BTreeMap<LogicType, LogicField>,
slots: Vec<Slot>,
memory: Vec<f64>,
}

View File

@@ -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<f64>;
fn get_memory(&self, index: i32) -> Result<f64, MemoryError>;
}
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<f64>;
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<f64, LogicError>;
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<f64>;
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<f64, LogicError>;
}
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<LogicableRef<Self>>;
fn get_logicable_from_index_mut(&self, device: usize, vm: &VM) -> Option<LogicableRefMut<Self>>;
fn get_logicable_from_id(&self, device: ObjectID, vm: &VM) -> Option<LogicableRef<Self>>;
fn get_logicable_from_id_mut(&self, device: ObjectID, vm: &VM) -> Option<LogicableRefMut<Self>>;
fn get_source_code(&self) -> String;
fn set_source_code(&self, code: String);
fn get_batch(&self) -> Vec<LogicableRef<Self>>;
fn get_batch_mut(&self) -> Vec<LogicableRefMut<Self>>;
}
pub trait SourceCode {
@@ -53,6 +63,10 @@ tag_object_traits! {
// fn logic_stack(&self) -> LogicStack;
}
pub trait Device: Logicable {
}
}
impl<T: Debug> Debug for dyn Object<ID = T> {