mirror of
https://github.com/ParkerTenBroeck/automata.git
synced 2026-06-06 21:24:06 -04:00
simulation
This commit is contained in:
parent
22ef009122
commit
c7309a75d9
2 changed files with 146 additions and 67 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import { updateVisualization } from "./visualizer.ts";
|
import { network, updateVisualization } from "./visualizer.ts";
|
||||||
|
|
||||||
export type Machine = Fa | Pda | Tm;
|
export type Machine = Fa | Pda | Tm;
|
||||||
|
|
||||||
|
|
@ -203,21 +203,58 @@ export type Tm = {
|
||||||
edges: Map<string, Edge[]>;
|
edges: Map<string, Edge[]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FaState = {
|
export type SimStepResult = "pending" | "accept" | "reject";
|
||||||
state: State;
|
|
||||||
position: number;
|
export class FaState {
|
||||||
|
readonly state: State;
|
||||||
|
|
||||||
|
readonly position: number;
|
||||||
|
readonly input: string;
|
||||||
|
readonly accepted: boolean = false;
|
||||||
|
private repr!: string;
|
||||||
|
|
||||||
|
constructor(state: State, position: number, input: string){
|
||||||
|
this.state=state;
|
||||||
|
this.position=position;
|
||||||
|
this.input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string{
|
||||||
|
if(!this.repr) this.repr = this.state + " " + this.position;
|
||||||
|
return this.repr
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export class FaSim {
|
export class FaSim {
|
||||||
step(): string {
|
|
||||||
return "";
|
current_states: Map<string, FaState[]> = new Map();
|
||||||
|
accepted: FaState[] = []
|
||||||
|
|
||||||
|
step(): SimStepResult {
|
||||||
|
return "pending";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PdaState = {
|
export class PdaState {
|
||||||
state: State;
|
readonly state: State;
|
||||||
stack: Symbol[];
|
readonly stack: Symbol[];
|
||||||
position: number;
|
|
||||||
|
readonly position: number;
|
||||||
|
readonly input: string;
|
||||||
|
readonly accepted: boolean = false
|
||||||
|
private repr!: string;
|
||||||
|
|
||||||
|
constructor(state: State, stack: Symbol[], position: number, input: string){
|
||||||
|
this.state=state;
|
||||||
|
this.stack=stack;
|
||||||
|
this.position=position;
|
||||||
|
this.input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string{
|
||||||
|
if(!this.repr) this.repr = this.state + " [" + this.stack + "]" + " " + this.position;
|
||||||
|
return this.repr
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export class PdaSim {
|
export class PdaSim {
|
||||||
|
|
@ -225,28 +262,43 @@ export class PdaSim {
|
||||||
paths: PdaState[];
|
paths: PdaState[];
|
||||||
input: string;
|
input: string;
|
||||||
|
|
||||||
|
current_states: Map<string, PdaState[]> = new Map();
|
||||||
|
accepted: PdaState[] = []
|
||||||
|
|
||||||
constructor(machine: Pda, input: string) {
|
constructor(machine: Pda, input: string) {
|
||||||
this.machine = machine;
|
this.machine = machine;
|
||||||
this.paths = [{
|
this.paths = [new PdaState(machine.initial_state, [machine.initial_stack], 0, input)];
|
||||||
state: machine.initial_state,
|
this.current_states.set(machine.initial_state, [this.paths[0]])
|
||||||
stack: [machine.initial_stack],
|
|
||||||
position: 0,
|
|
||||||
}];
|
|
||||||
this.input = input;
|
this.input = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
step(): string {
|
step(): SimStepResult {
|
||||||
const paths = [];
|
if (this.paths.length == 0) return "reject";
|
||||||
console.log(this.paths);
|
if (this.accepted.length != 0) return "accept";
|
||||||
|
|
||||||
|
const paths: PdaState[] = [];
|
||||||
|
this.current_states.clear();
|
||||||
|
|
||||||
|
const push = (state: PdaState) => {
|
||||||
|
paths.push(state);
|
||||||
|
if (!this.current_states.has(state.state)) this.current_states.set(state.state, []);
|
||||||
|
this.current_states.get(state.state)?.push(state);
|
||||||
|
|
||||||
|
if (
|
||||||
|
state.position == this.input.length && this.machine.final_states &&
|
||||||
|
this.machine.final_states.has(state.state)
|
||||||
|
||
|
||||||
|
state.position == this.input.length && !this.machine.final_states &&
|
||||||
|
state.stack.length == 1 && state.stack[0] == this.machine.initial_stack
|
||||||
|
) {
|
||||||
|
|
||||||
|
// @ts-expect-error sillllyyyy
|
||||||
|
state.accepted = true
|
||||||
|
this.accepted.push(state);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for (const path of 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 stack = path.stack.pop()!;
|
||||||
const letter_map = this.machine.transitions_components.get(path.state)
|
const letter_map = this.machine.transitions_components.get(path.state)
|
||||||
|
|
@ -254,11 +306,7 @@ export class PdaSim {
|
||||||
if (!letter_map) continue;
|
if (!letter_map) continue;
|
||||||
|
|
||||||
for (const to of letter_map.get(null) ?? []) {
|
for (const to of letter_map.get(null) ?? []) {
|
||||||
paths.push({
|
push(new PdaState(to.state, path.stack.concat(to.stack), path.position, this.input));
|
||||||
state: to.state,
|
|
||||||
position: path.position,
|
|
||||||
stack: path.stack.concat(to.stack),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.position >= this.input.length) continue;
|
if (path.position >= this.input.length) continue;
|
||||||
|
|
@ -266,19 +314,51 @@ export class PdaSim {
|
||||||
const char = this.input.charAt(path.position);
|
const char = this.input.charAt(path.position);
|
||||||
|
|
||||||
for (const to of letter_map.get(char) ?? []) {
|
for (const to of letter_map.get(char) ?? []) {
|
||||||
paths.push({
|
push(new PdaState(to.state, path.stack.concat(to.stack), path.position+1, this.input));
|
||||||
state: to.state,
|
|
||||||
position: path.position + 1,
|
|
||||||
stack: path.stack.concat(to.stack),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.paths = paths;
|
this.paths = paths;
|
||||||
return paths.length == 0 ? "reject" : "pending";
|
|
||||||
|
|
||||||
|
if (this.paths.length == 0) return "reject";
|
||||||
|
if (this.accepted.length != 0) return "accept";
|
||||||
|
return "pending"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Sim = FaSim | PdaSim | null
|
export class TmState{
|
||||||
|
readonly state: State;
|
||||||
|
readonly tape: Symbol[];
|
||||||
|
|
||||||
|
readonly position: number;
|
||||||
|
readonly input: string;
|
||||||
|
readonly accepted: boolean = false
|
||||||
|
private repr!: string;
|
||||||
|
|
||||||
|
constructor(state: State, tape: Symbol[], position: number, input: string){
|
||||||
|
this.state=state;
|
||||||
|
this.tape = tape;
|
||||||
|
this.position=position;
|
||||||
|
this.input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string{
|
||||||
|
if(!this.repr) this.repr = this.state + " " + this.position;
|
||||||
|
return this.repr
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TmSim {
|
||||||
|
current_states: Map<string, TmState[]> = new Map();
|
||||||
|
accepted: TmState[] = []
|
||||||
|
|
||||||
|
step(): SimStepResult {
|
||||||
|
return "pending"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Sim = FaSim | PdaSim | TmSim | null
|
||||||
export let sim: Sim = null;
|
export let sim: Sim = null;
|
||||||
|
|
||||||
export let automaton: Machine = {
|
export let automaton: Machine = {
|
||||||
|
|
@ -298,6 +378,7 @@ export function clearSimulation(){
|
||||||
|
|
||||||
export function setSimulation(sim_: Sim){
|
export function setSimulation(sim_: Sim){
|
||||||
sim = sim_;
|
sim = sim_;
|
||||||
|
network.redraw()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setAutomaton(auto: Machine) {
|
export function setAutomaton(auto: Machine) {
|
||||||
|
|
@ -323,6 +404,7 @@ export function stepSimulation(): void {
|
||||||
if (sim) {
|
if (sim) {
|
||||||
console.log(sim.step());
|
console.log(sim.step());
|
||||||
}
|
}
|
||||||
|
network.redraw()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetSimulation(): void {
|
export function resetSimulation(): void {
|
||||||
|
|
@ -330,7 +412,7 @@ export function resetSimulation(): void {
|
||||||
case "fa":
|
case "fa":
|
||||||
break;
|
break;
|
||||||
case "pda":
|
case "pda":
|
||||||
setSimulation(new PdaSim(automaton as Pda, "aabb"));
|
setSimulation(new PdaSim(automaton as Pda, "aabbaabbaa"));
|
||||||
break;
|
break;
|
||||||
case "tm":
|
case "tm":
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@
|
||||||
|
|
||||||
// deno-lint-ignore no-import-prefix
|
// deno-lint-ignore no-import-prefix
|
||||||
import * as vis from "npm:vis-network/standalone";
|
import * as vis from "npm:vis-network/standalone";
|
||||||
import { StateEffect } from "npm:@codemirror/state";
|
import { automaton, setAutomaton, sim } from "./automata.ts";
|
||||||
import { automaton, Machine, setAutomaton } from "./automata.ts";
|
|
||||||
import { getText } from "./editor.ts";
|
|
||||||
|
|
||||||
export const nodes = new vis.DataSet<vis.Node>();
|
export const nodes = new vis.DataSet<vis.Node>();
|
||||||
export const edges = new vis.DataSet<vis.Edge>();
|
export const edges = new vis.DataSet<vis.Edge>();
|
||||||
|
|
@ -298,7 +296,7 @@ function renderNode({
|
||||||
const isFinal = automaton.final_states
|
const isFinal = automaton.final_states
|
||||||
? automaton.final_states.has(id)
|
? automaton.final_states.has(id)
|
||||||
: false;
|
: false;
|
||||||
const isActive = false;
|
const isActive = sim?sim.current_states.has(id):false;
|
||||||
|
|
||||||
const fill = selected ? t.bg_2 : hover ? t.bg_1 : t.bg_0;
|
const fill = selected ? t.bg_2 : hover ? t.bg_1 : t.bg_0;
|
||||||
const stroke = isActive ? t.current_node_border : t.node_border;
|
const stroke = isActive ? t.current_node_border : t.node_border;
|
||||||
|
|
@ -340,34 +338,33 @@ function renderNode({
|
||||||
drawInitialArrow(ctx, x, y, r, t.edge);
|
drawInitialArrow(ctx, x, y, r, t.edge);
|
||||||
}
|
}
|
||||||
|
|
||||||
// const badgeText = "bleh\npee";
|
if (isActive) {
|
||||||
// if (badgeText) {
|
const paths = sim?.current_states.get(id)!;
|
||||||
// const lines = badgeText.split("\n").slice(0, 3);
|
const padX = 8;
|
||||||
// const padX = 8;
|
const padY = 6;
|
||||||
// const padY = 6;
|
const lineH = 14;
|
||||||
// const lineH = 14;
|
|
||||||
|
|
||||||
// let w = 0;
|
let w = 0;
|
||||||
// for (const ln of lines) w = Math.max(w, ctx.measureText(ln).width);
|
for (const ln of paths) w = Math.max(w, ctx.measureText(ln.toString()).width);
|
||||||
// const boxW = w + padX * 2;
|
const boxW = w + padX * 2;
|
||||||
// const boxH = lines.length * lineH + padY * 2;
|
const boxH = paths.length * lineH + padY * 2;
|
||||||
|
|
||||||
// const bx = x - boxW / 2;
|
const bx = x - boxW / 2;
|
||||||
// const by = y - r - 12 - boxH;
|
const by = y - r - 12 - boxH;
|
||||||
|
|
||||||
// ctx.fillStyle = t.bg_1;
|
ctx.fillStyle = t.bg_1;
|
||||||
// ctx.strokeStyle = t.bg_2;
|
ctx.strokeStyle = t.bg_2;
|
||||||
// ctx.lineWidth = 1;
|
ctx.lineWidth = 1;
|
||||||
// roundRect(ctx, bx, by, boxW, boxH, 8);
|
roundRect(ctx, bx, by, boxW, boxH, 8);
|
||||||
// ctx.fill();
|
ctx.fill();
|
||||||
// ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
// ctx.fillStyle = t.fg_0;
|
ctx.textBaseline = "top";
|
||||||
// ctx.textBaseline = "top";
|
for (let i = 0; i < paths.length; i++) {
|
||||||
// for (let i = 0; i < lines.length; i++) {
|
ctx.fillStyle = paths[i].accepted?t.current_node_border:t.fg_0;
|
||||||
// ctx.fillText(lines[i], x, by + padY + i * lineH);
|
ctx.fillText(paths[i].toString(), x, by + padY + i * lineH);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
const node: vis.Node = nodes.get(id)!;
|
const node: vis.Node = nodes.get(id)!;
|
||||||
const physicsOff = node.physics === false;
|
const physicsOff = node.physics === false;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue