Skip to content

Commit

Permalink
refactor(vm): cleanup, trait docs, reenable interpreter tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryex committed May 28, 2024
1 parent dae6be9 commit 1843bdb
Show file tree
Hide file tree
Showing 33 changed files with 50,351 additions and 51,177 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions ic10emu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,12 @@ time = { version = "0.3.36", features = [
color-eyre = "0.6.3"
serde_json = "1.0.117"

# Self dev dependency to enable prefab_database feature for tests
ic10emu = { path = ".", features = ["prefab_database"] }

[features]
default = []
tsify = ["dep:tsify", "dep:wasm-bindgen", "stationeers_data/tsify"]
prefab_database = [
"stationeers_data/prefab_database",
] # compile with the prefab database enabled
25 changes: 11 additions & 14 deletions ic10emu/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,12 @@ use tsify::Tsify;
use wasm_bindgen::prelude::*;

#[derive(Error, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum VMError {
#[error("device with id '{0}' does not exist")]
UnknownId(u32),
UnknownId(ObjectID),
#[error("ic with id '{0}' does not exist")]
UnknownIcId(u32),
#[error("device with id '{0}' does not have a ic slot")]
NoIC(u32),
UnknownIcId(ObjectID),
#[error("ic encountered an error: {0}")]
ICError(#[from] ICError),
#[error("ic encountered an error: {0}")]
Expand All @@ -49,6 +46,10 @@ pub enum VMError {
NotAnItem(ObjectID),
#[error("object {0} is not programmable")]
NotProgrammable(ObjectID),
#[error("object {0} is not a circuit holder or programmable")]
NotCircuitHolderOrProgrammable(ObjectID),
#[error("object {0} is a circuit holder but there is no programmable ic present")]
NoIC(ObjectID),
#[error("{0}")]
TemplateError(#[from] TemplateError),
#[error("missing child object {0}")]
Expand All @@ -58,8 +59,7 @@ pub enum VMError {
}

#[derive(Error, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum TemplateError {
#[error("object id {0} has a non conforming set of interfaces")]
NonConformingObject(ObjectID),
Expand All @@ -77,8 +77,7 @@ pub enum TemplateError {
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct LineError {
pub error: ICError,
pub line: u32,
Expand All @@ -93,8 +92,7 @@ impl Display for LineError {
impl StdError for LineError {}

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ParseError {
pub line: usize,
pub start: usize,
Expand Down Expand Up @@ -147,8 +145,7 @@ impl ParseError {
}

#[derive(Debug, Error, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum ICError {
#[error("error compiling code: {0}")]
ParseError(#[from] ParseError),
Expand Down
3 changes: 1 addition & 2 deletions ic10emu/src/grammar.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
errors::{ParseError},
errors::ParseError,
interpreter,
tokens::{SplitConsecutiveIndicesExt, SplitConsecutiveWithIndices},
vm::instructions::{
Expand All @@ -14,7 +14,6 @@ use stationeers_data::enums::{
script::{LogicBatchMethod, LogicReagentMode, LogicSlotType, LogicType},
};
use std::{fmt::Display, str::FromStr};
use strum::IntoEnumIterator;

pub fn parse(code: &str) -> Result<Vec<Line>, ParseError> {
code.lines()
Expand Down
252 changes: 164 additions & 88 deletions ic10emu/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ use crate::{
pub mod instructions;

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum ICState {
Start,
Running,
Expand All @@ -39,8 +38,7 @@ pub enum ICState {
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ICInfo {
pub instruction_pointer: u32,
pub registers: Vec<f64>,
Expand Down Expand Up @@ -74,8 +72,7 @@ impl Display for ICState {
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct Program {
pub instructions: Vec<Instruction>,
pub errors: Vec<ICError>,
Expand Down Expand Up @@ -214,87 +211,166 @@ pub fn i64_to_f64(i: i64) -> f64 {

#[cfg(test)]
mod tests {
use std::rc::Rc;

// static INIT: std::sync::Once = std::sync::Once::new();
//
// fn setup() {
// INIT.call_once(|| {
// let _ = color_eyre::install();
// })
// }
//
// #[test]
// fn batch_modes() -> color_eyre::Result<()> {
// setup();
// let mut vm = VM::new();
// let ic = vm.add_ic(None).unwrap();
// let ic_id = {
// let device = vm.devices.get(&ic).unwrap();
// let device_ref = device.borrow();
// device_ref.ic.unwrap()
// };
// let ic_chip = vm.circuit_holders.get(&ic_id).unwrap().borrow();
// vm.set_code(
// ic,
// r#"lb r0 HASH("ItemActiveVent") On Sum
// lb r1 HASH("ItemActiveVent") On Maximum
// lb r2 HASH("ItemActiveVent") On Minimum"#,
// )?;
// vm.step_ic(ic, false)?;
// let r0 = ic_chip.get_register(0, 0).unwrap();
// assert_eq!(r0, 0.0);
// vm.step_ic(ic, false)?;
// let r1 = ic_chip.get_register(0, 1).unwrap();
// assert_eq!(r1, f64::NEG_INFINITY);
// vm.step_ic(ic, false)?;
// let r2 = ic_chip.get_register(0, 2).unwrap();
// assert_eq!(r2, f64::INFINITY);
// Ok(())
// }
//
// #[test]
// fn stack() -> color_eyre::Result<()> {
// setup();
// let mut vm = VM::new();
// let ic = vm.add_ic(None).unwrap();
// let ic_id = {
// let device = vm.devices.get(&ic).unwrap();
// let device_ref = device.borrow();
// device_ref.ic.unwrap()
// };
// let ic_chip = vm.circuit_holders.get(&ic_id).unwrap().borrow();
// vm.set_code(
// ic,
// r#"push 100
// push 10
// pop r0
// push 1000
// peek r1
// poke 1 20
// pop r2
// "#,
// )?;
// vm.step_ic(ic, false)?;
// let stack0 = ic_chip.peek_addr(0.0)?;
// assert_eq!(stack0, 100.0);
// vm.step_ic(ic, false)?;
// let stack1 = ic_chip.peek_addr(1.0)?;
// assert_eq!(stack1, 10.0);
// vm.step_ic(ic, false)?;
// let r0 = ic_chip.get_register(0, 0).unwrap();
// assert_eq!(r0, 10.0);
// vm.step_ic(ic, false)?;
// let stack1 = ic_chip.peek_addr(1.0)?;
// assert_eq!(stack1, 1000.0);
// vm.step_ic(ic, false)?;
// let r1 = ic_chip.get_register(0, 1).unwrap();
// assert_eq!(r1, 1000.0);
// vm.step_ic(ic, false)?;
// let stack1 = ic_chip.peek_addr(1.0)?;
// assert_eq!(stack1, 20.0);
// vm.step_ic(ic, false)?;
// let r2 = ic_chip.get_register(0, 2).unwrap();
// assert_eq!(r2, 20.0);
// Ok(())
// }
use stationeers_data::enums::prefabs::StationpediaPrefab;

use crate::vm::{
object::{
templates::{FrozenObject, ObjectInfo, Prefab},
ObjectID,
},
VM,
};

static INIT: std::sync::Once = std::sync::Once::new();

fn setup() -> color_eyre::Result<(Rc<VM>, ObjectID, ObjectID)> {
INIT.call_once(|| {
let _ = color_eyre::install();
});

println!("building VM");
let vm = VM::new();

println!("VM built");

let frozen_ic = FrozenObject {
obj_info: ObjectInfo::with_prefab(Prefab::Hash(
StationpediaPrefab::ItemIntegratedCircuit10 as i32,
)),
database_template: true,
template: None,
};

println!("Adding IC");
let ic = vm.add_object_from_frozen(frozen_ic)?;
let frozen_circuit_holder = FrozenObject {
obj_info: ObjectInfo::with_prefab(Prefab::Hash(
StationpediaPrefab::StructureCircuitHousing as i32,
)),
database_template: true,
template: None,
};
println!("Adding circuit holder");
let ch = vm.add_object_from_frozen(frozen_circuit_holder)?;
println!("socketing ic into circuit holder");
vm.set_slot_occupant(ch, 0, Some(ic), 1)?;
Ok((vm, ch, ic))
}

#[test]
fn batch_modes() -> color_eyre::Result<()> {
let (vm, ch, ic) = setup()?;
eprintln!("Beginning batch mode test");
let ic_chip = vm.get_object(ic).unwrap();
let circuit_holder = vm.get_object(ch).unwrap();
eprintln!("IC Chip: {ic_chip:?}");
eprintln!("Circuit Holder: {circuit_holder:?}");
vm.set_code(
ic,
r#"lb r0 HASH("ItemActiveVent") On Sum
lb r1 HASH("ItemActiveVent") On Maximum
lb r2 HASH("ItemActiveVent") On Minimum"#,
)?;
vm.step_programmable(ic, false)?;
let r0 = ic_chip
.borrow()
.as_integrated_circuit()
.unwrap()
.get_register(0, 0)
.unwrap();
assert_eq!(r0, 0.0);
vm.step_programmable(ic, false)?;
let r1 = ic_chip
.borrow()
.as_integrated_circuit()
.unwrap()
.get_register(0, 1)
.unwrap();
assert_eq!(r1, f64::NEG_INFINITY);
vm.step_programmable(ic, false)?;
let r2 = ic_chip
.borrow()
.as_integrated_circuit()
.unwrap()
.get_register(0, 2)
.unwrap();
assert_eq!(r2, f64::INFINITY);
Ok(())
}

#[test]
fn stack() -> color_eyre::Result<()> {
let (vm, ch, ic) = setup()?;
eprintln!("Beginning stack test");
let ic_chip = vm.get_object(ic).unwrap();
let circuit_holder = vm.get_object(ch).unwrap();
eprintln!("IC Chip: {ic_chip:?}");
eprintln!("Circuit Holder: {circuit_holder:?}");
vm.set_code(
ic,
r#"push 100
push 10
pop r0
push 1000
peek r1
poke 1 20
pop r2
"#,
)?;
vm.step_programmable(ic, false)?;
let stack0 = ic_chip
.borrow()
.as_integrated_circuit()
.unwrap()
.get_stack(0.0)?;
assert_eq!(stack0, 100.0);
vm.step_programmable(ic, false)?;
let stack1 = ic_chip
.borrow()
.as_integrated_circuit()
.unwrap()
.get_stack(1.0)?;
assert_eq!(stack1, 10.0);
vm.step_programmable(ic, false)?;
let r0 = ic_chip
.borrow()
.as_integrated_circuit()
.unwrap()
.get_register(0, 0)
.unwrap();
assert_eq!(r0, 10.0);
vm.step_programmable(ic, false)?;
let stack1 = ic_chip
.borrow()
.as_integrated_circuit()
.unwrap()
.get_stack(1.0)?;
assert_eq!(stack1, 1000.0);
vm.step_programmable(ch, false)?;
let r1 = ic_chip
.borrow()
.as_integrated_circuit()
.unwrap()
.get_register(0, 1)
.unwrap();
assert_eq!(r1, 1000.0);
vm.step_programmable(ch, false)?;
let stack1 = ic_chip
.borrow()
.as_integrated_circuit()
.unwrap()
.get_stack(1.0)?;
assert_eq!(stack1, 20.0);
vm.step_programmable(ch, false)?;
let r2 = ic_chip
.borrow()
.as_integrated_circuit()
.unwrap()
.get_register(0, 2)
.unwrap();
assert_eq!(r2, 20.0);
Ok(())
}
}
Loading

0 comments on commit 1843bdb

Please sign in to comment.