lower to ir
This commit is contained in:
parent
4cadf3adbd
commit
ee00599362
@ -7,6 +7,7 @@ import {
|
|||||||
v2,
|
v2,
|
||||||
V2,
|
V2,
|
||||||
} from "./V2";
|
} from "./V2";
|
||||||
|
import * as ir from "./ir";
|
||||||
|
|
||||||
export class Board {
|
export class Board {
|
||||||
private components: Component[] = [];
|
private components: Component[] = [];
|
||||||
@ -192,6 +193,135 @@ export class Board {
|
|||||||
);
|
);
|
||||||
this.wires = this.wires.filter((wire) => !wire.isSelected(selection));
|
this.wires = this.wires.filter((wire) => !wire.isSelected(selection));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toIr(): ir.Component {
|
||||||
|
console.log("Lowering to IR");
|
||||||
|
|
||||||
|
for (const comp of this.components) {
|
||||||
|
comp.markedWiresConnected = [];
|
||||||
|
}
|
||||||
|
for (const joint of this.joints) {
|
||||||
|
joint.markedWiresConnected = [];
|
||||||
|
}
|
||||||
|
for (const wire of this.wires) {
|
||||||
|
wire.markConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputs = this.components.filter(
|
||||||
|
(comp) => comp.kind.label === "input",
|
||||||
|
);
|
||||||
|
const outputs = this.components.filter(
|
||||||
|
(comp) => comp.kind.label === "output",
|
||||||
|
);
|
||||||
|
|
||||||
|
const inputIdcs = new Map<Component, number>();
|
||||||
|
for (const [i, input] of inputs.entries()) {
|
||||||
|
inputIdcs.set(input, i);
|
||||||
|
}
|
||||||
|
const outputIdcs = new Map<Component, number>();
|
||||||
|
for (const [i, output] of outputs.entries()) {
|
||||||
|
outputIdcs.set(output, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const b = new ir.ComponentBuilder(inputs.length, outputs.length, "main");
|
||||||
|
|
||||||
|
const wireStates = new Map<Wire, ir.State>();
|
||||||
|
for (const wire of this.wires) {
|
||||||
|
wireStates.set(wire, b.makeState());
|
||||||
|
}
|
||||||
|
|
||||||
|
const compSet = new Set<Component>();
|
||||||
|
const jointSet = new Set<Joint>();
|
||||||
|
const wireSet = new Set<Wire>();
|
||||||
|
|
||||||
|
const visitor: BoardVisitor = {
|
||||||
|
visitComponent: (comp) => {
|
||||||
|
if (compSet.has(comp)) return "break";
|
||||||
|
compSet.add(comp);
|
||||||
|
|
||||||
|
const inputStates = new Map<number, ir.State>();
|
||||||
|
for (const [wire, connection] of comp.markedWiresConnected) {
|
||||||
|
if (connection.tag === "InputPin") {
|
||||||
|
inputStates.set(connection.i, wireStates.get(wire)!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputStmt = (i: number) => {
|
||||||
|
return inputStates.has(i)
|
||||||
|
? b.makeGetState(inputStates.get(i)!)
|
||||||
|
: b.makeNull();
|
||||||
|
};
|
||||||
|
|
||||||
|
const stmt = (() => {
|
||||||
|
switch (comp.kind.label) {
|
||||||
|
case "input":
|
||||||
|
return b.makeInput(inputIdcs.get(comp)!);
|
||||||
|
case "output":
|
||||||
|
return b.makeOutput(outputIdcs.get(comp)!, inputStmt(0));
|
||||||
|
case "not":
|
||||||
|
return b.makeNot(inputStmt(0));
|
||||||
|
case "and":
|
||||||
|
return b.makeBinary("And", inputStmt(0), inputStmt(1));
|
||||||
|
case "or":
|
||||||
|
return b.makeBinary("Or", inputStmt(0), inputStmt(1));
|
||||||
|
default:
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
for (const [wire, connection] of comp.markedWiresConnected) {
|
||||||
|
if (connection.tag === "OutputPin") {
|
||||||
|
b.makeSetState(wireStates.get(wire)!, stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
visitJoint: (joint) => {
|
||||||
|
if (jointSet.has(joint)) return "break";
|
||||||
|
jointSet.add(joint);
|
||||||
|
|
||||||
|
const visited = joint.markedWiresConnected.filter(([wire]) =>
|
||||||
|
wireSet.has(wire),
|
||||||
|
);
|
||||||
|
if (visited.length > 1) {
|
||||||
|
throw new Error("joint has more than 1 input");
|
||||||
|
}
|
||||||
|
|
||||||
|
const notVisited = joint.markedWiresConnected.filter(
|
||||||
|
([wire]) => !wireSet.has(wire),
|
||||||
|
);
|
||||||
|
|
||||||
|
const sourceState = wireStates.get(visited[0][0]);
|
||||||
|
if (!sourceState) {
|
||||||
|
throw new Error("assert");
|
||||||
|
}
|
||||||
|
const src = b.makeGetState(sourceState);
|
||||||
|
|
||||||
|
for (const [wire] of notVisited) {
|
||||||
|
const dst = wireStates.get(wire);
|
||||||
|
if (!dst) {
|
||||||
|
throw new Error("assert");
|
||||||
|
}
|
||||||
|
b.makeSetState(dst, src);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
visitWire: (wire) => {
|
||||||
|
if (wireSet.has(wire)) return "break";
|
||||||
|
wireSet.add(wire);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const comp of this.components) {
|
||||||
|
comp.visitForward(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BoardVisitor {
|
||||||
|
visitComponent(comp: Component): void | "break";
|
||||||
|
visitJoint(joint: Joint): void | "break";
|
||||||
|
visitWire(wire: Wire): void | "break";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ComponentRepo {
|
export class ComponentRepo {
|
||||||
@ -221,6 +351,8 @@ export class ComponentRepo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Component {
|
export class Component {
|
||||||
|
public markedWiresConnected: [Wire, WireConnection][] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public kind: ComponentKind,
|
public kind: ComponentKind,
|
||||||
public pos: V2,
|
public pos: V2,
|
||||||
@ -264,6 +396,17 @@ export class Component {
|
|||||||
outputPinPos(i: number): V2 {
|
outputPinPos(i: number): V2 {
|
||||||
return this.pos.add(v2(this.kind.size.x, this.kind.outputPinOffsets()[i]));
|
return this.pos.add(v2(this.kind.size.x, this.kind.outputPinOffsets()[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
visitForward(visitor: BoardVisitor) {
|
||||||
|
if (visitor.visitComponent(this) === "break") return;
|
||||||
|
for (const [wire, connection] of this.markedWiresConnected) {
|
||||||
|
switch (connection.tag) {
|
||||||
|
case "OutputPin":
|
||||||
|
wire.visitForward(visitor, connection);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ComponentMouseOverResult =
|
type ComponentMouseOverResult =
|
||||||
@ -291,11 +434,23 @@ export class ComponentKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Joint {
|
export class Joint {
|
||||||
|
public markedWiresConnected: [Wire, WireConnection][] = [];
|
||||||
|
|
||||||
constructor(public pos: V2) {}
|
constructor(public pos: V2) {}
|
||||||
|
|
||||||
isMouseOver(pos: V2): boolean {
|
isMouseOver(pos: V2): boolean {
|
||||||
return this.pos.distance(pos) < 6;
|
return this.pos.distance(pos) < 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
visitForward(visitor: BoardVisitor, entryWire: Wire) {
|
||||||
|
if (visitor.visitJoint(this) === "break") return;
|
||||||
|
for (const [wire, connection] of this.markedWiresConnected) {
|
||||||
|
if (wire === entryWire) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
wire.visitForward(visitor, connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Wire {
|
export class Wire {
|
||||||
@ -304,6 +459,26 @@ export class Wire {
|
|||||||
private end: WireConnection,
|
private end: WireConnection,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
isInput(): boolean {
|
||||||
|
return this.mapConns((connection) => connection.tag === "InputPin").some(
|
||||||
|
(v) => v,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
markConnections() {
|
||||||
|
this.mapConns((connection) => {
|
||||||
|
switch (connection.tag) {
|
||||||
|
case "InputPin":
|
||||||
|
case "OutputPin":
|
||||||
|
connection.comp.markedWiresConnected.push([this, connection]);
|
||||||
|
break;
|
||||||
|
case "Joint":
|
||||||
|
connection.joint.markedWiresConnected.push([this, connection]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
isMouseOver(pos: V2): boolean {
|
isMouseOver(pos: V2): boolean {
|
||||||
const distance = lineSegmentPointDistance(
|
const distance = lineSegmentPointDistance(
|
||||||
this.beginPos(),
|
this.beginPos(),
|
||||||
@ -314,23 +489,66 @@ export class Wire {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isSelected(selection: Selection): boolean {
|
isSelected(selection: Selection): boolean {
|
||||||
return (
|
return this.mapConns((connection) => {
|
||||||
this.connectionIsSelected(this.begin, selection) ||
|
switch (connection.tag) {
|
||||||
this.connectionIsSelected(this.end, selection)
|
case "InputPin":
|
||||||
);
|
case "OutputPin":
|
||||||
|
return selection.isComponentSelected(connection.comp);
|
||||||
|
case "Joint":
|
||||||
|
return selection.isJointSelected(connection.joint);
|
||||||
|
}
|
||||||
|
}).some((v) => v);
|
||||||
}
|
}
|
||||||
|
|
||||||
private connectionIsSelected(
|
connectedToComponent(comp: Component): boolean {
|
||||||
connection: WireConnection,
|
return this.mapConns((connection) => {
|
||||||
selection: Selection,
|
switch (connection.tag) {
|
||||||
): boolean {
|
case "InputPin":
|
||||||
switch (connection.tag) {
|
case "OutputPin":
|
||||||
case "InputPin":
|
return connection.comp === comp;
|
||||||
case "OutputPin":
|
case "Joint":
|
||||||
return selection.isComponentSelected(connection.comp);
|
return false;
|
||||||
case "Joint":
|
}
|
||||||
return selection.isJointSelected(connection.joint);
|
}).some((v) => v);
|
||||||
}
|
}
|
||||||
|
connectedToJoint(joint: Joint): boolean {
|
||||||
|
return this.mapConns((connection) => {
|
||||||
|
switch (connection.tag) {
|
||||||
|
case "InputPin":
|
||||||
|
case "OutputPin":
|
||||||
|
return false;
|
||||||
|
case "Joint":
|
||||||
|
return connection.joint === joint;
|
||||||
|
}
|
||||||
|
}).some((v) => v);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedComponents(): Component[] {
|
||||||
|
return this.mapConns((connection) => {
|
||||||
|
switch (connection.tag) {
|
||||||
|
case "InputPin":
|
||||||
|
case "OutputPin":
|
||||||
|
return [connection.comp];
|
||||||
|
case "Joint":
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}).flat();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedJoints(): Joint[] {
|
||||||
|
return this.mapConns((connection) => {
|
||||||
|
switch (connection.tag) {
|
||||||
|
case "InputPin":
|
||||||
|
case "OutputPin":
|
||||||
|
return [];
|
||||||
|
case "Joint":
|
||||||
|
return [connection.joint];
|
||||||
|
}
|
||||||
|
}).flat();
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapConns<R>(mapper: (connection: WireConnection) => R): [R, R] {
|
||||||
|
return [mapper(this.begin), mapper(this.end)];
|
||||||
}
|
}
|
||||||
|
|
||||||
beginPos(): V2 {
|
beginPos(): V2 {
|
||||||
@ -351,6 +569,19 @@ export class Wire {
|
|||||||
return connection.joint.pos;
|
return connection.joint.pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
visitForward(visitor: BoardVisitor, prev: WireConnection) {
|
||||||
|
if (visitor.visitWire(this) === "break") return;
|
||||||
|
const connection = this.begin === prev ? this.end : this.begin;
|
||||||
|
switch (connection.tag) {
|
||||||
|
case "InputPin":
|
||||||
|
connection.comp.visitForward(visitor);
|
||||||
|
break;
|
||||||
|
case "Joint":
|
||||||
|
connection.joint.visitForward(visitor, this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WireConnection =
|
export type WireConnection =
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
import { Renderer } from "./Renderer";
|
import { Renderer } from "./Renderer";
|
||||||
import * as states from "./states";
|
import * as states from "./states";
|
||||||
import { v2, V2 } from "./V2";
|
import { v2, V2 } from "./V2";
|
||||||
|
import * as ir from "./ir";
|
||||||
|
|
||||||
export type Tool = string;
|
export type Tool = string;
|
||||||
|
|
||||||
@ -136,6 +137,18 @@ export class Cx {
|
|||||||
const absY = pos.y - this.offset.y;
|
const absY = pos.y - this.offset.y;
|
||||||
return v2(absX, absY);
|
return v2(absX, absY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runSimulation() {
|
||||||
|
const comp = this.board.toIr();
|
||||||
|
console.log("Before optimizing");
|
||||||
|
console.log(new ir.ComponentPrinter().stringify(comp));
|
||||||
|
|
||||||
|
const optimizer = new ir.ComponentOptimizer(comp);
|
||||||
|
optimizer.optimize();
|
||||||
|
|
||||||
|
console.log("After optimizing");
|
||||||
|
console.log(new ir.ComponentPrinter().stringify(comp));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SelectionBox {
|
export class SelectionBox {
|
||||||
|
|||||||
210
editor/src/editor/ir.ts
Normal file
210
editor/src/editor/ir.ts
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
export class Component {
|
||||||
|
constructor(
|
||||||
|
public stmts: Stmt[],
|
||||||
|
public states: State[],
|
||||||
|
public inputs: number,
|
||||||
|
public outputs: number,
|
||||||
|
public label: string,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Stmt {
|
||||||
|
constructor(public kind: StmtKind) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StmtKind =
|
||||||
|
| { tag: "Null" }
|
||||||
|
| { tag: "Input"; i: number }
|
||||||
|
| { tag: "Output"; i: number; src: Stmt }
|
||||||
|
| { tag: "GetState"; state: State }
|
||||||
|
| { tag: "SetState"; state: State; src: Stmt }
|
||||||
|
| { tag: "Not"; op: Stmt }
|
||||||
|
| { tag: "And" | "Or"; lhs: Stmt; rhs: Stmt }
|
||||||
|
| { tag: "Component"; comp: Component; inputs: Stmt[]; outputs: Stmt[] };
|
||||||
|
|
||||||
|
export class State {}
|
||||||
|
|
||||||
|
export class ComponentBuilder {
|
||||||
|
private stmts: Stmt[] = [];
|
||||||
|
private states: State[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private inputs: number,
|
||||||
|
private outputs: number,
|
||||||
|
private label: string,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
makeState(): State {
|
||||||
|
const state = new State();
|
||||||
|
this.states.push(state);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
makeNull(): Stmt {
|
||||||
|
return this.makeStmt({ tag: "Null" });
|
||||||
|
}
|
||||||
|
makeInput(i: number): Stmt {
|
||||||
|
return this.makeStmt({ tag: "Input", i });
|
||||||
|
}
|
||||||
|
makeOutput(i: number, src: Stmt): Stmt {
|
||||||
|
return this.makeStmt({ tag: "Output", i, src });
|
||||||
|
}
|
||||||
|
makeGetState(state: State): Stmt {
|
||||||
|
return this.makeStmt({ tag: "GetState", state });
|
||||||
|
}
|
||||||
|
makeSetState(state: State, src: Stmt): Stmt {
|
||||||
|
return this.makeStmt({ tag: "SetState", state, src });
|
||||||
|
}
|
||||||
|
makeNot(op: Stmt): Stmt {
|
||||||
|
return this.makeStmt({ tag: "Not", op });
|
||||||
|
}
|
||||||
|
makeBinary(tag: "And" | "Or", lhs: Stmt, rhs: Stmt): Stmt {
|
||||||
|
return this.makeStmt({ tag, lhs, rhs });
|
||||||
|
}
|
||||||
|
|
||||||
|
private makeStmt(kind: StmtKind): Stmt {
|
||||||
|
const stmt = new Stmt(kind);
|
||||||
|
this.stmts.push(stmt);
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
build(): Component {
|
||||||
|
return new Component(
|
||||||
|
this.stmts,
|
||||||
|
this.states,
|
||||||
|
this.inputs,
|
||||||
|
this.outputs,
|
||||||
|
this.label,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StmtsMutater {
|
||||||
|
constructor(private comp: Component) {}
|
||||||
|
|
||||||
|
replaceStmt(oldStmt: Stmt, newStmt: Stmt) {
|
||||||
|
for (const stmt of this.comp.stmts) {
|
||||||
|
const k = stmt.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "Null":
|
||||||
|
case "Input":
|
||||||
|
case "GetState":
|
||||||
|
break;
|
||||||
|
case "Output":
|
||||||
|
case "SetState":
|
||||||
|
if (k.src === oldStmt) k.src = newStmt;
|
||||||
|
break;
|
||||||
|
case "Not":
|
||||||
|
if (k.op === oldStmt) k.op = newStmt;
|
||||||
|
break;
|
||||||
|
case "And":
|
||||||
|
case "Or":
|
||||||
|
if (k.lhs === oldStmt) k.lhs = newStmt;
|
||||||
|
if (k.rhs === oldStmt) k.rhs = newStmt;
|
||||||
|
break;
|
||||||
|
case "Component":
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeStmtAt(i: number) {
|
||||||
|
this.comp.stmts.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ComponentOptimizer {
|
||||||
|
constructor(private comp: Component) {}
|
||||||
|
|
||||||
|
optimize() {
|
||||||
|
let lengthBefore: number;
|
||||||
|
do {
|
||||||
|
lengthBefore = this.comp.stmts.length;
|
||||||
|
|
||||||
|
this.eliminateRedundantState();
|
||||||
|
this.hoistInputs();
|
||||||
|
} while (this.comp.stmts.length !== lengthBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
eliminateRedundantState() {
|
||||||
|
const mut = new StmtsMutater(this.comp);
|
||||||
|
const immediatelyReadStateStmt = new Map<State, Stmt>();
|
||||||
|
|
||||||
|
for (const [i, stmt] of this.comp.stmts.entries()) {
|
||||||
|
const k = stmt.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "GetState": {
|
||||||
|
const candidate = immediatelyReadStateStmt.get(k.state);
|
||||||
|
if (candidate) {
|
||||||
|
mut.replaceStmt(stmt, candidate);
|
||||||
|
mut.removeStmtAt(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "SetState":
|
||||||
|
immediatelyReadStateStmt.set(k.state, k.src);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hoistInputs() {
|
||||||
|
const inputs = this.comp.stmts.filter((stmt) => stmt.kind.tag === "Input");
|
||||||
|
const notInputs = this.comp.stmts.filter(
|
||||||
|
(stmt) => stmt.kind.tag !== "Input",
|
||||||
|
);
|
||||||
|
this.comp.stmts = [...inputs, ...notInputs];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ComponentPrinter {
|
||||||
|
private stmtIds = new Map<Stmt, number>();
|
||||||
|
private stateIds = new Map<State, number>();
|
||||||
|
|
||||||
|
stringify(comp: Component): string {
|
||||||
|
return (
|
||||||
|
`component ${comp.label} ${comp.inputs} ${comp.outputs} {\n` +
|
||||||
|
// ` states [ ${comp.states.map((state) => this.stateId(state)).join(", ")} ]\n` +
|
||||||
|
`${comp.stmts.map((stmt) => ` ${this.stringifyStmt(stmt)}\n`).join("")}}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private stmtId(stmt: Stmt): string {
|
||||||
|
if (!this.stmtIds.has(stmt)) {
|
||||||
|
this.stmtIds.set(stmt, this.stmtIds.size);
|
||||||
|
}
|
||||||
|
return `%${this.stmtIds.get(stmt)!}`;
|
||||||
|
}
|
||||||
|
private stateId(state: State): string {
|
||||||
|
if (!this.stateIds.has(state)) {
|
||||||
|
this.stateIds.set(state, this.stateIds.size);
|
||||||
|
}
|
||||||
|
return `#${this.stateIds.get(state)!}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private stringifyStmt(stmt: Stmt) {
|
||||||
|
const stmtId = (stmt: Stmt) => this.stmtId(stmt);
|
||||||
|
const stateId = (state: State) => this.stateId(state);
|
||||||
|
|
||||||
|
const k = stmt.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "Null":
|
||||||
|
return `${stmtId(stmt)} = Null`;
|
||||||
|
case "Input":
|
||||||
|
return `${stmtId(stmt)} = Input ${k.i}`;
|
||||||
|
case "Output":
|
||||||
|
return `Output ${k.i}, ${stmtId(k.src)}`;
|
||||||
|
case "GetState":
|
||||||
|
return `${stmtId(stmt)} = GetState ${stateId(k.state)}`;
|
||||||
|
case "SetState":
|
||||||
|
return `SetState ${stateId(k.state)}, ${stmtId(k.src)}`;
|
||||||
|
case "Not":
|
||||||
|
return `${stmtId(stmt)} = Not ${stmtId(k.op)}`;
|
||||||
|
case "And":
|
||||||
|
case "Or":
|
||||||
|
return `${stmtId(stmt)} = ${k.tag} ${stmtId(k.lhs)}, ${stmtId(k.rhs)}`;
|
||||||
|
case "Component":
|
||||||
|
return `Component <...>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,6 +20,10 @@ export class Normal implements State {
|
|||||||
|
|
||||||
constructor(private cx: Cx) {}
|
constructor(private cx: Cx) {}
|
||||||
|
|
||||||
|
enterState(): void {
|
||||||
|
this.cx.runSimulation();
|
||||||
|
}
|
||||||
|
|
||||||
onMouseDown(pos: V2): void {
|
onMouseDown(pos: V2): void {
|
||||||
if (
|
if (
|
||||||
this.cx.board.handleMouseClick(pos.sub(this.cx.offset), {
|
this.cx.board.handleMouseClick(pos.sub(this.cx.offset), {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user