-
Notifications
You must be signed in to change notification settings - Fork 0
/
NFA.ts
122 lines (107 loc) · 2.99 KB
/
NFA.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
type NFA = {
startState: State;
transitions: Transition[];
acceptStates: State[];
};
type Transition = {
triggeringState: State;
triggeringChar: Character;
stateToMoveTo: State;
};
type RunState = {
state: State;
inputIndex: number;
};
type State = string;
type Input = string;
type Character = string;
const EPSILON = "";
function getTransitions(
transitions: Transition[],
state: State,
char: Character
): Transition[] {
return transitions.filter(
({ triggeringState, triggeringChar }) =>
triggeringState === state &&
(triggeringChar === char || triggeringChar === EPSILON)
);
}
function simulateNFA(nfa: NFA, input: Input): boolean {
const runStateQueue: RunState[] = [{ state: nfa.startState, inputIndex: 0 }];
while (runStateQueue.length > 0) {
const { state, inputIndex } = runStateQueue.shift() as RunState;
const char = input[inputIndex];
if (inputIndex === input.length && nfa.acceptStates.includes(state)) {
console.log("Input used up and now in accept state, accept input");
return true;
}
getTransitions(nfa.transitions, state, char).forEach(transition => {
const nextInputIndex =
transition.triggeringChar === EPSILON ? inputIndex : inputIndex + 1;
runStateQueue.push({
state: transition.stateToMoveTo,
inputIndex: nextInputIndex,
});
});
}
console.log("No path found ending in accept state, reject input");
return false;
}
// Accepts input where either 101 or 11 is a substring
const exampleNFA = {
startState: "q1",
acceptStates: ["q4"],
transitions: [
{
triggeringState: "q1",
triggeringChar: "0",
stateToMoveTo: "q1",
},
{
triggeringState: "q1",
triggeringChar: "1",
stateToMoveTo: "q1",
},
{
triggeringState: "q1",
triggeringChar: "1",
stateToMoveTo: "q2",
},
{
triggeringState: "q2",
triggeringChar: "0",
stateToMoveTo: "q3",
},
{
triggeringState: "q2",
triggeringChar: EPSILON,
stateToMoveTo: "q3",
},
{
triggeringState: "q3",
triggeringChar: "1",
stateToMoveTo: "q4",
},
{
triggeringState: "q4",
triggeringChar: "0",
stateToMoveTo: "q4",
},
{
triggeringState: "q4",
triggeringChar: "1",
stateToMoveTo: "q4",
},
],
};
// Basic test cases
console.assert(simulateNFA(exampleNFA, "010110") === true);
console.assert(simulateNFA(exampleNFA, "0") === false);
console.assert(simulateNFA(exampleNFA, "1") === false);
console.assert(simulateNFA(exampleNFA, "11") === true);
console.assert(simulateNFA(exampleNFA, "101") === true);
console.assert(simulateNFA(exampleNFA, "100") === false);
console.assert(simulateNFA(exampleNFA, "010") === false);
console.assert(simulateNFA(exampleNFA, "") === false);
console.assert(simulateNFA(exampleNFA, "00011000") === true);