refactor(frontend): fix loading code to editor

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
Rachel Powers
2024-08-26 20:11:42 -07:00
parent f40437e94e
commit c81080659e
16 changed files with 599 additions and 403 deletions

View File

@@ -60,6 +60,7 @@
"brne",
"brnez",
"Circuitboard",
"Clrd",
"codegen",
"conv",
"cstyle",
@@ -110,6 +111,7 @@
"reagentmodes",
"repr",
"retval",
"Rmap",
"rocketstation",
"rparen",
"sapz",
@@ -143,6 +145,7 @@
"trunc",
"Tsify",
"uneval",
"whos"
"whos",
"xtask"
]
}

View File

@@ -2700,3 +2700,15 @@ impl<T: IC10Marker> LabelInstruction for T {
Ok(())
}
}
impl<T: IC10Marker> RmapInstruction for T {
///rmap r? d? reagentHash(r?|num)
fn execute_inner(
&mut self,
r: &crate::vm::instructions::operands::InstOperand,
d: &crate::vm::instructions::operands::InstOperand,
reagent_hash: &crate::vm::instructions::operands::InstOperand,
) -> Result<(), crate::errors::ICError> {
todo!()
}
}

View File

@@ -36,6 +36,7 @@ use wasm_bindgen::prelude::*;
#[strum(use_phf, serialize_all = "lowercase")]
#[serde(rename_all = "lowercase")]
pub enum InstructionOp {
#[strum(props(example = "", desc = "No Operation", operands = "0"))]
Nop,
#[strum(
props(
@@ -837,6 +838,14 @@ pub enum InstructionOp {
)
)]
Rand,
#[strum(
props(
example = "rmap r? d? reagentHash(r?|num)",
desc = "Given a reagent hash, store the corresponding prefab hash that the device expects to fulfill the reagent requirement. For example, on an autolathe, the hash for Iron will store the hash for ItemIronIngot.",
operands = "3"
)
)]
Rmap,
#[strum(
props(
example = "round r? a(r?|num)",
@@ -1459,6 +1468,9 @@ impl InstructionOp {
ic.execute_putd(&operands[0usize], &operands[1usize], &operands[2usize])
}
Self::Rand => ic.execute_rand(&operands[0usize]),
Self::Rmap => {
ic.execute_rmap(&operands[0usize], &operands[1usize], &operands[2usize])
}
Self::Round => ic.execute_round(&operands[0usize], &operands[1usize]),
Self::S => {
ic.execute_s(&operands[0usize], &operands[1usize], &operands[2usize])

View File

@@ -3291,6 +3291,41 @@ pub trait RandInstruction: IntegratedCircuit {
r: &crate::vm::instructions::operands::InstOperand,
) -> Result<(), crate::errors::ICError>;
}
pub trait RmapInstruction: IntegratedCircuit {
///rmap r? d? reagentHash(r?|num)
fn execute_rmap(
&mut self,
r: &crate::vm::instructions::operands::Operand,
d: &crate::vm::instructions::operands::Operand,
reagent_hash: &crate::vm::instructions::operands::Operand,
) -> Result<(), crate::errors::ICError> {
RmapInstruction::execute_inner(
self,
&crate::vm::instructions::operands::InstOperand::new(
r,
InstructionOp::Rmap,
0usize,
),
&crate::vm::instructions::operands::InstOperand::new(
d,
InstructionOp::Rmap,
1usize,
),
&crate::vm::instructions::operands::InstOperand::new(
reagent_hash,
InstructionOp::Rmap,
2usize,
),
)
}
///rmap r? d? reagentHash(r?|num)
fn execute_inner(
&mut self,
r: &crate::vm::instructions::operands::InstOperand,
d: &crate::vm::instructions::operands::InstOperand,
reagent_hash: &crate::vm::instructions::operands::InstOperand,
) -> Result<(), crate::errors::ICError>;
}
pub trait RoundInstruction: IntegratedCircuit {
///round r? a(r?|num)
fn execute_round(
@@ -4587,7 +4622,7 @@ pub trait YieldInstruction: IntegratedCircuit {
///yield
fn execute_inner(&mut self) -> Result<(), crate::errors::ICError>;
}
pub trait ICInstructable: AbsInstruction + AcosInstruction + AddInstruction + AliasInstruction + AndInstruction + AsinInstruction + AtanInstruction + Atan2Instruction + BapInstruction + BapalInstruction + BapzInstruction + BapzalInstruction + BdnsInstruction + BdnsalInstruction + BdseInstruction + BdsealInstruction + BeqInstruction + BeqalInstruction + BeqzInstruction + BeqzalInstruction + BgeInstruction + BgealInstruction + BgezInstruction + BgezalInstruction + BgtInstruction + BgtalInstruction + BgtzInstruction + BgtzalInstruction + BleInstruction + BlealInstruction + BlezInstruction + BlezalInstruction + BltInstruction + BltalInstruction + BltzInstruction + BltzalInstruction + BnaInstruction + BnaalInstruction + BnanInstruction + BnazInstruction + BnazalInstruction + BneInstruction + BnealInstruction + BnezInstruction + BnezalInstruction + BrapInstruction + BrapzInstruction + BrdnsInstruction + BrdseInstruction + BreqInstruction + BreqzInstruction + BrgeInstruction + BrgezInstruction + BrgtInstruction + BrgtzInstruction + BrleInstruction + BrlezInstruction + BrltInstruction + BrltzInstruction + BrnaInstruction + BrnanInstruction + BrnazInstruction + BrneInstruction + BrnezInstruction + CeilInstruction + ClrInstruction + ClrdInstruction + CosInstruction + DefineInstruction + DivInstruction + ExpInstruction + FloorInstruction + GetInstruction + GetdInstruction + HcfInstruction + JInstruction + JalInstruction + JrInstruction + LInstruction + LabelInstruction + LbInstruction + LbnInstruction + LbnsInstruction + LbsInstruction + LdInstruction + LogInstruction + LrInstruction + LsInstruction + MaxInstruction + MinInstruction + ModInstruction + MoveInstruction + MulInstruction + NorInstruction + NotInstruction + OrInstruction + PeekInstruction + PokeInstruction + PopInstruction + PushInstruction + PutInstruction + PutdInstruction + RandInstruction + RoundInstruction + SInstruction + SapInstruction + SapzInstruction + SbInstruction + SbnInstruction + SbsInstruction + SdInstruction + SdnsInstruction + SdseInstruction + SelectInstruction + SeqInstruction + SeqzInstruction + SgeInstruction + SgezInstruction + SgtInstruction + SgtzInstruction + SinInstruction + SlaInstruction + SleInstruction + SleepInstruction + SlezInstruction + SllInstruction + SltInstruction + SltzInstruction + SnaInstruction + SnanInstruction + SnanzInstruction + SnazInstruction + SneInstruction + SnezInstruction + SqrtInstruction + SraInstruction + SrlInstruction + SsInstruction + SubInstruction + TanInstruction + TruncInstruction + XorInstruction + YieldInstruction {}
pub trait ICInstructable: AbsInstruction + AcosInstruction + AddInstruction + AliasInstruction + AndInstruction + AsinInstruction + AtanInstruction + Atan2Instruction + BapInstruction + BapalInstruction + BapzInstruction + BapzalInstruction + BdnsInstruction + BdnsalInstruction + BdseInstruction + BdsealInstruction + BeqInstruction + BeqalInstruction + BeqzInstruction + BeqzalInstruction + BgeInstruction + BgealInstruction + BgezInstruction + BgezalInstruction + BgtInstruction + BgtalInstruction + BgtzInstruction + BgtzalInstruction + BleInstruction + BlealInstruction + BlezInstruction + BlezalInstruction + BltInstruction + BltalInstruction + BltzInstruction + BltzalInstruction + BnaInstruction + BnaalInstruction + BnanInstruction + BnazInstruction + BnazalInstruction + BneInstruction + BnealInstruction + BnezInstruction + BnezalInstruction + BrapInstruction + BrapzInstruction + BrdnsInstruction + BrdseInstruction + BreqInstruction + BreqzInstruction + BrgeInstruction + BrgezInstruction + BrgtInstruction + BrgtzInstruction + BrleInstruction + BrlezInstruction + BrltInstruction + BrltzInstruction + BrnaInstruction + BrnanInstruction + BrnazInstruction + BrneInstruction + BrnezInstruction + CeilInstruction + ClrInstruction + ClrdInstruction + CosInstruction + DefineInstruction + DivInstruction + ExpInstruction + FloorInstruction + GetInstruction + GetdInstruction + HcfInstruction + JInstruction + JalInstruction + JrInstruction + LInstruction + LabelInstruction + LbInstruction + LbnInstruction + LbnsInstruction + LbsInstruction + LdInstruction + LogInstruction + LrInstruction + LsInstruction + MaxInstruction + MinInstruction + ModInstruction + MoveInstruction + MulInstruction + NorInstruction + NotInstruction + OrInstruction + PeekInstruction + PokeInstruction + PopInstruction + PushInstruction + PutInstruction + PutdInstruction + RandInstruction + RmapInstruction + RoundInstruction + SInstruction + SapInstruction + SapzInstruction + SbInstruction + SbnInstruction + SbsInstruction + SdInstruction + SdnsInstruction + SdseInstruction + SelectInstruction + SeqInstruction + SeqzInstruction + SgeInstruction + SgezInstruction + SgtInstruction + SgtzInstruction + SinInstruction + SlaInstruction + SleInstruction + SleepInstruction + SlezInstruction + SllInstruction + SltInstruction + SltzInstruction + SnaInstruction + SnanInstruction + SnanzInstruction + SnazInstruction + SneInstruction + SnezInstruction + SqrtInstruction + SraInstruction + SrlInstruction + SsInstruction + SubInstruction + TanInstruction + TruncInstruction + XorInstruction + YieldInstruction {}
impl<T> ICInstructable for T
where
T: AbsInstruction + AcosInstruction + AddInstruction + AliasInstruction
@@ -4615,15 +4650,15 @@ where
+ MinInstruction + ModInstruction + MoveInstruction + MulInstruction
+ NorInstruction + NotInstruction + OrInstruction + PeekInstruction
+ PokeInstruction + PopInstruction + PushInstruction + PutInstruction
+ PutdInstruction + RandInstruction + RoundInstruction + SInstruction
+ SapInstruction + SapzInstruction + SbInstruction + SbnInstruction
+ SbsInstruction + SdInstruction + SdnsInstruction + SdseInstruction
+ SelectInstruction + SeqInstruction + SeqzInstruction + SgeInstruction
+ SgezInstruction + SgtInstruction + SgtzInstruction + SinInstruction
+ SlaInstruction + SleInstruction + SleepInstruction + SlezInstruction
+ SllInstruction + SltInstruction + SltzInstruction + SnaInstruction
+ SnanInstruction + SnanzInstruction + SnazInstruction + SneInstruction
+ SnezInstruction + SqrtInstruction + SraInstruction + SrlInstruction
+ SsInstruction + SubInstruction + TanInstruction + TruncInstruction
+ XorInstruction + YieldInstruction,
+ PutdInstruction + RandInstruction + RmapInstruction + RoundInstruction
+ SInstruction + SapInstruction + SapzInstruction + SbInstruction
+ SbnInstruction + SbsInstruction + SdInstruction + SdnsInstruction
+ SdseInstruction + SelectInstruction + SeqInstruction + SeqzInstruction
+ SgeInstruction + SgezInstruction + SgtInstruction + SgtzInstruction
+ SinInstruction + SlaInstruction + SleInstruction + SleepInstruction
+ SlezInstruction + SllInstruction + SltInstruction + SltzInstruction
+ SnaInstruction + SnanInstruction + SnanzInstruction + SnazInstruction
+ SneInstruction + SnezInstruction + SqrtInstruction + SraInstruction
+ SrlInstruction + SsInstruction + SubInstruction + TanInstruction
+ TruncInstruction + XorInstruction + YieldInstruction,
{}

View File

@@ -52,8 +52,8 @@
"@lit/context": "^1.1.2",
"@popperjs/core": "^2.11.8",
"@shoelace-style/shoelace": "^2.16.0",
"ace-builds": "^1.35.4",
"ace-linters": "^1.2.3",
"ace-builds": "^1.36.0",
"ace-linters": "^1.3.0",
"bootstrap": "^5.3.3",
"bson": "^6.8.0",
"buffer": "^6.0.3",

21
www/pnpm-lock.yaml generated
View File

@@ -24,11 +24,11 @@ importers:
specifier: ^2.16.0
version: 2.16.0(@types/react@18.2.79)
ace-builds:
specifier: ^1.35.4
version: 1.35.4
specifier: ^1.36.0
version: 1.36.0
ace-linters:
specifier: ^1.2.3
version: 1.2.3
specifier: ^1.3.0
version: 1.3.0
bootstrap:
specifier: ^5.3.3
version: 5.3.3(@popperjs/core@2.11.8)
@@ -645,11 +645,11 @@ packages:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
ace-builds@1.35.4:
resolution: {integrity: sha512-r0KQclhZ/uk5a4zOqRYQkJuQuu4vFMiA6VTj54Tk4nI1TUR3iEMMppZkWbNoWEgWwv4ciDloObb9Rf4V55Qgjw==}
ace-builds@1.36.0:
resolution: {integrity: sha512-7to4F86V5N13EY4M9LWaGo2Wmr9iWe5CrYpc28F+/OyYCf7yd+xBV5x9v/GB73EBGGoYd89m6JjeIUjkL6Yw+w==}
ace-linters@1.2.3:
resolution: {integrity: sha512-kfr3WG3zeAQWYLX9NwD+2AYVShvkyfVGJQLuh/wX2qIh2f71bYaFL9h7wHWtl8hofkKgDsWqfsX2LjXjoCqv5g==}
ace-linters@1.3.0:
resolution: {integrity: sha512-dX34G8iVapk+nl2cnhY5ZTKfXLo0MbtvwILsr9PVHEW8/NubudJkEE5twkQ69vGOJp2dW2i5jrU417X8CpXSIA==}
acorn-import-attributes@1.9.5:
resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
@@ -3320,9 +3320,9 @@ snapshots:
mime-types: 2.1.35
negotiator: 0.6.3
ace-builds@1.35.4: {}
ace-builds@1.36.0: {}
ace-linters@1.2.3:
ace-linters@1.3.0:
dependencies:
'@xml-tools/ast': 5.0.5
'@xml-tools/constraints': 1.1.1
@@ -3337,6 +3337,7 @@ snapshots:
vscode-languageserver-protocol: 3.17.5
vscode-languageserver-textdocument: 1.0.12
vscode-languageserver-types: 3.17.5
vscode-uri: 3.0.8
vscode-ws-jsonrpc: 2.0.2
transitivePeerDependencies:
- encoding

View File

@@ -1,7 +1,7 @@
import { HTMLTemplateResult, html, css, CSSResultGroup } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import { html, css} from "lit";
import { customElement, query, state } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { SessionDB } from "session";
import { SessionDB } from "sessionDB";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
import { repeat } from "lit/directives/repeat.js";

View File

@@ -243,9 +243,9 @@ export class IC10Editor extends BaseElement {
app.session.onLoad((_e) => {
const session = app.session;
const updated_ids: number[] = [];
for (const [id, code] of session.programs.value) {
for (const [id, code] of session.programs) {
updated_ids.push(id);
that.createOrSetSession(id, code);
that.createOrSetSession(id, code.peek());
}
that.activateSession(that.activeSession);
for (const [id, _] of that.sessions) {

View File

@@ -0,0 +1,99 @@
import { SessionDB } from "../sessionDB";
export const defaultVMState: SessionDB.CurrentDBVmState = {
vm: {
objects: [
{
obj_info: {
id: 1,
prefab: "StructureCircuitHousing",
socketed_ic: 2,
slots: new Map([
[0, { id: 2, quantity: 1 }],
]),
connections: new Map([
[0, 1],
]),
// unused, provided to make compiler happy
name: undefined,
prefab_hash: undefined,
compile_errors: undefined,
parent_slot: undefined,
root_parent_human: undefined,
damage: undefined,
device_pins: undefined,
reagents: undefined,
logic_values: undefined,
slot_logic_values: undefined,
entity: undefined,
visible_devices: undefined,
memory: undefined,
source_code: undefined,
circuit: undefined,
},
template: undefined,
database_template: true,
},
{
obj_info: {
id: 2,
prefab: "ItemIntegratedCircuit10",
source_code: "",
memory: new Array(512).fill(0),
circuit: {
instruction_pointer: 0,
yield_instruction_count: 0,
state: "Start",
aliases: new Map(),
defines: new Map(),
labels: new Map(),
registers: new Array(18).fill(0),
},
// unused, provided to make compiler happy
name: undefined,
prefab_hash: undefined,
compile_errors: undefined,
slots: undefined,
parent_slot: undefined,
root_parent_human: undefined,
damage: undefined,
device_pins: undefined,
connections: undefined,
reagents: undefined,
logic_values: undefined,
slot_logic_values: undefined,
entity: undefined,
socketed_ic: undefined,
visible_devices: undefined,
},
template: undefined,
database_template: true,
},
],
networks: [
{
id: 1,
devices: [1],
power_only: [],
channels: Array(8).fill(NaN) as [
number,
number,
number,
number,
number,
number,
number,
number,
],
},
],
program_holders: [2],
circuit_holders: [1],
default_network_key: 1,
wireless_receivers: [],
wireless_transmitters: [],
},
activeIC: 1,
};

View File

@@ -1,5 +1,4 @@
import { ObjectInfo } from "ic10emu_wasm";
import { SessionDB } from "../session";
import { SessionDB } from "../sessionDB";
export const demoCode = `# Highlighting Demo

View File

@@ -1,3 +1,4 @@
import { defaultVMState } from "./default";
import { demoVMState } from "./demo";
export { demoVMState };
export { defaultVMState, demoVMState };

View File

@@ -1,31 +1,20 @@
import type {
ICError,
FrozenVM,
RegisterSpec,
DeviceSpec,
LogicType,
LogicSlotType,
LogicField,
Class as SlotType,
FrozenCableNetwork,
FrozenObject,
ObjectInfo,
ICState,
ObjectID,
} from "ic10emu_wasm";
import { App } from "./app";
import { openDB, DBSchema, IDBPTransaction, IDBPDatabase } from "idb";
import { openDB, IDBPTransaction } from "idb";
import {
TypedEventTarget,
crc32,
dispatchTypedEvent,
fromJson,
structuralEqual,
toJson,
} from "./utils";
import * as presets from "./presets";
import { batch, computed, effect, signal, Signal } from "@lit-labs/preact-signals";
import { computed, signal, Signal } from "@lit-labs/preact-signals";
import { SessionDB } from "sessionDB";
const { demoVMState } = presets;
export interface SessionEventMap {
@@ -38,7 +27,7 @@ export interface SessionEventMap {
}
export class Session extends TypedEventTarget<SessionEventMap>() {
private _programs: Signal<Map<ObjectID, string>>;
private _programs: Map<ObjectID, Signal<string>>;
private _errors: Signal<Map<ObjectID, ICError[]>>;
private _activeIC: Signal<ObjectID>;
private _activeLines: Signal<Map<ObjectID, number>>;
@@ -49,7 +38,7 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
constructor(app: App) {
super();
this.app = app;
this._programs = signal(new Map());
this._programs = new Map();
this._errors = signal(new Map());
this._save_timeout = undefined;
this._activeIC = signal(null);
@@ -60,16 +49,23 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
window.addEventListener("hashchange", (_event) => {
that.loadFromFragment();
});
this._programs.subscribe((_) => {this._fireOnLoad()});
}
get programs(): Signal<Map<number, string>> {
get programs(): Map<ObjectID, Signal<string>> {
return this._programs;
}
set programs(programs: Iterable<[number, string]>) {
this._programs.value = new Map(programs);
set programs(programs: Iterable<[ObjectID, string]>) {
const seenIds: ObjectID[] = []
for (const [id, code] of programs) {
this.setProgram(id, code);
seenIds.push(id);
}
for (const id of this._programs.keys()) {
if (!seenIds.includes(id)) {
this.setProgram(id, null);
}
}
}
get activeIC(): Signal<ObjectID> {
@@ -82,16 +78,14 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
}
changeID(oldID: ObjectID, newID: ObjectID) {
if (this.programs.peek().has(oldID)) {
const newVal = new Map(this.programs.value);
newVal.set(newID, newVal.get(oldID));
newVal.delete(oldID);
this.programs.value = newVal;
if (this._programs.has(oldID)) {
this._programs.set(newID, this._programs.get(oldID));
this._programs.delete(oldID);
}
this.dispatchCustomEvent("session-id-change", { old: oldID, new: newID });
}
onIDChange(callback: (e: CustomEvent<{ old: ObjectID; new: ObjectID}>) => any) {
onIDChange(callback: (e: CustomEvent<{ old: ObjectID; new: ObjectID }>) => any) {
this.addEventListener("session-id-change", callback);
}
@@ -110,21 +104,36 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
setActiveLine(id: ObjectID, line: number) {
const last = this._activeLines.peek().get(id);
if (last !== line) {
this._activeLines.value = new Map([ ... this._activeLines.value.entries(), [id, line]]);
this._activeLines.value = new Map([... this._activeLines.value.entries(), [id, line]]);
this._fireOnActiveLine(id);
}
}
setProgramCode(id: ObjectID, code: string) {
this._programs.value = new Map([ ...this._programs.value.entries(), [id, code]]);
this.setProgram(id, code);
if (this.app.vm) {
this.app.vm.updateCode();
}
this.save();
}
getProgram(id: ObjectID): Signal<string> {
if (!this._programs.has(id)) {
this._programs.set(id, signal(null));
}
return this._programs.get(id);
}
private setProgram(id: ObjectID, code: string) {
if (!this._programs.has(id)) {
this._programs.set(id, signal(code));
} else {
this._programs.get(id).value = code;
}
}
setProgramErrors(id: ObjectID, errors: ICError[]) {
this._errors.value = new Map([ ...this._errors.value.entries(), [id, errors]]);
this._errors.value = new Map([...this._errors.value.entries(), [id, errors]]);
this._fireOnErrors([id]);
}
@@ -223,6 +232,8 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
console.log("Bad session data:", data);
}
}
} else {
this.load(presets.defaultVMState);
}
}
@@ -312,333 +323,6 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
}
}
export namespace SessionDB {
export namespace V1 {
export interface VMState {
activeIC: number;
vm: FrozenVM;
}
export interface FrozenVM {
ics: FrozenIC[];
devices: DeviceTemplate[];
networks: FrozenNetwork[];
default_network: number;
}
export interface FrozenNetwork {
id: number;
devices: number[];
power_only: number[];
channels: number[];
}
export type RegisterSpec = {
readonly RegisterSpec: {
readonly indirection: number;
readonly target: number;
};
};
export type DeviceSpec = {
readonly DeviceSpec: {
readonly device:
| "Db"
| { readonly Numbered: number }
| {
readonly Indirect: {
readonly indirection: number;
readonly target: number;
};
};
readonly connection: number | undefined;
};
};
export type Alias = RegisterSpec | DeviceSpec;
export type Aliases = Map<string, Alias>;
export type Defines = Map<string, number>;
export type Pins = (number | undefined)[];
export interface SlotOccupantTemplate {
id?: number;
fields: { [key in LogicSlotType]?: LogicField };
}
export interface ConnectionCableNetwork {
CableNetwork: {
net: number | undefined;
typ: string;
};
}
export type Connection = ConnectionCableNetwork | "Other";
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 FrozenIC {
device: number;
id: number;
registers: number[];
ip: number;
ic: number;
stack: number[];
aliases: Aliases;
defines: Defines;
pins: Pins;
state: string;
code: string;
}
}
export namespace V2 {
export interface VMState {
activeIC: number;
vm: FrozenVM;
}
function objectFromIC(ic: SessionDB.V1.FrozenIC): FrozenObject {
return {
obj_info: {
name: undefined,
id: ic.id,
prefab: "ItemIntegratedCircuit10",
prefab_hash: crc32("ItemIntegratedCircuit10"),
memory: ic.stack,
source_code: ic.code,
compile_errors: undefined,
circuit: {
instruction_pointer: ic.ip,
yield_instruction_count: ic.ic,
state: ic.state as ICState,
aliases: ic.aliases,
defines: ic.defines,
labels: new Map(),
registers: ic.registers,
},
// unused
slots: undefined,
parent_slot: undefined,
root_parent_human: undefined,
damage: undefined,
device_pins: undefined,
connections: undefined,
reagents: undefined,
logic_values: undefined,
slot_logic_values: undefined,
entity: undefined,
socketed_ic: undefined,
visible_devices: undefined,
},
database_template: true,
template: undefined,
};
}
function objectsFromV1Template(
template: SessionDB.V1.DeviceTemplate,
idFn: () => number,
socketedIcFn: (id: number) => number | undefined,
): FrozenObject[] {
const slotOccupantsPairs = new Map(
template.slots.flatMap((slot, index) => {
if (typeof slot.occupant !== "undefined") {
return [
[
index,
[
{
obj_info: {
name: undefined,
id: slot.occupant.id ?? idFn(),
prefab: undefined,
prefab_hash: slot.occupant.fields.PrefabHash?.value,
damage: slot.occupant.fields.Damage?.value,
socketed_ic: undefined,
// unused
memory: undefined,
source_code: undefined,
compile_errors: undefined,
circuit: undefined,
slots: undefined,
device_pins: undefined,
connections: undefined,
reagents: undefined,
logic_values: undefined,
slot_logic_values: undefined,
entity: undefined,
visible_devices: undefined,
},
database_template: true,
template: undefined,
},
slot.occupant.fields.Quantity ?? 1,
],
],
] as [number, [FrozenObject, number]][];
} else {
return [] as [number, [FrozenObject, number]][];
}
}),
);
const frozen: FrozenObject = {
obj_info: {
name: template.name,
id: template.id,
prefab: template.prefab_name,
prefab_hash: undefined,
slots: new Map(
Array.from(slotOccupantsPairs.entries()).map(
([index, [obj, quantity]]) => [
index,
{
quantity,
id: obj.obj_info.id,
},
],
),
),
socketed_ic: socketedIcFn(template.id),
logic_values: new Map(
Object.entries(template.fields).map(([key, val]) => {
return [key as LogicType, val.value];
}),
),
// unused
memory: undefined,
source_code: undefined,
compile_errors: undefined,
circuit: undefined,
parent_slot: undefined,
root_parent_human: undefined,
damage: undefined,
device_pins: undefined,
connections: undefined,
reagents: undefined,
slot_logic_values: undefined,
entity: undefined,
visible_devices: undefined,
},
database_template: true,
template: undefined,
};
return [
...Array.from(slotOccupantsPairs.entries()).map(
([_index, [obj, _quantity]]) => obj,
),
frozen,
];
}
export function fromV1State(v1State: SessionDB.V1.VMState): VMState {
const highestObjetId = Math.max(
...v1State.vm.devices
.map((device) => device.id ?? -1)
.concat(v1State.vm.ics.map((ic) => ic.id ?? -1)),
);
let nextId = highestObjetId + 1;
const deviceIcs = new Map(
v1State.vm.ics.map((ic) => [ic.device, objectFromIC(ic)]),
);
const objects = v1State.vm.devices.flatMap((device) => {
return objectsFromV1Template(
device,
() => nextId++,
(id) => deviceIcs.get(id)?.obj_info.id ?? undefined,
);
});
const vm: FrozenVM = {
objects,
circuit_holders: objects.flatMap((obj) =>
"socketed_ic" in obj.obj_info &&
typeof obj.obj_info.socketed_ic !== "undefined"
? [obj.obj_info.id]
: [],
),
program_holders: objects.flatMap((obj) =>
"source_code" in obj.obj_info &&
typeof obj.obj_info.source_code !== "undefined"
? [obj.obj_info.id]
: [],
),
default_network_key: v1State.vm.default_network,
networks: v1State.vm.networks as FrozenCableNetwork[],
wireless_receivers: [],
wireless_transmitters: [],
};
const v2State: VMState = {
activeIC: v1State.activeIC,
vm,
};
return v2State;
}
}
export enum DBVersion {
V1 = 1,
V2 = 2,
}
export const LOCAL_DB_VERSION = DBVersion.V2 as const;
export type CurrentDBSchema = AppDBSchemaV2;
export type CurrentDBVmState = V2.VMState;
export const LOCAL_DB_SESSION_STORE = "sessionsV2" as const;
export interface AppDBSchemaV1 extends DBSchema {
sessions: {
key: string;
value: {
name: string;
date: Date;
session: V1.VMState;
};
indexes: {
"by-date": Date;
"by-name": string;
};
};
}
export interface AppDBSchemaV2 extends DBSchema {
sessions: {
key: string;
value: {
name: string;
date: Date;
session: V1.VMState;
};
indexes: {
"by-date": Date;
"by-name": string;
};
};
sessionsV2: {
key: string;
value: {
name: string;
date: Date;
version: DBVersion.V2;
session: V2.VMState;
};
indexes: {
"by-date": Date;
"by-name": string;
};
};
}
}
export interface OldPrograms {
programs: [number, string][];

345
www/src/ts/sessionDB.ts Normal file
View File

@@ -0,0 +1,345 @@
import type {
ICError,
FrozenVM,
RegisterSpec,
DeviceSpec,
LogicType,
LogicSlotType,
LogicField,
Class as SlotType,
FrozenCableNetwork,
FrozenObject,
ObjectInfo,
ICState,
ObjectID,
} from "ic10emu_wasm";
import { DBSchema } from "idb";
import { crc32 } from "utils";
export namespace SessionDB {
export namespace V1 {
export interface VMState {
activeIC: number;
vm: FrozenVM;
}
export interface FrozenVM {
ics: FrozenIC[];
devices: DeviceTemplate[];
networks: FrozenNetwork[];
default_network: number;
}
export interface FrozenNetwork {
id: number;
devices: number[];
power_only: number[];
channels: number[];
}
export type RegisterSpec = {
readonly RegisterSpec: {
readonly indirection: number;
readonly target: number;
};
};
export type DeviceSpec = {
readonly DeviceSpec: {
readonly device:
| "Db"
| { readonly Numbered: number }
| {
readonly Indirect: {
readonly indirection: number;
readonly target: number;
};
};
readonly connection: number | undefined;
};
};
export type Alias = RegisterSpec | DeviceSpec;
export type Aliases = Map<string, Alias>;
export type Defines = Map<string, number>;
export type Pins = (number | undefined)[];
export interface SlotOccupantTemplate {
id?: number;
fields: { [key in LogicSlotType]?: LogicField };
}
export interface ConnectionCableNetwork {
CableNetwork: {
net: number | undefined;
typ: string;
};
}
export type Connection = ConnectionCableNetwork | "Other";
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 FrozenIC {
device: number;
id: number;
registers: number[];
ip: number;
ic: number;
stack: number[];
aliases: Aliases;
defines: Defines;
pins: Pins;
state: string;
code: string;
}
}
export namespace V2 {
export interface VMState {
activeIC: number;
vm: FrozenVM;
}
function objectFromIC(ic: SessionDB.V1.FrozenIC): FrozenObject {
return {
obj_info: {
name: undefined,
id: ic.id,
prefab: "ItemIntegratedCircuit10",
prefab_hash: crc32("ItemIntegratedCircuit10"),
memory: ic.stack,
source_code: ic.code,
compile_errors: undefined,
circuit: {
instruction_pointer: ic.ip,
yield_instruction_count: ic.ic,
state: ic.state as ICState,
aliases: ic.aliases,
defines: ic.defines,
labels: new Map(),
registers: ic.registers,
},
// unused
slots: undefined,
parent_slot: undefined,
root_parent_human: undefined,
damage: undefined,
device_pins: undefined,
connections: undefined,
reagents: undefined,
logic_values: undefined,
slot_logic_values: undefined,
entity: undefined,
socketed_ic: undefined,
visible_devices: undefined,
},
database_template: true,
template: undefined,
};
}
function objectsFromV1Template(
template: SessionDB.V1.DeviceTemplate,
idFn: () => number,
socketedIcFn: (id: number) => number | undefined,
): FrozenObject[] {
const slotOccupantsPairs = new Map(
template.slots.flatMap((slot, index) => {
if (typeof slot.occupant !== "undefined") {
return [
[
index,
[
{
obj_info: {
name: undefined,
id: slot.occupant.id ?? idFn(),
prefab: undefined,
prefab_hash: slot.occupant.fields.PrefabHash?.value,
damage: slot.occupant.fields.Damage?.value,
socketed_ic: undefined,
// unused
memory: undefined,
source_code: undefined,
compile_errors: undefined,
circuit: undefined,
slots: undefined,
device_pins: undefined,
connections: undefined,
reagents: undefined,
logic_values: undefined,
slot_logic_values: undefined,
entity: undefined,
visible_devices: undefined,
},
database_template: true,
template: undefined,
},
slot.occupant.fields.Quantity ?? 1,
],
],
] as [number, [FrozenObject, number]][];
} else {
return [] as [number, [FrozenObject, number]][];
}
}),
);
const frozen: FrozenObject = {
obj_info: {
name: template.name,
id: template.id,
prefab: template.prefab_name,
prefab_hash: undefined,
slots: new Map(
Array.from(slotOccupantsPairs.entries()).map(
([index, [obj, quantity]]) => [
index,
{
quantity,
id: obj.obj_info.id,
},
],
),
),
socketed_ic: socketedIcFn(template.id),
logic_values: new Map(
Object.entries(template.fields).map(([key, val]) => {
return [key as LogicType, val.value];
}),
),
// unused
memory: undefined,
source_code: undefined,
compile_errors: undefined,
circuit: undefined,
parent_slot: undefined,
root_parent_human: undefined,
damage: undefined,
device_pins: undefined,
connections: undefined,
reagents: undefined,
slot_logic_values: undefined,
entity: undefined,
visible_devices: undefined,
},
database_template: true,
template: undefined,
};
return [
...Array.from(slotOccupantsPairs.entries()).map(
([_index, [obj, _quantity]]) => obj,
),
frozen,
];
}
export function fromV1State(v1State: SessionDB.V1.VMState): VMState {
const highestObjetId = Math.max(
...v1State.vm.devices
.map((device) => device.id ?? -1)
.concat(v1State.vm.ics.map((ic) => ic.id ?? -1)),
);
let nextId = highestObjetId + 1;
const deviceIcs = new Map(
v1State.vm.ics.map((ic) => [ic.device, objectFromIC(ic)]),
);
const objects = v1State.vm.devices.flatMap((device) => {
return objectsFromV1Template(
device,
() => nextId++,
(id) => deviceIcs.get(id)?.obj_info.id ?? undefined,
);
});
const vm: FrozenVM = {
objects,
circuit_holders: objects.flatMap((obj) =>
"socketed_ic" in obj.obj_info &&
typeof obj.obj_info.socketed_ic !== "undefined"
? [obj.obj_info.id]
: [],
),
program_holders: objects.flatMap((obj) =>
"source_code" in obj.obj_info &&
typeof obj.obj_info.source_code !== "undefined"
? [obj.obj_info.id]
: [],
),
default_network_key: v1State.vm.default_network,
networks: v1State.vm.networks as FrozenCableNetwork[],
wireless_receivers: [],
wireless_transmitters: [],
};
const v2State: VMState = {
activeIC: v1State.activeIC,
vm,
};
return v2State;
}
}
export enum DBVersion {
V1 = 1,
V2 = 2,
}
export const LOCAL_DB_VERSION = DBVersion.V2 as const;
export type CurrentDBSchema = AppDBSchemaV2;
export type CurrentDBVmState = V2.VMState;
export const LOCAL_DB_SESSION_STORE = "sessionsV2" as const;
export interface AppDBSchemaV1 extends DBSchema {
sessions: {
key: string;
value: {
name: string;
date: Date;
session: V1.VMState;
};
indexes: {
"by-date": Date;
"by-name": string;
};
};
}
export interface AppDBSchemaV2 extends DBSchema {
sessions: {
key: string;
value: {
name: string;
date: Date;
session: V1.VMState;
};
indexes: {
"by-date": Date;
"by-name": string;
};
};
sessionsV2: {
key: string;
value: {
name: string;
date: Date;
version: DBVersion.V2;
session: V2.VMState;
};
indexes: {
"by-date": Date;
"by-name": string;
};
};
}
}

View File

@@ -23,15 +23,10 @@ export interface ToastMessage {
id: string;
}
import {
signal,
computed,
effect,
batch,
} from '@lit-labs/preact-signals';
import type { Signal } from '@lit-labs/preact-signals';
import { getJsonContext } from "./jsonErrorUtils";
import { VMState } from "./state";
import { Obj } from "@popperjs/core";
export interface VirtualMachineEventMap {
"vm-template-db-loaded": CustomEvent<TemplateDatabase>;
@@ -94,19 +89,19 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
}
async updateCode() {
const progs = this.app.session.programs.peek();
const progs = this.app.session.programs;
for (const id of progs.keys()) {
const attempt = Date.now().toString(16);
const circuitHolder = this.state.getObject(id);
const prog = progs.get(id);
const vmProg = this.state.getObjectProgramSource(id).peek();
const prog = progs.get(id).peek();
if (
circuitHolder &&
vmProg &&
prog &&
circuitHolder.peek().obj_info.source_code !== prog
vmProg !== prog
) {
try {
console.time(`CompileProgram_${id}_${attempt}`);
await this.ic10vm.setCodeInvalid(id, progs.get(id)!);
await this.ic10vm.setCodeInvalid(id, prog);
const errors = await this.ic10vm.getCompileErrors(id);
this.app.session.setProgramErrors(id, errors);
this.dispatchCustomEvent("vm-object-modified", id);

View File

@@ -593,7 +593,16 @@ export class VMState {
const key = `obj:${id},source`;
if (!this.signalCacheHas(key)) {
const s = computed(() => {
return this.getObject(id).value?.obj_info.source_code ?? null;
if (this.circuitHolderIds.value?.includes(id)) {
const circuit = this.getObject(id).value;
const ic = this.getObject(circuit?.obj_info.socketed_ic).value;
return ic?.obj_info.source_code ?? null;
} else if (this.programHolderIds.value?.includes(id)) {
return this.getObject(id).value?.obj_info.source_code ?? null;
} else {
console.error(`(objectId: ${id}) does not refer to a object with a known program interface`)
return null;
}
})
this.signalCacheSet(key, s);
return s;

View File

@@ -89,6 +89,7 @@ fn write_instructions_enum<T: std::io::Write>(
#[strum(use_phf, serialize_all = "lowercase")]
#[serde(rename_all = "lowercase")]
pub enum InstructionOp {
#[strum(props( example = "", desc = "No Operation", operands = "0" ))]
Nop,
#(#inst_variants)*