Simulate nondeterministic automatons
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { Graph } from './d3/graph.ts';
|
import { Graph } from './d3/graph.ts';
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
transitions: Record<string, number>;
|
transitions: Record<string, number[]>;
|
||||||
start?: boolean;
|
start?: boolean;
|
||||||
accepting?: boolean;
|
accepting?: boolean;
|
||||||
};
|
};
|
||||||
@@ -19,10 +19,12 @@ export function createGraph(states: State[]): Graph {
|
|||||||
}
|
}
|
||||||
for (let i = 0; i < states.length; i++) {
|
for (let i = 0; i < states.length; i++) {
|
||||||
const state = states[i];
|
const state = states[i];
|
||||||
for (const [letter, target] of Object.entries(state.transitions)) {
|
for (const [letter, targets] of Object.entries(state.transitions)) {
|
||||||
|
for (const target of targets) {
|
||||||
graph.createLink(i, target, letter);
|
graph.createLink(i, target, letter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return graph;
|
return graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +45,10 @@ export function createStateList(graph: Graph): State[] {
|
|||||||
const source = reduced[link.source.id];
|
const source = reduced[link.source.id];
|
||||||
const target = reduced[link.target.id];
|
const target = reduced[link.target.id];
|
||||||
for (const label of link.transition.split('')) {
|
for (const label of link.transition.split('')) {
|
||||||
states[source].transitions[label] = target;
|
if (!states[source].transitions[label]) {
|
||||||
|
states[source].transitions[label] = [];
|
||||||
|
}
|
||||||
|
states[source].transitions[label].push(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return states;
|
return states;
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef State
|
* @typedef State
|
||||||
* @property {Object.<string, number>} transitions
|
* @property {Object.<string, number[]>} transitions
|
||||||
* @property {boolean} [start]
|
* @property {boolean} [start]
|
||||||
* @property {boolean} [accepting]
|
* @property {boolean} [accepting]
|
||||||
*/
|
*/
|
||||||
@@ -9,46 +9,46 @@
|
|||||||
* @type {State[]}
|
* @type {State[]}
|
||||||
*/
|
*/
|
||||||
export const STARTS_ENDS_A = [
|
export const STARTS_ENDS_A = [
|
||||||
{ transitions: { a: 1, b: 3 } },
|
{ transitions: { a: [1], b: [3] } },
|
||||||
{ transitions: { a: 2, b: 1 } },
|
{ transitions: { a: [2], b: [1] } },
|
||||||
{ transitions: { a: 2, b: 1 }, accepting: true },
|
{ transitions: { a: [2], b: [1] }, accepting: true },
|
||||||
{ transitions: { a: 3, b: 3 } },
|
{ transitions: { a: [3], b: [3] } },
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {State[]}
|
* @type {State[]}
|
||||||
*/
|
*/
|
||||||
export const STARTS_BB = [
|
export const STARTS_BB = [
|
||||||
{ transitions: { a: 3, b: 1 } },
|
{ transitions: { a: [3], b: [1] } },
|
||||||
{ transitions: { a: 3, b: 2 } },
|
{ transitions: { a: [3], b: [2] } },
|
||||||
{ transitions: { a: 2, b: 2 }, accepting: true },
|
{ transitions: { a: [2], b: [2] }, accepting: true },
|
||||||
{ transitions: { a: 3, b: 3 } },
|
{ transitions: { a: [3], b: [3] } },
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {State[]}
|
* @type {State[]}
|
||||||
*/
|
*/
|
||||||
export const EXACTLY_ONE_B = [
|
export const EXACTLY_ONE_B = [
|
||||||
{ transitions: { a: 0, b: 1 } },
|
{ transitions: { a: [0], b: [1] } },
|
||||||
{ transitions: { a: 1, b: 2 }, accepting: true },
|
{ transitions: { a: [1], b: [2] }, accepting: true },
|
||||||
{ transitions: { a: 2, b: 2 } },
|
{ transitions: { a: [2], b: [2] } },
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {State[]}
|
* @type {State[]}
|
||||||
*/
|
*/
|
||||||
export const ODD_NUMBER_OF_A = [
|
export const ODD_NUMBER_OF_A = [
|
||||||
{ transitions: { a: 1, b: 0 } },
|
{ transitions: { a: [1], b: [0] } },
|
||||||
{ transitions: { a: 0, b: 1 }, accepting: true },
|
{ transitions: { a: [0], b: [1] }, accepting: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {State[]}
|
* @type {State[]}
|
||||||
*/
|
*/
|
||||||
export const ENDS_WITH_TWO_B = [
|
export const ENDS_WITH_TWO_B = [
|
||||||
{ transitions: { a: 0, b: 1 } },
|
{ transitions: { a: [0], b: [1] } },
|
||||||
{ transitions: { a: 0, b: 2 } },
|
{ transitions: { a: [0], b: [2] } },
|
||||||
{ transitions: { a: 0, b: 2 }, accepting: true },
|
{ transitions: { a: [0], b: [2] }, accepting: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const AUTOMATONS = {
|
export const AUTOMATONS = {
|
||||||
|
34
src/fsm.js
34
src/fsm.js
@@ -47,7 +47,7 @@ export function openAutomaton(states, editable = false) {
|
|||||||
*/
|
*/
|
||||||
function updateUIState() {
|
function updateUIState() {
|
||||||
wordInput.value = builder;
|
wordInput.value = builder;
|
||||||
if (state === -1 || states.length === 0 || !states[state].accepting) {
|
if (typeof Array.from(state).find((state) => states[state].accepting) === 'undefined') {
|
||||||
light.classList.remove(IS_VALID);
|
light.classList.remove(IS_VALID);
|
||||||
light.classList.add(IS_INVALID);
|
light.classList.add(IS_INVALID);
|
||||||
} else {
|
} else {
|
||||||
@@ -55,8 +55,10 @@ export function openAutomaton(states, editable = false) {
|
|||||||
light.classList.add(IS_VALID);
|
light.classList.add(IS_VALID);
|
||||||
}
|
}
|
||||||
graph.forEach((node) => node.active = false);
|
graph.forEach((node) => node.active = false);
|
||||||
if (state in graph.nodes) {
|
for (const s of state) {
|
||||||
graph.nodes[state].active = true;
|
if (s in graph.nodes) {
|
||||||
|
graph.nodes[s].active = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
viewer.restart();
|
viewer.restart();
|
||||||
}
|
}
|
||||||
@@ -79,11 +81,16 @@ export function openAutomaton(states, editable = false) {
|
|||||||
* @param {string} letter
|
* @param {string} letter
|
||||||
*/
|
*/
|
||||||
function step(letter) {
|
function step(letter) {
|
||||||
if (state === -1) {
|
/** @type {number[]} */
|
||||||
return;
|
const newStates = [];
|
||||||
}
|
|
||||||
builder += letter;
|
builder += letter;
|
||||||
state = states[state].transitions[letter] ?? -1;
|
for (const s of state) {
|
||||||
|
if (s in states) {
|
||||||
|
const transitions = states[s].transitions[letter];
|
||||||
|
newStates.push(...transitions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state = newStates;
|
||||||
updateUIState();
|
updateUIState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,8 +133,17 @@ export function openAutomaton(states, editable = false) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('./examples.js').State[]} states
|
* @param {import('./examples.js').State[]} states
|
||||||
|
* @returns {number[]}
|
||||||
*/
|
*/
|
||||||
function findStart(states) {
|
function findStart(states) {
|
||||||
let state = states.findIndex(state => state.start);
|
const starts = [];
|
||||||
return Math.max(state, 0);
|
for (let i = 0; i < states.length; i++) {
|
||||||
|
if (states[i].start) {
|
||||||
|
starts.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (starts.length === 0) {
|
||||||
|
starts.push(0);
|
||||||
|
}
|
||||||
|
return starts;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user