components work
This commit is contained in:
parent
cbc416da46
commit
e8c8609217
@ -29,10 +29,10 @@ export class Board {
|
||||
|
||||
private wireCachedState = new Map<Wire, ir.State>();
|
||||
|
||||
constructor() {}
|
||||
constructor(private repo: ComponentRepo) {}
|
||||
|
||||
static withExample(repo: ComponentRepo): Board {
|
||||
const board = new Board();
|
||||
const board = new Board(repo);
|
||||
board.placeComponent(repo.get("input"), v2(100, 100));
|
||||
board.placeComponent(repo.get("input"), v2(100, 200));
|
||||
board.placeComponent(repo.get("and"), v2(300, 150));
|
||||
@ -52,13 +52,10 @@ export class Board {
|
||||
return board;
|
||||
}
|
||||
|
||||
static fromSerialized(
|
||||
data: ser.Board,
|
||||
kindMap: Map<string, ComponentKind>,
|
||||
): Board {
|
||||
const board = new Board();
|
||||
static fromSerialized(data: ser.Board, repo: ComponentRepo): Board {
|
||||
const board = new Board(repo);
|
||||
board.components = data.components.map((c) =>
|
||||
Component.fromSerialized(c, kindMap),
|
||||
Component.fromSerialized(c, repo.defs),
|
||||
);
|
||||
board.joints = data.joints.map((j) => Joint.fromSerialized(j));
|
||||
board.wires = data.wires.map((w) =>
|
||||
@ -318,12 +315,20 @@ export class Board {
|
||||
|
||||
simulate(inputStates: Map<Component, boolean>) {
|
||||
console.log("Lowering to IR");
|
||||
const comp = this.toIr();
|
||||
const comps = new Map<string, ir.Component>();
|
||||
const comp = this.toIr("<main>", comps);
|
||||
console.log("Before optimizing");
|
||||
for (const [_label, comp] of comps) {
|
||||
console.log(...new ir.ComponentPrinter().stringifyToConsole(comp));
|
||||
}
|
||||
console.log(...new ir.ComponentPrinter().stringifyToConsole(comp));
|
||||
|
||||
for (const [_label, comp] of comps) {
|
||||
new ir.ComponentOptimizer(comp, []).optimizeComponent();
|
||||
}
|
||||
|
||||
const replacedStates: [ir.State, ir.State][] = [];
|
||||
new ir.ComponentOptimizer(comp, replacedStates).optimize();
|
||||
new ir.ComponentOptimizer(comp, replacedStates).optimizeMain();
|
||||
|
||||
for (const [oldState, newState] of replacedStates) {
|
||||
this.stateWireMap
|
||||
@ -333,6 +338,9 @@ export class Board {
|
||||
}
|
||||
|
||||
console.log("After optimizing");
|
||||
for (const [_label, comp] of comps) {
|
||||
console.log(...new ir.ComponentPrinter().stringifyToConsole(comp));
|
||||
}
|
||||
console.log(...new ir.ComponentPrinter().stringifyToConsole(comp));
|
||||
|
||||
const inputs = this.inputArray(inputStates);
|
||||
@ -357,7 +365,7 @@ export class Board {
|
||||
}
|
||||
}
|
||||
|
||||
toIr(): ir.Component {
|
||||
toIr(label: string, comps: Map<string, ir.Component>): ir.Component {
|
||||
for (const comp of this.components) {
|
||||
comp.markedWiresConnected = [];
|
||||
}
|
||||
@ -384,7 +392,7 @@ export class Board {
|
||||
outputIdcs.set(output, i);
|
||||
}
|
||||
|
||||
const b = new ir.ComponentBuilder(inputs.length, outputs.length, "main");
|
||||
const b = new ir.ComponentBuilder(inputs.length, outputs.length, label);
|
||||
|
||||
this.stateWireMap.clear();
|
||||
const wireStates = new Map<Wire, ir.State>();
|
||||
@ -429,14 +437,32 @@ export class Board {
|
||||
return b.makeBinary("And", inputStmt(0), inputStmt(1));
|
||||
case "or":
|
||||
return b.makeBinary("Or", inputStmt(0), inputStmt(1));
|
||||
default:
|
||||
throw new Error("not implemented");
|
||||
default: {
|
||||
const savedBoard = this.repo.getSavedBoard(comp.kind.label);
|
||||
if (!savedBoard) {
|
||||
throw new Error(`no component '${comp.kind.label}'`);
|
||||
}
|
||||
|
||||
const label = comp.kind.label;
|
||||
const board = Board.fromSerialized(savedBoard, this.repo);
|
||||
|
||||
const ir = comps.get(label) ?? board.toIr(label, comps);
|
||||
comps.set(label, ir);
|
||||
|
||||
return b.makeCall(
|
||||
ir,
|
||||
comp.kind.inputs.map((_, i) => inputStmt(i)),
|
||||
);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
for (const [wire, connection] of comp.markedWiresConnected) {
|
||||
if (connection.tag === "OutputPin") {
|
||||
b.makeSetState(wireStates.get(wire)!, stmt);
|
||||
b.makeSetState(
|
||||
wireStates.get(wire)!,
|
||||
stmt.kind.tag === "Call" ? b.makeElem(stmt, connection.i) : stmt,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -491,6 +517,7 @@ export interface BoardVisitor {
|
||||
|
||||
export class ComponentRepo {
|
||||
public defs = new Map<string, ComponentKind>();
|
||||
private savedBoards = new Map<string, ser.Board>();
|
||||
|
||||
static withDefaults(): ComponentRepo {
|
||||
const repo = new ComponentRepo();
|
||||
@ -507,12 +534,14 @@ export class ComponentRepo {
|
||||
repo.defs = new Map(
|
||||
data.defs.map((e) => [e[0], ComponentKind.fromSerialized(e[1])]),
|
||||
);
|
||||
repo.savedBoards = new Map(data.savedBoards);
|
||||
return repo;
|
||||
}
|
||||
|
||||
serialize(): ser.ComponentRepo {
|
||||
return {
|
||||
defs: [...this.defs.entries()].map((e) => [e[0], e[1].serialize()]),
|
||||
savedBoards: [...this.savedBoards],
|
||||
};
|
||||
}
|
||||
|
||||
@ -531,6 +560,14 @@ export class ComponentRepo {
|
||||
}
|
||||
return kind;
|
||||
}
|
||||
|
||||
addSavedBoard(ident: string, savedBoard: ser.Board) {
|
||||
this.savedBoards.set(ident, savedBoard);
|
||||
}
|
||||
|
||||
getSavedBoard(ident: string): ser.Board | null {
|
||||
return this.savedBoards.get(ident) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
export class Component {
|
||||
|
||||
@ -74,6 +74,8 @@ export class Editor {
|
||||
}
|
||||
case "SelectTab": {
|
||||
this.switchTab(ev.idx);
|
||||
this.events.send({ tag: "SimulateRequest" });
|
||||
|
||||
break;
|
||||
}
|
||||
case "CloseComponent": {
|
||||
@ -89,7 +91,7 @@ export class Editor {
|
||||
break;
|
||||
}
|
||||
case "SimulateRequest": {
|
||||
// this.runSimulation();
|
||||
this.runSimulation();
|
||||
break;
|
||||
}
|
||||
case "SaveRequest": {
|
||||
@ -146,7 +148,6 @@ export class Editor {
|
||||
|
||||
runSimulation() {
|
||||
this.board.simulate(this.inputStates);
|
||||
this.events.send({ tag: "RenderRequest" });
|
||||
}
|
||||
|
||||
private onSelectTool(tool: string) {
|
||||
|
||||
@ -10,7 +10,6 @@ export class Project {
|
||||
private events: EventBus,
|
||||
private boardEditors: BoardEditor[],
|
||||
public componentRepo: ComponentRepo,
|
||||
private savedBoards: Map<string, ser.Board>,
|
||||
) {
|
||||
this.current = boardEditors[this.selectedIdx];
|
||||
}
|
||||
@ -34,7 +33,6 @@ export class Project {
|
||||
},
|
||||
],
|
||||
repo,
|
||||
new Map(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -46,13 +44,10 @@ export class Project {
|
||||
}
|
||||
|
||||
save() {
|
||||
console.log("Saving");
|
||||
// console.log("Saving");
|
||||
const data = this.serialize();
|
||||
globalThis.localStorage.setItem(
|
||||
"nandsim",
|
||||
JSON.stringify(this.serialize()),
|
||||
);
|
||||
console.log(data);
|
||||
globalThis.localStorage.setItem("nandsim", JSON.stringify(data));
|
||||
// console.log(data);
|
||||
}
|
||||
|
||||
private static fromSerialized(data: ser.Project, events: EventBus): Project {
|
||||
@ -62,11 +57,10 @@ export class Project {
|
||||
data.boardEditors.map(
|
||||
(data): BoardEditor => ({
|
||||
name: data.name,
|
||||
board: Board.fromSerialized(data.board, repo.defs),
|
||||
board: Board.fromSerialized(data.board, repo),
|
||||
}),
|
||||
),
|
||||
repo,
|
||||
new Map(data.savedBoards),
|
||||
);
|
||||
return project;
|
||||
}
|
||||
@ -82,7 +76,6 @@ export class Project {
|
||||
),
|
||||
currentBoardEditorIdx: this.selectedIdx,
|
||||
componentRepo,
|
||||
savedBoards: [...this.savedBoards],
|
||||
};
|
||||
}
|
||||
|
||||
@ -103,7 +96,7 @@ export class Project {
|
||||
newTab(): number {
|
||||
this.boardEditors.push({
|
||||
name: `(Unnamed ${this.boardEditors.length})`,
|
||||
board: new Board(),
|
||||
board: new Board(this.componentRepo),
|
||||
});
|
||||
this.events.send({ tag: "ShowSelectedTab", idx: this.selectedIdx });
|
||||
return this.boardEditors.length - 1;
|
||||
@ -114,11 +107,11 @@ export class Project {
|
||||
this.current = this.boardEditors[this.selectedIdx];
|
||||
this.events.send({ tag: "ShowSelectedTab", idx: this.selectedIdx });
|
||||
this.events.send({ tag: "ShowSelectedTool", tool: this.current.name });
|
||||
this.events.send({ tag: "SaveRequest" });
|
||||
}
|
||||
|
||||
closeTab(): number {
|
||||
const [removed] = this.boardEditors.splice(this.selectedIdx, 1);
|
||||
this.savedBoards.set(removed.name, removed.board.serialize());
|
||||
const [_removed] = this.boardEditors.splice(this.selectedIdx, 1);
|
||||
this.events.send({ tag: "SaveRequest" });
|
||||
|
||||
if (this.boardEditors.length === 0) {
|
||||
@ -138,7 +131,13 @@ export class Project {
|
||||
this.current.name,
|
||||
this.current.board.toComponentKind(this.current.name),
|
||||
);
|
||||
this.componentRepo.addSavedBoard(
|
||||
this.current.name,
|
||||
this.current.board.serialize(),
|
||||
);
|
||||
|
||||
this.events.send({ tag: "ShowSelectedTool", tool: this.current.name });
|
||||
this.events.send({ tag: "SaveRequest" });
|
||||
}
|
||||
|
||||
tabWithTool(name: string): number {
|
||||
@ -148,12 +147,12 @@ export class Project {
|
||||
return foundIdx;
|
||||
}
|
||||
|
||||
const saved = this.savedBoards.get(name);
|
||||
const saved = this.componentRepo.getSavedBoard(name);
|
||||
if (!saved) throw new Error(`cannot open '${name}'`);
|
||||
|
||||
this.boardEditors.push({
|
||||
name: name,
|
||||
board: Board.fromSerialized(saved, this.componentRepo.defs),
|
||||
board: Board.fromSerialized(saved, this.componentRepo),
|
||||
});
|
||||
this.events.send({ tag: "ShowSelectedTab", idx: this.selectedIdx });
|
||||
return this.boardEditors.length - 1;
|
||||
|
||||
@ -29,8 +29,10 @@ export class Stmt {
|
||||
case "And":
|
||||
case "Or":
|
||||
return [k.lhs, k.rhs];
|
||||
case "Component":
|
||||
case "Call":
|
||||
return [...k.inputs];
|
||||
case "Elem":
|
||||
return [k.src];
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,10 +55,11 @@ export class Stmt {
|
||||
if (k.lhs === oldStmt) k.lhs = newStmt;
|
||||
if (k.rhs === oldStmt) k.rhs = newStmt;
|
||||
break;
|
||||
case "Component":
|
||||
k.inputs = k.inputs.map((stmt) =>
|
||||
stmt === oldStmt ? newStmt : oldStmt,
|
||||
);
|
||||
case "Call":
|
||||
k.inputs = k.inputs.map((stmt) => (stmt === oldStmt ? newStmt : stmt));
|
||||
break;
|
||||
case "Elem":
|
||||
if (k.src === oldStmt) k.src = newStmt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -82,7 +85,8 @@ export type StmtKind =
|
||||
| { 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[] };
|
||||
| { tag: "Call"; comp: Component; inputs: Stmt[] }
|
||||
| { tag: "Elem"; src: Stmt; i: number };
|
||||
|
||||
export class State {}
|
||||
|
||||
@ -123,6 +127,12 @@ export class ComponentBuilder {
|
||||
makeBinary(tag: "And" | "Or", lhs: Stmt, rhs: Stmt): Stmt {
|
||||
return this.makeStmt({ tag, lhs, rhs });
|
||||
}
|
||||
makeCall(comp: Component, inputs: Stmt[]): Stmt {
|
||||
return this.makeStmt({ tag: "Call", comp, inputs });
|
||||
}
|
||||
makeElem(src: Stmt, i: number): Stmt {
|
||||
return this.makeStmt({ tag: "Elem", src, i });
|
||||
}
|
||||
|
||||
private makeStmt(kind: StmtKind): Stmt {
|
||||
const stmt = new Stmt(kind);
|
||||
@ -186,7 +196,15 @@ export class ComponentOptimizer {
|
||||
private replacedStates: [State, State][],
|
||||
) {}
|
||||
|
||||
optimize() {
|
||||
optimizeMain() {
|
||||
this.optimizeWithOptions(false);
|
||||
}
|
||||
|
||||
optimizeComponent() {
|
||||
this.optimizeWithOptions(true);
|
||||
}
|
||||
|
||||
private optimizeWithOptions(removeUnusedStates: boolean) {
|
||||
const score = () => this.comp.stmts.length * 100 + this.comp.states.length;
|
||||
|
||||
let scoreBefore: number;
|
||||
@ -199,6 +217,9 @@ export class ComponentOptimizer {
|
||||
this.collapseStates();
|
||||
this.eliminateUnusedStates();
|
||||
this.eliminateRedundantSetState();
|
||||
if (removeUnusedStates) {
|
||||
this.eliminateIndependentSetState();
|
||||
}
|
||||
} while (score() !== scoreBefore);
|
||||
}
|
||||
|
||||
@ -309,6 +330,28 @@ export class ComponentOptimizer {
|
||||
}
|
||||
}
|
||||
|
||||
eliminateIndependentSetState() {
|
||||
const mut = new StmtsMutater(this.comp, this.replacedStates);
|
||||
|
||||
const usedStates = new Set<State>();
|
||||
for (const stmt of mut) {
|
||||
const k = stmt.kind;
|
||||
switch (k.tag) {
|
||||
case "GetState":
|
||||
usedStates.add(k.state);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const stmt of [...mut]) {
|
||||
if (stmt.kind.tag === "SetState" && !usedStates.has(stmt.kind.state)) {
|
||||
mut.removeStmt(stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private indexMap(): Map<Stmt, number> {
|
||||
return new Map(this.comp.stmts.map((stmt, i) => [stmt, i]));
|
||||
}
|
||||
@ -355,7 +398,7 @@ export class ComponentPrinter {
|
||||
"\\c(color: #d44949; font-weight: bold)$&\\c",
|
||||
)
|
||||
.replaceAll(
|
||||
/(?:Null)|(?:Input)|(?:Output)|(?:GetState)|(?:SetState)|(?:Not)|(?:And)|(?:Or)|(?:Component)/g,
|
||||
/(?:Null)|(?:Input)|(?:Output)|(?:GetState)|(?:SetState)|(?:Not)|(?:And)|(?:Or)|(?:Call)|(?:Elem)/g,
|
||||
"\\c(color: orange)$&\\c",
|
||||
);
|
||||
|
||||
@ -407,8 +450,10 @@ export class ComponentPrinter {
|
||||
case "And":
|
||||
case "Or":
|
||||
return `${stmtId(stmt)} = ${k.tag} ${stmtId(k.lhs)}, ${stmtId(k.rhs)}`;
|
||||
case "Component":
|
||||
return `Component <...>`;
|
||||
case "Call":
|
||||
return `${stmtId(stmt)} = Call ${k.comp.label} (${k.inputs.map((s) => stmtId(s)).join(", ")})`;
|
||||
case "Elem":
|
||||
return `${stmtId(stmt)} = Elem ${stmtId(k.src)}, ${k.i}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ export type Project = {
|
||||
boardEditors: BoardEditor[];
|
||||
currentBoardEditorIdx: number;
|
||||
componentRepo: ComponentRepo;
|
||||
savedBoards: [string, Board][];
|
||||
};
|
||||
|
||||
export type BoardEditor = {
|
||||
@ -37,6 +36,7 @@ export type WireConnection =
|
||||
|
||||
export type ComponentRepo = {
|
||||
defs: [string, ComponentKind][];
|
||||
savedBoards: [string, Board][];
|
||||
};
|
||||
|
||||
export type ComponentKind = {
|
||||
|
||||
@ -16,6 +16,7 @@ export class Sim {
|
||||
const regs = new Array<boolean>(comp.stmts.length).fill(false);
|
||||
|
||||
const stateDependents = new Map<ir.State, number>();
|
||||
const callOutput = new Map<ir.Stmt, boolean[]>();
|
||||
|
||||
const operation = <Ops extends ir.Stmt[]>(
|
||||
action: (...ops: boolean[]) => boolean,
|
||||
@ -65,8 +66,25 @@ export class Sim {
|
||||
case "Or":
|
||||
regs[i] = operation((a, b) => a || b, k.lhs, k.rhs);
|
||||
break;
|
||||
case "Component":
|
||||
throw new Error("not implemented");
|
||||
case "Call": {
|
||||
const outputs = new Array<boolean>(k.comp.outputs).fill(false);
|
||||
new Sim(
|
||||
k.comp,
|
||||
k.inputs.map((stmt) => regs[stmtIdcs.get(stmt)!]),
|
||||
outputs,
|
||||
this.state,
|
||||
).simulate();
|
||||
callOutput.set(stmt, outputs);
|
||||
break;
|
||||
}
|
||||
case "Elem": {
|
||||
const outputs = callOutput.get(k.src);
|
||||
if (!outputs) {
|
||||
throw new Error();
|
||||
}
|
||||
regs[i] = outputs[k.i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("Sim:", i, stmt.kind.tag, inputs, outputs, this.state);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user