diff --git a/Cargo.lock b/Cargo.lock index 8339f26..874cafc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,6 +588,7 @@ dependencies = [ "serde", "serde-wasm-bindgen 0.6.5", "serde_with", + "strum", "thiserror", "tsify", "wasm-bindgen", diff --git a/ic10emu/src/lib.rs b/ic10emu/src/lib.rs index fce9408..11e9cd9 100644 --- a/ic10emu/src/lib.rs +++ b/ic10emu/src/lib.rs @@ -449,8 +449,8 @@ impl Network { #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct Prefab { - name: String, - hash: i32, + pub name: String, + pub hash: i32, } impl Prefab { @@ -481,7 +481,7 @@ pub struct DeviceTemplate { pub name: Option, pub prefab_name: Option, pub slots: Vec, - pub reagents: HashMap>, + // pub reagents: HashMap>, pub connections: Vec, pub fields: HashMap, } @@ -1089,7 +1089,8 @@ impl VM { name_hash, prefab: template.prefab_name.map(|name| Prefab::new(&name)), slots, - reagents: template.reagents, + // reagents: template.reagents, + reagents: HashMap::new(), ic, connections: template.connections, fields, diff --git a/ic10emu_wasm/Cargo.toml b/ic10emu_wasm/Cargo.toml index 64ad699..05c907d 100644 --- a/ic10emu_wasm/Cargo.toml +++ b/ic10emu_wasm/Cargo.toml @@ -21,6 +21,11 @@ serde_with = "3.7.0" tsify = { version = "0.4.5", default-features = false, features = ["js", "wasm-bindgen"] } thiserror = "1.0.58" +[build-dependencies] +ic10emu = { path = "../ic10emu" } +strum = { version = "0.26.2"} +itertools = "0.12.1" + [features] default = ["console_error_panic_hook"] console_error_panic_hook = ["dep:console_error_panic_hook"] diff --git a/ic10emu_wasm/build.rs b/ic10emu_wasm/build.rs index 55a368f..7a1ea33 100644 --- a/ic10emu_wasm/build.rs +++ b/ic10emu_wasm/build.rs @@ -5,18 +5,59 @@ use std::{ path::Path, }; +use itertools::Itertools; +use strum::IntoEnumIterator; + fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("ts_types.rs"); let output_file = File::create(dest_path).unwrap(); let mut writer = BufWriter::new(&output_file); + let mut ts_types: String = String::new(); + + let lt_tsunion: String = Itertools::intersperse( + ic10emu::grammar::generated::LogicType::iter().map(|lt| format!("\"{}\"", lt.as_ref())), + "\n | ".to_owned(), + ) + .collect(); + let lt_tstype = format!("\nexport type LogicType = {};", lt_tsunion); + ts_types.push_str(<_tstype); + + let slt_tsunion: String = Itertools::intersperse( + ic10emu::grammar::generated::SlotLogicType::iter().map(|slt| format!("\"{}\"", slt.as_ref())), + "\n | ".to_owned(), + ) + .collect(); + let slt_tstype = format!("\nexport type SlotLogicType = {};", slt_tsunion); + ts_types.push_str(&slt_tstype); + + let bm_tsunion: String = Itertools::intersperse( + ic10emu::grammar::generated::BatchMode::iter().map(|bm| format!("\"{}\"", bm.as_ref())), + "\n | ".to_owned(), + ) + .collect(); + let bm_tstype = format!("\nexport type BatchMode = {};", bm_tsunion); + ts_types.push_str(&bm_tstype); + + let rm_tsunion: String = Itertools::intersperse( + ic10emu::grammar::generated::ReagentMode::iter().map(|rm| format!("\"{}\"", rm.as_ref())), + "\n | ".to_owned(), + ) + .collect(); + let rm_tstype = format!("\nexport type ReagentMode = {};", rm_tsunion); + ts_types.push_str(&rm_tstype); + let infile = Path::new("src/types.ts"); let contents = fs::read_to_string(infile).unwrap(); + + ts_types.push('\n'); + ts_types.push_str(&contents); + write!( &mut writer, "#[wasm_bindgen(typescript_custom_section)]\n\ - const TYPES: &'static str = r#\"{contents}\"#; + const TYPES: &'static str = r#\"{ts_types}\"#; " ) .unwrap(); diff --git a/ic10emu_wasm/src/lib.rs b/ic10emu_wasm/src/lib.rs index 2cd1d7b..f554509 100644 --- a/ic10emu_wasm/src/lib.rs +++ b/ic10emu_wasm/src/lib.rs @@ -2,7 +2,7 @@ mod utils; mod types; -use ic10emu::grammar::{LogicType, SlotLogicType}; +use ic10emu::{grammar::{LogicType, SlotLogicType}, DeviceTemplate}; use serde::{Deserialize, Serialize}; use types::{Registers, Stack}; @@ -55,18 +55,18 @@ impl DeviceRef { } #[wasm_bindgen(getter, js_name = "nameHash")] - pub fn name_hash(&self) -> Option { + pub fn name_hash(&self) -> Option { self.device.borrow().name_hash } #[wasm_bindgen(getter, js_name = "prefabName")] pub fn prefab_name(&self) -> Option { - self.device.borrow().prefab_name.clone() + self.device.borrow().prefab.as_ref().map(|prefab| prefab.name.clone()) } #[wasm_bindgen(getter, js_name = "prefabHash")] pub fn prefab_hash(&self) -> Option { - self.device.borrow().prefab_hash + self.device.borrow().prefab.as_ref().map(|prefab| prefab.hash) } #[wasm_bindgen(getter, skip_typescript)] @@ -351,6 +351,12 @@ impl VM { Ok(self.vm.borrow_mut().add_device(network)?) } + #[wasm_bindgen(js_name = "addDeviceFromTemplate", skip_typescript)] + pub fn add_device_from_template(&self, template: JsValue) -> Result { + let template: DeviceTemplate = serde_wasm_bindgen::from_value(template)?; + Ok(self.vm.borrow_mut().add_device_from_template(template)?) + } + #[wasm_bindgen(js_name = "getDevice")] pub fn get_device(&self, id: u32) -> Option { let device = self.vm.borrow().get_device(id); diff --git a/ic10emu_wasm/src/types.ts b/ic10emu_wasm/src/types.ts index a38f3ce..8c97f7f 100644 --- a/ic10emu_wasm/src/types.ts +++ b/ic10emu_wasm/src/types.ts @@ -4,7 +4,8 @@ export interface LogicField { field_type: FieldType; value: number; } -export type Fields = Map; +export type LogicFields = Map; +export type SlotLogicFields = Map; export type SlotType = | "AccessCard" @@ -46,12 +47,12 @@ export interface SlotOccupant { readonly quantity: number; readonly max_quantity: number; readonly damage: number; - readonly fields: Fields; + readonly fields: SlotLogicFields; } export interface Slot { readonly typ: SlotType; readonly occupant: SlotOccupant | undefined; - readonly fields: Fields; + readonly fields: SlotLogicFields; } export type Reagents = Map>; @@ -80,10 +81,10 @@ export type DeviceSpec = { }; readonly connection: number | undefined; }; -export type LogicType = { readonly LogicType: string }; -export type SlotLogicType = { readonly SlotLogicType: string }; -export type BatchMode = { readonly BatchMode: string }; -export type ReagentMode = { readonly ReagentMode: string }; +export type OperandLogicType = { readonly LogicType: string }; +export type OperandSlotLogicType = { readonly SlotLogicType: string }; +export type OperandBatchMode = { readonly BatchMode: string }; +export type OperandReagentMode = { readonly ReagentMode: string }; export type Identifier = { readonly Identifier: { name: string } }; export type NumberFloat = { readonly Float: number }; @@ -106,10 +107,10 @@ export type Operand = | RegisterSpec | DeviceSpec | NumberOperand - | LogicType - | SlotLogicType - | BatchMode - | ReagentMode + | OperandLogicType + | OperandSlotLogicType + | OperandBatchMode + | OperandReagentMode | Identifier; export type Alias = RegisterSpec | DeviceSpec; @@ -141,7 +142,7 @@ export interface Program { } export interface DeviceRef { - readonly fields: Fields; + readonly fields: LogicFields; readonly slots: Slot[]; readonly reagents: Reagents; readonly connections: Connection[]; @@ -149,5 +150,29 @@ export interface DeviceRef { readonly defines?: Defines | undefined; readonly pins?: Pins; readonly program?: Program; - getSlotFields(slot: number): Fields; + getSlotFields(slot: number): SlotLogicFields; +} + +export interface SlotOccupantTemplate { + id?: number; + fields: { [key in SlotLogicType]?: LogicField }; +} + +export interface SlotTemplate { + typ: SlotType; + occupant?: SlotOccupantTemplate; +} + +export interface DeviceTemplate { + id?: number; + name?: string; + prefab_name?: string; + slots: SlotTemplate[]; + // reagents: { [key: string]: float} + connections: Connection[]; + fields: { [key in LogicType]?: LogicField }; +} + +export interface VM { + addDeviceFromTemplate(template: DeviceTemplate): number } diff --git a/www/src/ts/editor/index.ts b/www/src/ts/editor/index.ts index d3ceda7..4cdf718 100644 --- a/www/src/ts/editor/index.ts +++ b/www/src/ts/editor/index.ts @@ -44,7 +44,7 @@ export class IC10Editor extends BaseElement { }; sessions: Map; - @state() active_session: number = 0; + @state() active_session: number = 1; active_line_markers: Map = new Map(); languageProvider?: LanguageProvider; diff --git a/www/src/ts/session.ts b/www/src/ts/session.ts index 97e0706..ebb7556 100644 --- a/www/src/ts/session.ts +++ b/www/src/ts/session.ts @@ -74,7 +74,7 @@ export class Session extends EventTarget { this._programs = new Map(); this._errors = new Map(); this._save_timeout = undefined; - this._activeIC = 0; + this._activeIC = 1; this._activeLines = new Map(); this.loadFromFragment(); @@ -201,7 +201,7 @@ export class Session extends EventTarget { async loadFromFragment() { const fragment = window.location.hash.slice(1); if (fragment === "demo") { - this._programs = new Map([[0, demoCode]]); + this._programs = new Map([[1, demoCode]]); this._fireOnLoad(); return; } @@ -213,7 +213,7 @@ export class Session extends EventTarget { const data = getJson(txt); if (data === null) { // backwards compatible - this._programs = new Map([[0, txt]]); + this._programs = new Map([[1, txt]]); this, this._fireOnLoad(); return; } diff --git a/www/src/ts/virtual_machine/base_device.ts b/www/src/ts/virtual_machine/base_device.ts index 6cd1dbc..af2be7f 100644 --- a/www/src/ts/virtual_machine/base_device.ts +++ b/www/src/ts/virtual_machine/base_device.ts @@ -2,7 +2,7 @@ import { property, state } from "lit/decorators.js"; import { DeviceRef, - Fields, + LogicFields, Reagents, Slot, Connection, @@ -25,7 +25,7 @@ export declare class VMDeviceMixinInterface { name: string | null; nameHash: number | null; prefabName: string | null; - fields: Fields; + fields: LogicFields; slots: Slot[]; reagents: Reagents; connections: Connection[]; @@ -53,7 +53,7 @@ export const VMDeviceMixin = >( @state() accessor name: string | null = null; @state() accessor nameHash: number | null = null; @state() accessor prefabName: string | null; - @state() accessor fields: Fields; + @state() accessor fields: LogicFields; @state() accessor slots: Slot[]; @state() accessor reagents: Reagents; @state() accessor connections: Connection[];