mirror of
https://github.com/ParkerTenBroeck/automata.git
synced 2026-06-07 05:28:45 -04:00
started adding simulators
This commit is contained in:
parent
61d7edd929
commit
22ef009122
5 changed files with 190 additions and 68 deletions
|
|
@ -1,3 +1,5 @@
|
|||
import { updateVisualization } from "./visualizer.ts";
|
||||
|
||||
export type Machine = Fa | Pda | Tm;
|
||||
|
||||
export function machine_from_json(json: string): Machine {
|
||||
|
|
@ -26,16 +28,16 @@ export function machine_from_json(json: string): Machine {
|
|||
for (const [from, tos] of machine.transitions) {
|
||||
for (const to of tos) {
|
||||
const layer_0 = machine.transitions_components;
|
||||
if(!layer_0.has(from.state)) layer_0.set(from.state, new Map());
|
||||
if (!layer_0.has(from.state)) layer_0.set(from.state, new Map());
|
||||
const layer_1 = machine.transitions_components.get(from.state)!;
|
||||
if(!layer_1.has(from.letter)) layer_1.set(from.letter, []);
|
||||
if (!layer_1.has(from.letter)) layer_1.set(from.letter, []);
|
||||
const layer_2 = layer_1.get(from.letter)!;
|
||||
layer_2.push(to);
|
||||
|
||||
const edge = from.state + "#" + to.state;
|
||||
if (!machine.edges.has(edge)) machine.edges.set(edge, []);
|
||||
machine.edges.get(edge)?.push({
|
||||
repr: from.letter?from.letter:"ε",
|
||||
repr: from.letter ? from.letter : "ε",
|
||||
function: to.function,
|
||||
transition: to.transition,
|
||||
});
|
||||
|
|
@ -49,18 +51,19 @@ export function machine_from_json(json: string): Machine {
|
|||
for (const [from, tos] of machine.transitions) {
|
||||
for (const to of tos) {
|
||||
const layer_0 = machine.transitions_components;
|
||||
if(!layer_0.has(from.state)) layer_0.set(from.state, new Map());
|
||||
if (!layer_0.has(from.state)) layer_0.set(from.state, new Map());
|
||||
const layer_1 = machine.transitions_components.get(from.state)!;
|
||||
if(!layer_1.has(from.symbol)) layer_1.set(from.symbol, new Map());
|
||||
if (!layer_1.has(from.symbol)) layer_1.set(from.symbol, new Map());
|
||||
const layer_2 = layer_1.get(from.symbol)!;
|
||||
if(!layer_2.has(from.letter)) layer_2.set(from.letter, []);
|
||||
if (!layer_2.has(from.letter)) layer_2.set(from.letter, []);
|
||||
const layer_3 = layer_2.get(from.letter)!;
|
||||
layer_3.push(to);
|
||||
|
||||
const edge = from.state + "#" + to.state;
|
||||
if (!machine.edges.has(edge)) machine.edges.set(edge, []);
|
||||
machine.edges.get(edge)?.push({
|
||||
repr: (from.letter?from.letter:"ε")+","+from.symbol+"->["+to.stack+"]",
|
||||
repr: (from.letter ? from.letter : "ε") + "," + from.symbol +
|
||||
"->[" + to.stack + "]",
|
||||
function: to.function,
|
||||
transition: to.transition,
|
||||
});
|
||||
|
|
@ -74,16 +77,16 @@ export function machine_from_json(json: string): Machine {
|
|||
for (const [from, tos] of machine.transitions) {
|
||||
for (const to of tos) {
|
||||
const layer_0 = machine.transitions_components;
|
||||
if(!layer_0.has(from.state)) layer_0.set(from.state, new Map());
|
||||
if (!layer_0.has(from.state)) layer_0.set(from.state, new Map());
|
||||
const layer_1 = machine.transitions_components.get(from.state)!;
|
||||
if(!layer_1.has(from.symbol)) layer_1.set(from.symbol, []);
|
||||
if (!layer_1.has(from.symbol)) layer_1.set(from.symbol, []);
|
||||
const layer_2 = layer_1.get(from.symbol)!;
|
||||
layer_2.push(to);
|
||||
|
||||
const edge = from.state + "#" + to.state;
|
||||
if (!machine.edges.has(edge)) machine.edges.set(edge, []);
|
||||
machine.edges.get(edge)?.push({
|
||||
repr: from.symbol+"->"+to.symbol+","+to.direction,
|
||||
repr: from.symbol + "->" + to.symbol + "," + to.direction,
|
||||
function: to.function,
|
||||
transition: to.transition,
|
||||
});
|
||||
|
|
@ -107,7 +110,7 @@ export type SymbolInfo = { definition: Span };
|
|||
|
||||
export type FaTransFrom = {
|
||||
state: State;
|
||||
letter: Letter|null;
|
||||
letter: Letter | null;
|
||||
};
|
||||
|
||||
export type FaTransTo = {
|
||||
|
|
@ -132,14 +135,14 @@ export type Fa = {
|
|||
final_states: Map<State, StateInfo>;
|
||||
|
||||
transitions: Map<FaTransFrom, FaTransTo[]>;
|
||||
transitions_components: Map<State, Map<Letter|null, FaTransTo[]>>;
|
||||
transitions_components: Map<State, Map<Letter | null, FaTransTo[]>>;
|
||||
|
||||
edges: Map<string, Edge[]>;
|
||||
};
|
||||
|
||||
export type PdaTransFrom = {
|
||||
state: State;
|
||||
letter: Letter|null;
|
||||
letter: Letter | null;
|
||||
symbol: Symbol;
|
||||
};
|
||||
|
||||
|
|
@ -162,7 +165,10 @@ export type Pda = {
|
|||
final_states: Map<State, StateInfo> | null;
|
||||
|
||||
transitions: Map<PdaTransFrom, PdaTransTo[]>;
|
||||
transitions_components: Map<State, Map<Symbol, Map<Letter|null, PdaTransTo[]>>>;
|
||||
transitions_components: Map<
|
||||
State,
|
||||
Map<Symbol, Map<Letter | null, PdaTransTo[]>>
|
||||
>;
|
||||
|
||||
edges: Map<string, Edge[]>;
|
||||
};
|
||||
|
|
@ -197,12 +203,136 @@ export type Tm = {
|
|||
edges: Map<string, Edge[]>;
|
||||
};
|
||||
|
||||
|
||||
export type FaState = {
|
||||
state: State,
|
||||
position: number
|
||||
state: State;
|
||||
position: number;
|
||||
};
|
||||
|
||||
export class FaSim {
|
||||
step(): string {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export class FaSim{
|
||||
export type PdaState = {
|
||||
state: State;
|
||||
stack: Symbol[];
|
||||
position: number;
|
||||
};
|
||||
|
||||
}
|
||||
export class PdaSim {
|
||||
machine: Pda;
|
||||
paths: PdaState[];
|
||||
input: string;
|
||||
|
||||
constructor(machine: Pda, input: string) {
|
||||
this.machine = machine;
|
||||
this.paths = [{
|
||||
state: machine.initial_state,
|
||||
stack: [machine.initial_stack],
|
||||
position: 0,
|
||||
}];
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
step(): string {
|
||||
const paths = [];
|
||||
console.log(this.paths);
|
||||
for (const path of this.paths) {
|
||||
if (
|
||||
path.position == this.input.length && this.machine.final_states &&
|
||||
this.machine.final_states.has(path.state)
|
||||
) return "accept";
|
||||
if (
|
||||
path.position == this.input.length && !this.machine.final_states &&
|
||||
path.stack.length == 1 && path.stack[0] == this.machine.initial_stack
|
||||
) return "accept";
|
||||
|
||||
const stack = path.stack.pop()!;
|
||||
const letter_map = this.machine.transitions_components.get(path.state)
|
||||
?.get(stack);
|
||||
if (!letter_map) continue;
|
||||
|
||||
for (const to of letter_map.get(null) ?? []) {
|
||||
paths.push({
|
||||
state: to.state,
|
||||
position: path.position,
|
||||
stack: path.stack.concat(to.stack),
|
||||
});
|
||||
}
|
||||
|
||||
if (path.position >= this.input.length) continue;
|
||||
|
||||
const char = this.input.charAt(path.position);
|
||||
|
||||
for (const to of letter_map.get(char) ?? []) {
|
||||
paths.push({
|
||||
state: to.state,
|
||||
position: path.position + 1,
|
||||
stack: path.stack.concat(to.stack),
|
||||
});
|
||||
}
|
||||
}
|
||||
this.paths = paths;
|
||||
return paths.length == 0 ? "reject" : "pending";
|
||||
}
|
||||
}
|
||||
|
||||
export type Sim = FaSim | PdaSim | null
|
||||
export let sim: Sim = null;
|
||||
|
||||
export let automaton: Machine = {
|
||||
type: "fa",
|
||||
alphabet: new Map(),
|
||||
final_states: new Map(),
|
||||
initial_state: "",
|
||||
states: new Map(),
|
||||
transitions: new Map(),
|
||||
transitions_components: new Map(),
|
||||
edges: new Map(),
|
||||
};
|
||||
|
||||
export function clearSimulation(){
|
||||
setSimulation(null);
|
||||
}
|
||||
|
||||
export function setSimulation(sim_: Sim){
|
||||
sim = sim_;
|
||||
}
|
||||
|
||||
export function setAutomaton(auto: Machine) {
|
||||
automaton = auto;
|
||||
sim = null;
|
||||
updateVisualization()
|
||||
}
|
||||
|
||||
export function clearAutomaton() {
|
||||
setAutomaton({
|
||||
type: "fa",
|
||||
alphabet: new Map(),
|
||||
final_states: new Map(),
|
||||
initial_state: "",
|
||||
states: new Map(),
|
||||
transitions: new Map(),
|
||||
transitions_components: new Map(),
|
||||
edges: new Map(),
|
||||
});
|
||||
}
|
||||
|
||||
export function stepSimulation(): void {
|
||||
if (sim) {
|
||||
console.log(sim.step());
|
||||
}
|
||||
}
|
||||
|
||||
export function resetSimulation(): void {
|
||||
switch (automaton.type) {
|
||||
case "fa":
|
||||
break;
|
||||
case "pda":
|
||||
setSimulation(new PdaSim(automaton as Pda, "aabb"));
|
||||
break;
|
||||
case "tm":
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { resetSimulation, stepSimulation } from "./automata.ts";
|
||||
import {nodes, edges, network} from "./visualizer.ts"
|
||||
|
||||
const togglePhysicsBtn = document.getElementById("togglePhysics") as HTMLButtonElement;
|
||||
|
|
@ -9,14 +10,6 @@ const speedLabel = document.getElementById("speedLabel") as HTMLSpanEle
|
|||
const resetSimBtn = document.getElementById("resetSim") as HTMLButtonElement;
|
||||
|
||||
|
||||
function stepSimulation(): void {
|
||||
console.log("step");
|
||||
}
|
||||
|
||||
function resetSimulation(): void {
|
||||
console.log("reset");
|
||||
}
|
||||
|
||||
// ---- Physics toggle (styled label) ----
|
||||
function setPhysicsButtonUI(enabled: boolean) {
|
||||
togglePhysicsBtn.classList.toggle("active", enabled);
|
||||
|
|
@ -106,12 +99,7 @@ stepBtn.onclick = () => {
|
|||
};
|
||||
|
||||
resetSimBtn.onclick = () => {
|
||||
// Stop if running
|
||||
if (running) setRunning(false);
|
||||
|
||||
// Reset
|
||||
resetSimulation();
|
||||
|
||||
// Optional: re-enable Step after reset
|
||||
stepBtn.disabled = false;
|
||||
};
|
||||
|
|
@ -20,8 +20,7 @@ import { closeBrackets } from "npm:@codemirror/autocomplete";
|
|||
import wasm from "./wasm.ts"
|
||||
import { terminalPlugin } from "./terminal.ts";
|
||||
|
||||
import { setAutomaton } from "./visualizer.ts";
|
||||
import { machine_from_json } from "./automata.ts";
|
||||
import { machine_from_json, setAutomaton } from "./automata.ts";
|
||||
import { sharedText } from "./share.ts";
|
||||
import { examples } from "./examples.ts";
|
||||
|
||||
|
|
|
|||
|
|
@ -104,9 +104,40 @@ d(qeq, b, z0) = (qmb, z0)
|
|||
d(qmb, b, z0) = (qmb, z0)`,
|
||||
),
|
||||
|
||||
|
||||
new Example(
|
||||
"NPDA",
|
||||
"unequal",
|
||||
"palindrome",
|
||||
`type=NPDA
|
||||
Q = {q0, q1} // states
|
||||
E = {a, b} // alphabet
|
||||
T = {z0, A, B} // stack
|
||||
q0 = q0
|
||||
z0 = z0
|
||||
|
||||
// push letters we see to stack
|
||||
d(q0, a, z0) = (q0, [A z0])
|
||||
d(q0, b, z0) = (q0, [B z0])
|
||||
|
||||
d(q0, a, A) = (q0, [A A])
|
||||
d(q0, b, A) = (q0, [B A])
|
||||
|
||||
d(q0, a, B) = (q0, [A B])
|
||||
d(q0, b, B) = (q0, [B B])
|
||||
|
||||
// transition to q1
|
||||
d(q0, epsilon, z0) = { (q1, z0) }
|
||||
d(q0, epsilon, A) = { (q1, A) }
|
||||
d(q0, epsilon, B) = { (q1, B) }
|
||||
|
||||
// consume stack until empty
|
||||
d(q1, a, A) = { (q1, epsilon) }
|
||||
d(q1, b, B) = { (q1, epsilon) }`,
|
||||
),
|
||||
|
||||
new Example(
|
||||
"NPDA",
|
||||
"kleen star stack",
|
||||
`type=NPDA
|
||||
Q = {q0, q1} // states
|
||||
E = {a, b} // alphabet
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// deno-lint-ignore no-import-prefix
|
||||
import * as vis from "npm:vis-network/standalone";
|
||||
import { StateEffect } from "npm:@codemirror/state";
|
||||
import { Machine } from "./automata.ts";
|
||||
import { automaton, Machine, setAutomaton } from "./automata.ts";
|
||||
import { getText } from "./editor.ts";
|
||||
|
||||
export const nodes = new vis.DataSet<vis.Node>();
|
||||
|
|
@ -125,30 +125,6 @@ export function updateGraphTheme() {
|
|||
setAutomaton(automaton)
|
||||
}
|
||||
|
||||
let automaton: Machine = {
|
||||
type: "fa",
|
||||
alphabet: new Map(),
|
||||
final_states: new Map(),
|
||||
initial_state: "",
|
||||
states: new Map(),
|
||||
transitions: new Map(),
|
||||
transitions_components: new Map(),
|
||||
edges: new Map(),
|
||||
};
|
||||
|
||||
export function clearAutomaton() {
|
||||
automaton = {
|
||||
type: "fa",
|
||||
alphabet: new Map(),
|
||||
final_states: new Map(),
|
||||
initial_state: "",
|
||||
states: new Map(),
|
||||
transitions: new Map(),
|
||||
transitions_components: new Map(),
|
||||
edges: new Map(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
let _measureCanvas: HTMLCanvasElement | null = null;
|
||||
|
||||
|
|
@ -163,9 +139,7 @@ export function measureTextWidth(text: string, font: string): number {
|
|||
return ctx.measureText(text).width;
|
||||
}
|
||||
|
||||
export function setAutomaton(auto: Machine) {
|
||||
automaton = auto;
|
||||
|
||||
export function updateVisualization() {
|
||||
// Populate nodes
|
||||
for (const state of automaton.states.keys()) {
|
||||
|
||||
|
|
@ -186,7 +160,7 @@ export function setAutomaton(auto: Machine) {
|
|||
}
|
||||
|
||||
// Populate edges
|
||||
for (const [edge_id, transitions] of auto.edges) {
|
||||
for (const [edge_id, transitions] of automaton.edges) {
|
||||
const to_from = edge_id.split("#");
|
||||
const vadjust = -getGraphTheme().edge_font_size *
|
||||
Math.floor(transitions.length / 2);
|
||||
|
|
@ -202,7 +176,7 @@ export function setAutomaton(auto: Machine) {
|
|||
font,
|
||||
from: to_from[0],
|
||||
to: to_from[1],
|
||||
label: transitions.map(i => i.repr).join(auto.type=="fa"?",":"\n"),
|
||||
label: transitions.map(i => i.repr).join(automaton.type=="fa"?",":"\n"),
|
||||
});
|
||||
} else {
|
||||
edges.add({
|
||||
|
|
@ -210,19 +184,19 @@ export function setAutomaton(auto: Machine) {
|
|||
font,
|
||||
from: to_from[0],
|
||||
to: to_from[1],
|
||||
label: transitions.map(i => i.repr).join(auto.type=="fa"?",":"\n"),
|
||||
label: transitions.map(i => i.repr).join(automaton.type=="fa"?",":"\n"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const edge_id of edges.getIds()) {
|
||||
if (!auto.edges.has(edge_id as string)) {
|
||||
if (!automaton.edges.has(edge_id as string)) {
|
||||
edges.remove(edge_id);
|
||||
}
|
||||
}
|
||||
|
||||
for (const node_id of nodes.getIds()) {
|
||||
if (!auto.states.has(node_id as string)) {
|
||||
if (!automaton.states.has(node_id as string)) {
|
||||
nodes.remove(node_id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue