Skip to content

Commit a2136fd

Browse files
committed
add support to AFND
1 parent ce8932e commit a2136fd

File tree

5 files changed

+106
-134
lines changed

5 files changed

+106
-134
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ npm run dev or yarn dev
1818

1919
## ToDo
2020

21-
- [ ] Support to AFND and AFNλ
21+
- [x] Support to AFND and AFNλ
2222
- [ ] Generate transition table
2323
- [ ] Generate automata from the transition table and viceversa
2424
- [ ] Run step by step

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ <h2 class="text-2xl font-bold mb-2">ToDo</h2>
177177

178178
<ul class="ml-2">
179179
<li>
180-
<i class="fa-regular fa-square-check"></i> Support to AFND and AFN λ
180+
<i class="fa-solid fa-square-check"></i> Support to AFND and AFN λ
181181
</li>
182182
<li>
183183
<i class="fa-regular fa-square-check"></i> Generate transition table

src/afnd.js

Lines changed: 47 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,68 @@
11
import { animateNode, renderError, renderOut } from "./animateNode.js";
22

33
function verifyAFND(paper, graph, automata, string) {
4-
// function epsilonClosure(states) {
5-
// const epsilonClosureStates = new Set(states);
6-
// const stack = Array.from(states);
7-
8-
// while (stack.length > 0) {
9-
// const state = stack.pop();
10-
11-
// if (automata.transitions[state] && automata.transitions[state]["ε"]) {
12-
// for (const nextState of automata.transitions[state]["ε"]) {
13-
// if (!epsilonClosureStates.has(nextState)) {
14-
// epsilonClosureStates.add(nextState);
15-
// stack.push(nextState);
16-
// }
17-
// }
18-
// }
19-
// }
20-
21-
// return epsilonClosureStates;
22-
// }
23-
24-
// function move(states, symbol) {
25-
// const nextStates = new Set();
26-
27-
// for (const state of states) {
28-
// setTimeout(() => {
29-
// animateNode(
30-
// paper,
31-
// graph,
32-
// state,
33-
// symbol,
34-
// automata.finalStates.includes(state)
35-
// );
36-
37-
// if (
38-
// automata.transitions[state] &&
39-
// automata.transitions[state][symbol]
40-
// ) {
41-
// for (const nextState of automata.transitions[state][symbol]) {
42-
// nextStates.add(nextState);
43-
// }
44-
// }
45-
// }, 1000);
46-
// }
47-
// return nextStates;
48-
// }
49-
50-
// let i = 0;
51-
// let currentStates = epsilonClosure(new Set([automata.initialState]));
52-
// let symbol = string[i];
53-
54-
// const interval = setInterval(() => {
55-
// currentStates = epsilonClosure(move(currentStates, symbol));
56-
57-
// if (i >= string.length) {
58-
// for (const state of currentStates) {
59-
// if (automata.finalStates.includes(state)) {
60-
// console.log("YES");
61-
// return true;
62-
// }
63-
// }
64-
65-
// clearInterval(interval);
66-
// }
67-
68-
// i++;
69-
// symbol = string[i];
70-
// }, 1000);
71-
72-
const getNextStates = (states, symbol) => {
73-
const nextStates = new Set();
74-
75-
for (const state of states) {
76-
if (automata.transitions[state] && automata.transitions[state][symbol]) {
77-
for (const nextState of automata.transitions[state][symbol]) {
78-
nextStates.add(nextState);
4+
const steps = [];
5+
6+
const verify = (afd, currentState, string) => {
7+
if (string.length <= 0) {
8+
return afd.finalStates.includes(currentState);
9+
}
10+
11+
for (let i = 0; i < afd.transitions[currentState].length; i++) {
12+
const [nextState, symbol] = afd.transitions[currentState][i];
13+
14+
if (symbol === string[0]) {
15+
steps.push([currentState, symbol, nextState, string[0]]);
16+
17+
if (verify(afd, nextState, string.slice(1))) {
18+
return true;
19+
}
20+
} else if (symbol === "λ") {
21+
steps.push([currentState, symbol, nextState, string[0]]);
22+
23+
if (verify(afd, nextState, string)) {
24+
return true;
7925
}
8026
}
8127
}
8228

83-
return nextStates;
29+
return false;
8430
};
8531

86-
let currentStates = [automata.initialState];
87-
88-
const verify = (states, symbol) => {
89-
let i = 0;
90-
let state = states[0];
32+
const res = verify(automata, automata.initialState, string);
9133

92-
const interval = setInterval(() => {
93-
animateNode(
94-
paper,
95-
graph,
96-
state,
97-
symbol,
98-
automata.finalStates.includes(state)
99-
);
34+
let indexStep = 0;
35+
document.querySelector("#string-out").textContent = "";
10036

101-
if (i >= string.length) {
102-
clearInterval(interval);
103-
return;
37+
const interval = setInterval(() => {
38+
if (indexStep >= steps.length) {
39+
if (!res) {
40+
renderOut("INVALID");
41+
} else {
42+
renderOut("VALID");
10443
}
10544

106-
states = getNextStates(states, symbol);
107-
108-
if (states.length <= 0) {
109-
return;
110-
}
45+
clearInterval(interval);
46+
return;
47+
}
11148

112-
console.log("Current: ", states);
49+
const step = steps[indexStep];
11350

114-
i++;
115-
symbol = string[i];
51+
animateNode(
52+
paper,
53+
graph,
54+
step[0],
55+
step[1],
56+
automata.finalStates.includes(step[0]),
57+
step[2],
58+
);
11659

117-
verify(states, symbol);
118-
}, 1000);
119-
};
60+
document.querySelector("#string-out").textContent += step[3];
12061

121-
verify(currentStates, string[0]);
62+
indexStep++;
63+
}, 1000);
12264

123-
return false;
65+
return res;
12466
}
12567

12668
export { verifyAFND };

src/animateNode.js

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ function animateNode(
66
state = "q0",
77
symbol,
88
isFinal,
9-
color = "#f8d6ac"
9+
target,
10+
color = "#f8d6ac",
1011
) {
12+
const elements = paper.model.getElements();
13+
1114
const data = paper.model
1215
.getElements()
1316
.find((el) => el.attributes.attrs.label.text === state);
@@ -17,8 +20,12 @@ function animateNode(
1720

1821
const links = graph.getConnectedLinks(data, { outbound: true });
1922

23+
const nodeTarget = elements.find((el) =>
24+
el.attributes.attrs.label.text === target
25+
);
2026
const currentLink = links.find((el) =>
21-
el.attributes.labels[0].attrs.text.text.split(",").includes(symbol)
27+
el.attributes.labels[0].attrs.text.text.split(",").includes(symbol) &&
28+
el.attributes.target.id === nodeTarget.id
2229
);
2330

2431
if (currentLink) {
@@ -50,28 +57,30 @@ function renderOut(value) {
5057
document.querySelector("#out").innerHTML = `
5158
<div class="animate__fadeIn">
5259
${
53-
value === "VALID"
54-
? `<i class="fa-solid fa-circle-check text-sm text-green-500"></i>`
55-
: value === "INVALID"
56-
? `<i class="fa-solid fa-circle-exclamation text-sm text-red-500"></i>`
57-
: ""
58-
}
60+
value === "VALID"
61+
? `<i class="fa-solid fa-circle-check text-sm text-green-500"></i>`
62+
: value === "INVALID"
63+
? `<i class="fa-solid fa-circle-exclamation text-sm text-red-500"></i>`
64+
: ""
65+
}
5966
<span class="${
60-
value === "VALID"
61-
? "text-green-400"
62-
: value === "INVALID"
63-
? "text-red-400"
64-
: ""
65-
}">${value}</span>
67+
value === "VALID"
68+
? "text-green-400"
69+
: value === "INVALID"
70+
? "text-red-400"
71+
: ""
72+
}">${value}</span>
6673
</div>
6774
`;
6875
}
6976

7077
function renderOutString(value) {
71-
document.querySelector("#string-out").innerHTML = `${value
72-
.split("")
73-
.map((el) => `<span class='symbol'>${el}</span>`)
74-
.join("")}`;
78+
document.querySelector("#string-out").innerHTML = `${
79+
value
80+
.split("")
81+
.map((el) => `<span class='symbol'>${el}</span>`)
82+
.join("")
83+
}`;
7584
}
7685

7786
function renderError(error) {
@@ -88,4 +97,4 @@ function renderError(error) {
8897
</p>`;
8998
}
9099

91-
export { animateNode, renderOut, renderOutString, renderError };
100+
export { animateNode, renderError, renderOut, renderOutString };

src/main.js

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import MicroModal from "micromodal";
22
import Split from "split.js";
3-
import { verifyAFD } from "./afd.js";
3+
// import { verifyAFD } from "./afd.js";
4+
import { verifyAFND } from "./afnd.js";
45
import { renderError, renderOut, renderOutString } from "./animateNode.js";
56
import { clearAutomata, createAutomata } from "./automata.js";
67
import { startDragTools } from "./dragTools.js";
@@ -10,7 +11,7 @@ import download from "./utils/download.js";
1011

1112
const { graph, paper } = initGraph();
1213
const inputString = document.querySelector("#input-string");
13-
const inputEl = document.querySelector("#input-label-name");
14+
// const inputEl = document.querySelector("#input-label-name");
1415
const inputLabel = document.querySelector("#input-label-name");
1516
const inputState = document.querySelector("#input-state-name");
1617
const btnClearAll = document.querySelector("#btn-clear-all");
@@ -26,7 +27,7 @@ function run() {
2627
const alphabet = [];
2728
const string = inputString.value;
2829
const statesArr = [];
29-
const transitions = [];
30+
const transitions = {};
3031

3132
// clear errors
3233
renderError(null);
@@ -38,6 +39,8 @@ function run() {
3839
id: el.attributes.id,
3940
};
4041

42+
transitions[el.attributes.attrs.label.text] = {};
43+
4144
if (el.attributes.attrs.body.fill === FILL_NODE_FINAL) {
4245
finalStates.push(el.attributes.attrs.label.text);
4346
}
@@ -48,10 +51,23 @@ function run() {
4851
if (el.type === "Link") {
4952
alphabet.push(...el.labels[0].attrs.text.text.split(","));
5053

51-
transitions.push({
52-
state: states[el.source.id].text,
53-
symbol: el.labels[0].attrs.text.text.split(",") || "transition",
54-
nextState: states[el.target.id].text,
54+
// transitions.push({
55+
// state: states[el.source.id].text,
56+
// symbol: el.labels[0].attrs.text.text.split(",") || "transition",
57+
// nextState: states[el.target.id].text,
58+
// });
59+
60+
el.labels[0].attrs.text.text.split(",").forEach((symbol) => {
61+
if (transitions[states[el.source.id].text].length >= 0) {
62+
transitions[states[el.source.id].text].push([
63+
states[el.target.id].text,
64+
symbol,
65+
]);
66+
} else {
67+
transitions[states[el.source.id].text] = [
68+
[states[el.target.id].text, symbol],
69+
];
70+
}
5571
});
5672
}
5773
});
@@ -74,9 +90,14 @@ function run() {
7490
automata.finalStates = finalStates;
7591
automata.transitions = transitions;
7692

93+
console.log(automata);
94+
7795
renderOut("Loading ...");
7896
renderOutString(string);
79-
verifyAFD(paper, graph, automata, string);
97+
// verifyAFD(paper, graph, automata, string);
98+
99+
const res = verifyAFND(paper, graph, automata, string);
100+
console.log(res);
80101
}
81102

82103
function changeLabelName() {

0 commit comments

Comments
 (0)