@turing-machine-js/machine 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/README.md +256 -12
- package/dist/classes/State.d.ts +27 -0
- package/dist/classes/State.js +172 -2
- package/dist/classes/TapeBlock.js +2 -3
- package/dist/index.cjs +1313 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/index.mjs +1295 -0
- package/dist/utilities/equivalence.d.ts +31 -0
- package/dist/utilities/equivalence.js +68 -0
- package/dist/utilities/graph.d.ts +29 -0
- package/dist/utilities/graph.js +127 -0
- package/dist/utilities/graphFormats.d.ts +3 -0
- package/dist/utilities/graphFormats.js +141 -0
- package/dist/utilities/introspection.d.ts +15 -0
- package/dist/utilities/introspection.js +88 -0
- package/package.json +7 -6
- package/tsconfig.tsbuildinfo +0 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1313 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const uniquePredicate = (v, i, a) => a.indexOf(v) === i;
|
|
4
|
+
const idKey = Symbol('idCurrentKey');
|
|
5
|
+
const idWeakMapKey = Symbol('idWeakMapKey');
|
|
6
|
+
function id(object) {
|
|
7
|
+
if (!id[idWeakMapKey].has(object)) {
|
|
8
|
+
id[idWeakMapKey].set(object, id[idKey]);
|
|
9
|
+
id[idKey] += 1;
|
|
10
|
+
}
|
|
11
|
+
return id[idWeakMapKey].get(object);
|
|
12
|
+
}
|
|
13
|
+
id[idKey] = 0;
|
|
14
|
+
id[idWeakMapKey] = new WeakMap();
|
|
15
|
+
|
|
16
|
+
var __classPrivateFieldSet$8 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
17
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
18
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
19
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
20
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
21
|
+
};
|
|
22
|
+
var __classPrivateFieldGet$8 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
23
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
24
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
25
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
26
|
+
};
|
|
27
|
+
var _Alphabet_symbols;
|
|
28
|
+
class Alphabet {
|
|
29
|
+
constructor(symbols) {
|
|
30
|
+
_Alphabet_symbols.set(this, void 0);
|
|
31
|
+
if (symbols instanceof Alphabet) {
|
|
32
|
+
symbols = symbols.symbols;
|
|
33
|
+
}
|
|
34
|
+
const uniqueSymbols = symbols.filter(uniquePredicate);
|
|
35
|
+
if (uniqueSymbols.length < 2) {
|
|
36
|
+
throw new Error('Invalid symbols length');
|
|
37
|
+
}
|
|
38
|
+
const isSymbolsValid = uniqueSymbols.every((symbol) => symbol.length === 1);
|
|
39
|
+
if (!isSymbolsValid) {
|
|
40
|
+
throw new Error('symbols contains invalid symbol');
|
|
41
|
+
}
|
|
42
|
+
__classPrivateFieldSet$8(this, _Alphabet_symbols, Array.from(uniqueSymbols), "f");
|
|
43
|
+
}
|
|
44
|
+
get symbols() {
|
|
45
|
+
return Array.from(__classPrivateFieldGet$8(this, _Alphabet_symbols, "f"));
|
|
46
|
+
}
|
|
47
|
+
get blankSymbol() {
|
|
48
|
+
return __classPrivateFieldGet$8(this, _Alphabet_symbols, "f")[0];
|
|
49
|
+
}
|
|
50
|
+
has(symbol) {
|
|
51
|
+
return __classPrivateFieldGet$8(this, _Alphabet_symbols, "f").indexOf(symbol) >= 0;
|
|
52
|
+
}
|
|
53
|
+
get(index) {
|
|
54
|
+
if (index < 0 || index >= __classPrivateFieldGet$8(this, _Alphabet_symbols, "f").length) {
|
|
55
|
+
throw new Error('Invalid index');
|
|
56
|
+
}
|
|
57
|
+
return __classPrivateFieldGet$8(this, _Alphabet_symbols, "f")[index];
|
|
58
|
+
}
|
|
59
|
+
index(symbol) {
|
|
60
|
+
return __classPrivateFieldGet$8(this, _Alphabet_symbols, "f").indexOf(symbol);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
_Alphabet_symbols = new WeakMap();
|
|
64
|
+
|
|
65
|
+
var __classPrivateFieldSet$7 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
66
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
67
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
68
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
69
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
70
|
+
};
|
|
71
|
+
var __classPrivateFieldGet$7 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
72
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
73
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
74
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
75
|
+
};
|
|
76
|
+
var _TapeCommand_movement, _TapeCommand_symbol;
|
|
77
|
+
const movements = {
|
|
78
|
+
left: Symbol('move caret left command'),
|
|
79
|
+
right: Symbol('move caret right command'),
|
|
80
|
+
stay: Symbol('do not move carer'),
|
|
81
|
+
};
|
|
82
|
+
const symbolCommands = {
|
|
83
|
+
erase: Symbol('erase symbol command'),
|
|
84
|
+
keep: Symbol('keep symbol command'),
|
|
85
|
+
};
|
|
86
|
+
class TapeCommand {
|
|
87
|
+
constructor({ movement = movements.stay, symbol = symbolCommands.keep, }) {
|
|
88
|
+
_TapeCommand_movement.set(this, void 0);
|
|
89
|
+
_TapeCommand_symbol.set(this, void 0);
|
|
90
|
+
const isValidMovement = [movements.left, movements.stay, movements.right].includes(movement);
|
|
91
|
+
if (!isValidMovement) {
|
|
92
|
+
throw new Error('invalid movement');
|
|
93
|
+
}
|
|
94
|
+
__classPrivateFieldSet$7(this, _TapeCommand_movement, movement, "f");
|
|
95
|
+
const isValidSymbol = ((typeof symbol === 'string' && symbol.length === 1)
|
|
96
|
+
|| [symbolCommands.keep, symbolCommands.erase].includes(symbol));
|
|
97
|
+
if (!isValidSymbol) {
|
|
98
|
+
throw new Error('invalid symbol');
|
|
99
|
+
}
|
|
100
|
+
__classPrivateFieldSet$7(this, _TapeCommand_symbol, symbol, "f");
|
|
101
|
+
}
|
|
102
|
+
get symbol() {
|
|
103
|
+
return __classPrivateFieldGet$7(this, _TapeCommand_symbol, "f");
|
|
104
|
+
}
|
|
105
|
+
get movement() {
|
|
106
|
+
return __classPrivateFieldGet$7(this, _TapeCommand_movement, "f");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
_TapeCommand_movement = new WeakMap(), _TapeCommand_symbol = new WeakMap();
|
|
110
|
+
|
|
111
|
+
var __classPrivateFieldSet$6 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
112
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
113
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
114
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
115
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
116
|
+
};
|
|
117
|
+
var __classPrivateFieldGet$6 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
118
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
119
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
120
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
121
|
+
};
|
|
122
|
+
var _Command_tapesCommands;
|
|
123
|
+
class Command {
|
|
124
|
+
constructor(tapesCommands) {
|
|
125
|
+
_Command_tapesCommands.set(this, void 0);
|
|
126
|
+
if (tapesCommands.length === 0) {
|
|
127
|
+
throw new Error('invalid parameter');
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
__classPrivateFieldSet$6(this, _Command_tapesCommands, tapesCommands.map((tapeCommand) => {
|
|
131
|
+
if (tapeCommand instanceof TapeCommand) {
|
|
132
|
+
return tapeCommand;
|
|
133
|
+
}
|
|
134
|
+
return new TapeCommand(tapeCommand);
|
|
135
|
+
}), "f");
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw new Error('invalid tapeCommand');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
get tapesCommands() {
|
|
142
|
+
return [...__classPrivateFieldGet$6(this, _Command_tapesCommands, "f")];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
_Command_tapesCommands = new WeakMap();
|
|
146
|
+
|
|
147
|
+
var __classPrivateFieldGet$5 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
148
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
149
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
150
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
151
|
+
};
|
|
152
|
+
var __classPrivateFieldSet$5 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
153
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
154
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
155
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
156
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
157
|
+
};
|
|
158
|
+
var _Reference_referenceBinding;
|
|
159
|
+
class Reference {
|
|
160
|
+
constructor() {
|
|
161
|
+
_Reference_referenceBinding.set(this, null);
|
|
162
|
+
}
|
|
163
|
+
get ref() {
|
|
164
|
+
if (!__classPrivateFieldGet$5(this, _Reference_referenceBinding, "f")) {
|
|
165
|
+
throw new Error('unbounded reference');
|
|
166
|
+
}
|
|
167
|
+
return __classPrivateFieldGet$5(this, _Reference_referenceBinding, "f");
|
|
168
|
+
}
|
|
169
|
+
bind(state) {
|
|
170
|
+
if (__classPrivateFieldGet$5(this, _Reference_referenceBinding, "f") == null) {
|
|
171
|
+
__classPrivateFieldSet$5(this, _Reference_referenceBinding, state, "f");
|
|
172
|
+
}
|
|
173
|
+
return __classPrivateFieldGet$5(this, _Reference_referenceBinding, "f");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
_Reference_referenceBinding = new WeakMap();
|
|
177
|
+
|
|
178
|
+
var __classPrivateFieldSet$4 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
179
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
180
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
181
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
182
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
183
|
+
};
|
|
184
|
+
var __classPrivateFieldGet$4 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
185
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
186
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
187
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
188
|
+
};
|
|
189
|
+
var _Tape_alphabet, _Tape_symbols, _Tape_position, _Tape_viewportWidth;
|
|
190
|
+
class Tape {
|
|
191
|
+
constructor({ alphabet, symbols = [], position = 0, viewportWidth = 1, }) {
|
|
192
|
+
_Tape_alphabet.set(this, void 0);
|
|
193
|
+
_Tape_symbols.set(this, void 0);
|
|
194
|
+
_Tape_position.set(this, void 0);
|
|
195
|
+
_Tape_viewportWidth.set(this, void 0);
|
|
196
|
+
const isSymbolsValid = symbols.every((symbol) => alphabet.has(symbol));
|
|
197
|
+
if (!isSymbolsValid) {
|
|
198
|
+
throw new Error('symbolList contains invalid symbol');
|
|
199
|
+
}
|
|
200
|
+
__classPrivateFieldSet$4(this, _Tape_alphabet, new Alphabet(alphabet), "f");
|
|
201
|
+
__classPrivateFieldSet$4(this, _Tape_position, position, "f");
|
|
202
|
+
__classPrivateFieldSet$4(this, _Tape_viewportWidth, viewportWidth, "f");
|
|
203
|
+
const symbolsCopy = Array.from(symbols);
|
|
204
|
+
if (symbolsCopy.length === 0) {
|
|
205
|
+
symbolsCopy.push(__classPrivateFieldGet$4(this, _Tape_alphabet, "f").blankSymbol);
|
|
206
|
+
}
|
|
207
|
+
__classPrivateFieldSet$4(this, _Tape_symbols, symbolsCopy.map((symbol) => __classPrivateFieldGet$4(this, _Tape_alphabet, "f").index(symbol)), "f");
|
|
208
|
+
}
|
|
209
|
+
get alphabet() {
|
|
210
|
+
return __classPrivateFieldGet$4(this, _Tape_alphabet, "f");
|
|
211
|
+
}
|
|
212
|
+
get extraCellsCount() {
|
|
213
|
+
return (__classPrivateFieldGet$4(this, _Tape_viewportWidth, "f") - 1) / 2;
|
|
214
|
+
}
|
|
215
|
+
get position() {
|
|
216
|
+
return __classPrivateFieldGet$4(this, _Tape_position, "f");
|
|
217
|
+
}
|
|
218
|
+
get symbol() {
|
|
219
|
+
return __classPrivateFieldGet$4(this, _Tape_alphabet, "f").get(__classPrivateFieldGet$4(this, _Tape_symbols, "f")[__classPrivateFieldGet$4(this, _Tape_position, "f")]);
|
|
220
|
+
}
|
|
221
|
+
set symbol(symbol) {
|
|
222
|
+
if (!__classPrivateFieldGet$4(this, _Tape_alphabet, "f").has(symbol)) {
|
|
223
|
+
throw new Error('Invalid symbol');
|
|
224
|
+
}
|
|
225
|
+
__classPrivateFieldGet$4(this, _Tape_symbols, "f")[__classPrivateFieldGet$4(this, _Tape_position, "f")] = __classPrivateFieldGet$4(this, _Tape_alphabet, "f").index(symbol);
|
|
226
|
+
}
|
|
227
|
+
get symbols() {
|
|
228
|
+
return __classPrivateFieldGet$4(this, _Tape_symbols, "f")
|
|
229
|
+
.map((index) => __classPrivateFieldGet$4(this, _Tape_alphabet, "f").get(index));
|
|
230
|
+
}
|
|
231
|
+
get viewport() {
|
|
232
|
+
const startIx = __classPrivateFieldGet$4(this, _Tape_position, "f") - this.extraCellsCount;
|
|
233
|
+
const endIx = __classPrivateFieldGet$4(this, _Tape_position, "f") + this.extraCellsCount + 1;
|
|
234
|
+
return __classPrivateFieldGet$4(this, _Tape_symbols, "f")
|
|
235
|
+
.slice(startIx, endIx)
|
|
236
|
+
.map((index) => __classPrivateFieldGet$4(this, _Tape_alphabet, "f").get(index));
|
|
237
|
+
}
|
|
238
|
+
get viewportWidth() {
|
|
239
|
+
return __classPrivateFieldGet$4(this, _Tape_viewportWidth, "f");
|
|
240
|
+
}
|
|
241
|
+
set viewportWidth(width) {
|
|
242
|
+
let finalWidth = width;
|
|
243
|
+
if (finalWidth < 1) {
|
|
244
|
+
throw new Error('Invalid viewportWidth');
|
|
245
|
+
}
|
|
246
|
+
if (finalWidth % 2 === 0) {
|
|
247
|
+
finalWidth += 1;
|
|
248
|
+
}
|
|
249
|
+
__classPrivateFieldSet$4(this, _Tape_viewportWidth, finalWidth, "f");
|
|
250
|
+
this.normalise();
|
|
251
|
+
}
|
|
252
|
+
left() {
|
|
253
|
+
__classPrivateFieldSet$4(this, _Tape_position, __classPrivateFieldGet$4(this, _Tape_position, "f") - 1, "f");
|
|
254
|
+
this.normalise();
|
|
255
|
+
}
|
|
256
|
+
normalise() {
|
|
257
|
+
while (__classPrivateFieldGet$4(this, _Tape_position, "f") - this.extraCellsCount < 0) {
|
|
258
|
+
__classPrivateFieldGet$4(this, _Tape_symbols, "f").unshift(0);
|
|
259
|
+
__classPrivateFieldSet$4(this, _Tape_position, __classPrivateFieldGet$4(this, _Tape_position, "f") + 1, "f");
|
|
260
|
+
}
|
|
261
|
+
while (__classPrivateFieldGet$4(this, _Tape_position, "f") + this.extraCellsCount >= __classPrivateFieldGet$4(this, _Tape_symbols, "f").length) {
|
|
262
|
+
__classPrivateFieldGet$4(this, _Tape_symbols, "f").push(0);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
right() {
|
|
266
|
+
__classPrivateFieldSet$4(this, _Tape_position, __classPrivateFieldGet$4(this, _Tape_position, "f") + 1, "f");
|
|
267
|
+
this.normalise();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
_Tape_alphabet = new WeakMap(), _Tape_symbols = new WeakMap(), _Tape_position = new WeakMap(), _Tape_viewportWidth = new WeakMap();
|
|
271
|
+
|
|
272
|
+
var __classPrivateFieldGet$3 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
273
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
274
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
275
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
276
|
+
};
|
|
277
|
+
var __classPrivateFieldSet$3 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
278
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
279
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
280
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
281
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
282
|
+
};
|
|
283
|
+
var _Lock_lockSymbol;
|
|
284
|
+
class Lock {
|
|
285
|
+
constructor() {
|
|
286
|
+
_Lock_lockSymbol.set(this, null);
|
|
287
|
+
}
|
|
288
|
+
lock(symbol) {
|
|
289
|
+
if (__classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") === null) {
|
|
290
|
+
__classPrivateFieldSet$3(this, _Lock_lockSymbol, symbol, "f");
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
unlock(symbol) {
|
|
294
|
+
if (__classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") === symbol) {
|
|
295
|
+
__classPrivateFieldSet$3(this, _Lock_lockSymbol, null, "f");
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
check(symbol) {
|
|
299
|
+
if (__classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") && __classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") !== symbol) {
|
|
300
|
+
throw new Error('Lock check failed');
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
_Lock_lockSymbol = new WeakMap();
|
|
305
|
+
|
|
306
|
+
var __classPrivateFieldSet$2 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
307
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
308
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
309
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
310
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
311
|
+
};
|
|
312
|
+
var __classPrivateFieldGet$2 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
313
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
314
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
315
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
316
|
+
};
|
|
317
|
+
var _a, _TapeBlock_symbolToPatternListMap, _TapeBlock_lock, _TapeBlock_tapes, _TapeBlock_generateSymbolHint, _TapeBlock_buildPatternList, _TapeBlock_getSymbolForPatternList, _TapeBlock_symbol;
|
|
318
|
+
const symbolToPatternListMapSymbol = Symbol('symbol for symbolToPatternListMap setter');
|
|
319
|
+
const lockSymbol = Symbol('capture symbol');
|
|
320
|
+
class TapeBlock {
|
|
321
|
+
constructor(argument) {
|
|
322
|
+
_TapeBlock_symbolToPatternListMap.set(this, new Map());
|
|
323
|
+
_TapeBlock_lock.set(this, new Lock());
|
|
324
|
+
_TapeBlock_tapes.set(this, void 0);
|
|
325
|
+
_TapeBlock_buildPatternList.set(this, (symbolList) => symbolList.reduce((result, symbol, ix) => {
|
|
326
|
+
const row = Math.floor(ix / __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length);
|
|
327
|
+
if (!Array.isArray(result[row])) {
|
|
328
|
+
result[row] = [];
|
|
329
|
+
}
|
|
330
|
+
result[row].push(symbol);
|
|
331
|
+
return result;
|
|
332
|
+
}, [])
|
|
333
|
+
.filter((pattern, ix, patternList) => {
|
|
334
|
+
const samePatternIx = patternList.findIndex((otherPattern) => (pattern
|
|
335
|
+
.every((symbol, symbolIx) => symbol === otherPattern[symbolIx])));
|
|
336
|
+
return samePatternIx === ix;
|
|
337
|
+
}));
|
|
338
|
+
_TapeBlock_getSymbolForPatternList.set(this, (patternList) => {
|
|
339
|
+
if (patternList.some((pattern) => pattern.every((symbol) => symbol === ifOtherSymbol))) {
|
|
340
|
+
return ifOtherSymbol;
|
|
341
|
+
}
|
|
342
|
+
const [storedPatternListSymbol] = [...__classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").entries()]
|
|
343
|
+
.find(([, storedPatternList]) => {
|
|
344
|
+
if (storedPatternList.length !== patternList.length) {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
return patternList
|
|
348
|
+
.every((pattern, patternIx) => pattern
|
|
349
|
+
.every((symbol, symbolIx) => symbol === storedPatternList[patternIx][symbolIx]));
|
|
350
|
+
}) || [null, null];
|
|
351
|
+
let symbol;
|
|
352
|
+
if (storedPatternListSymbol) {
|
|
353
|
+
symbol = storedPatternListSymbol;
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
symbol = Symbol(__classPrivateFieldGet$2(_a, _a, "f", _TapeBlock_generateSymbolHint).call(_a, patternList));
|
|
357
|
+
__classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").set(symbol, patternList);
|
|
358
|
+
}
|
|
359
|
+
return symbol;
|
|
360
|
+
});
|
|
361
|
+
_TapeBlock_symbol.set(this, (symbols) => {
|
|
362
|
+
let localSymbols = [];
|
|
363
|
+
if (symbols === ifOtherSymbol) {
|
|
364
|
+
return ifOtherSymbol;
|
|
365
|
+
}
|
|
366
|
+
if (typeof symbols === 'string') {
|
|
367
|
+
localSymbols = symbols.split('');
|
|
368
|
+
}
|
|
369
|
+
else if (Array.isArray(symbols)) {
|
|
370
|
+
localSymbols = [...symbols];
|
|
371
|
+
}
|
|
372
|
+
if (localSymbols.length === 0 || localSymbols.length % __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length > 0) {
|
|
373
|
+
throw new Error('invalid symbol parameter');
|
|
374
|
+
}
|
|
375
|
+
const invalidSymbolIndex = localSymbols.findIndex((symbol, ix) => (symbol !== ifOtherSymbol
|
|
376
|
+
&& !__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")[ix % __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length].alphabet.has(symbol)));
|
|
377
|
+
if (invalidSymbolIndex >= 0) {
|
|
378
|
+
throw new Error('invalid symbol parameter');
|
|
379
|
+
}
|
|
380
|
+
if (localSymbols.every((symbol) => symbol === ifOtherSymbol)) {
|
|
381
|
+
return ifOtherSymbol;
|
|
382
|
+
}
|
|
383
|
+
return __classPrivateFieldGet$2(this, _TapeBlock_getSymbolForPatternList, "f").call(this, __classPrivateFieldGet$2(this, _TapeBlock_buildPatternList, "f").call(this, localSymbols));
|
|
384
|
+
});
|
|
385
|
+
__classPrivateFieldSet$2(this, _TapeBlock_tapes, [], "f");
|
|
386
|
+
if ('alphabets' in argument) {
|
|
387
|
+
const { alphabets } = argument;
|
|
388
|
+
if (alphabets.length === 0) {
|
|
389
|
+
throw new Error('empty alphabet list');
|
|
390
|
+
}
|
|
391
|
+
__classPrivateFieldSet$2(this, _TapeBlock_tapes, alphabets.map((alphabet) => new Tape({
|
|
392
|
+
alphabet,
|
|
393
|
+
})), "f");
|
|
394
|
+
}
|
|
395
|
+
else if ('tapes' in argument) {
|
|
396
|
+
__classPrivateFieldSet$2(this, _TapeBlock_tapes, argument.tapes, "f");
|
|
397
|
+
}
|
|
398
|
+
if (__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length === 0) {
|
|
399
|
+
throw new Error('empty tape list');
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
get alphabets() {
|
|
403
|
+
return [...__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").map((tape) => tape.alphabet)];
|
|
404
|
+
}
|
|
405
|
+
get currentSymbols() {
|
|
406
|
+
return __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").map((tape) => tape.symbol);
|
|
407
|
+
}
|
|
408
|
+
get [(_TapeBlock_symbolToPatternListMap = new WeakMap(), _TapeBlock_lock = new WeakMap(), _TapeBlock_tapes = new WeakMap(), _TapeBlock_buildPatternList = new WeakMap(), _TapeBlock_getSymbolForPatternList = new WeakMap(), _TapeBlock_symbol = new WeakMap(), lockSymbol)]() {
|
|
409
|
+
return __classPrivateFieldGet$2(this, _TapeBlock_lock, "f");
|
|
410
|
+
}
|
|
411
|
+
get symbol() {
|
|
412
|
+
return __classPrivateFieldGet$2(this, _TapeBlock_symbol, "f").bind(this);
|
|
413
|
+
}
|
|
414
|
+
get tapes() {
|
|
415
|
+
return [...__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")];
|
|
416
|
+
}
|
|
417
|
+
set [symbolToPatternListMapSymbol](symbolToPatternListMap) {
|
|
418
|
+
__classPrivateFieldSet$2(this, _TapeBlock_symbolToPatternListMap, new Map(symbolToPatternListMap), "f");
|
|
419
|
+
}
|
|
420
|
+
applyCommand(command, executionSymbol = null) {
|
|
421
|
+
__classPrivateFieldGet$2(this, _TapeBlock_lock, "f").check(executionSymbol);
|
|
422
|
+
if (__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length !== command.tapesCommands.length) {
|
|
423
|
+
throw new Error('invalid command');
|
|
424
|
+
}
|
|
425
|
+
__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").forEach((tape, ix) => {
|
|
426
|
+
const { movement, symbol } = command.tapesCommands[ix];
|
|
427
|
+
if (typeof symbol === 'string') {
|
|
428
|
+
tape.symbol = symbol;
|
|
429
|
+
}
|
|
430
|
+
if (typeof symbol === 'symbol') {
|
|
431
|
+
switch (symbol) {
|
|
432
|
+
case symbolCommands.keep:
|
|
433
|
+
break;
|
|
434
|
+
case symbolCommands.erase:
|
|
435
|
+
tape.symbol = tape.alphabet.blankSymbol;
|
|
436
|
+
break;
|
|
437
|
+
// no default
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
switch (movement) {
|
|
441
|
+
case movements.left:
|
|
442
|
+
tape.left();
|
|
443
|
+
break;
|
|
444
|
+
case movements.stay:
|
|
445
|
+
break;
|
|
446
|
+
case movements.right:
|
|
447
|
+
tape.right();
|
|
448
|
+
break;
|
|
449
|
+
// no default
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
clone(cloneTapes = false) {
|
|
454
|
+
let tapeBlock;
|
|
455
|
+
if (cloneTapes) {
|
|
456
|
+
tapeBlock = _a.fromTapes(this.tapes.map((tape) => new Tape(tape)));
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
tapeBlock = _a.fromAlphabets(this.alphabets);
|
|
460
|
+
}
|
|
461
|
+
tapeBlock[symbolToPatternListMapSymbol] = __classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f");
|
|
462
|
+
return tapeBlock;
|
|
463
|
+
}
|
|
464
|
+
isMatched({ currentSymbols = this.currentSymbols, symbol }) {
|
|
465
|
+
if (symbol === ifOtherSymbol) {
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
if (!__classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").has(symbol)) {
|
|
469
|
+
throw new Error('invalid symbol');
|
|
470
|
+
}
|
|
471
|
+
const patternList = __classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").get(symbol);
|
|
472
|
+
return patternList?.some((pattern) => (pattern
|
|
473
|
+
.every((everySymbol, ix) => (everySymbol === ifOtherSymbol
|
|
474
|
+
|| everySymbol === currentSymbols[ix])))) ?? false;
|
|
475
|
+
}
|
|
476
|
+
replaceTape(tape, tapeIx = 0) {
|
|
477
|
+
if (__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")[tapeIx] == null) {
|
|
478
|
+
throw new Error('invalid tapeIx');
|
|
479
|
+
}
|
|
480
|
+
if (tape.alphabet.symbols.join('') === __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")[tapeIx].alphabet.symbols.join('')) {
|
|
481
|
+
__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")[tapeIx] = tape;
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
throw new Error('invalid tape');
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
_a = TapeBlock;
|
|
489
|
+
TapeBlock.fromAlphabets = (alphabets) => {
|
|
490
|
+
return new _a({ alphabets });
|
|
491
|
+
};
|
|
492
|
+
TapeBlock.fromTapes = (tapes) => {
|
|
493
|
+
return new _a({ tapes });
|
|
494
|
+
};
|
|
495
|
+
_TapeBlock_generateSymbolHint = { value: (patternList) => JSON.stringify(patternList
|
|
496
|
+
.map((pattern) => pattern
|
|
497
|
+
.map((symbol) => (symbol === ifOtherSymbol ? null : symbol)))) };
|
|
498
|
+
|
|
499
|
+
const movementDescriptionToLabel = {
|
|
500
|
+
'move caret left command': 'L',
|
|
501
|
+
'move caret right command': 'R',
|
|
502
|
+
'do not move carer': 'S',
|
|
503
|
+
};
|
|
504
|
+
const symbolCommandDescriptionToLabel = {
|
|
505
|
+
'keep symbol command': '·',
|
|
506
|
+
'erase symbol command': '⌫',
|
|
507
|
+
};
|
|
508
|
+
// Reserved characters in the encoded pattern string:
|
|
509
|
+
// '*' per-cell ifOtherSymbol (matches any symbol on that tape)
|
|
510
|
+
// '-' the tape's blank symbol
|
|
511
|
+
// ',' separates per-tape cells inside one pattern
|
|
512
|
+
// '|' separates alternative patterns
|
|
513
|
+
// '\\' escape prefix — to represent any of '*', '-', ',', '|', or '\\' as a
|
|
514
|
+
// *literal* alphabet symbol, prefix it with '\\' (e.g. '\\*' for literal '*').
|
|
515
|
+
function escapeAlphabetSymbol(s) {
|
|
516
|
+
return s
|
|
517
|
+
.replace(/\\/g, '\\\\')
|
|
518
|
+
.replace(/\*/g, '\\*')
|
|
519
|
+
.replace(/-/g, '\\-')
|
|
520
|
+
.replace(/,/g, '\\,')
|
|
521
|
+
.replace(/\|/g, '\\|');
|
|
522
|
+
}
|
|
523
|
+
function decodePatternDescription(description, alphabets) {
|
|
524
|
+
if (!description) {
|
|
525
|
+
return '?';
|
|
526
|
+
}
|
|
527
|
+
if (description === 'other symbol') {
|
|
528
|
+
return '*';
|
|
529
|
+
}
|
|
530
|
+
try {
|
|
531
|
+
const patternList = JSON.parse(description);
|
|
532
|
+
return patternList
|
|
533
|
+
.map((pattern) => pattern
|
|
534
|
+
.map((s, tapeIx) => {
|
|
535
|
+
if (s === null) {
|
|
536
|
+
return '*';
|
|
537
|
+
}
|
|
538
|
+
if (s === alphabets[tapeIx]?.[0]) {
|
|
539
|
+
return '-';
|
|
540
|
+
}
|
|
541
|
+
return escapeAlphabetSymbol(s);
|
|
542
|
+
})
|
|
543
|
+
.join(','))
|
|
544
|
+
.join('|');
|
|
545
|
+
}
|
|
546
|
+
catch {
|
|
547
|
+
return description;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function decodeMovement(description) {
|
|
551
|
+
if (!description) {
|
|
552
|
+
return '?';
|
|
553
|
+
}
|
|
554
|
+
return movementDescriptionToLabel[description] ?? description;
|
|
555
|
+
}
|
|
556
|
+
function splitUnescaped(s, sep) {
|
|
557
|
+
const parts = [];
|
|
558
|
+
let current = '';
|
|
559
|
+
let i = 0;
|
|
560
|
+
while (i < s.length) {
|
|
561
|
+
if (s[i] === '\\' && i + 1 < s.length) {
|
|
562
|
+
current += s[i + 1];
|
|
563
|
+
i += 2;
|
|
564
|
+
}
|
|
565
|
+
else if (s[i] === sep) {
|
|
566
|
+
parts.push(current);
|
|
567
|
+
current = '';
|
|
568
|
+
i += 1;
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
current += s[i];
|
|
572
|
+
i += 1;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
parts.push(current);
|
|
576
|
+
return parts;
|
|
577
|
+
}
|
|
578
|
+
function parsePatternString(s, alphabets) {
|
|
579
|
+
if (s === '*') {
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
const alternatives = splitUnescaped(s, '|');
|
|
583
|
+
return alternatives.map((alt) => {
|
|
584
|
+
const cells = splitUnescaped(alt, ',');
|
|
585
|
+
return cells.map((cell, tapeIx) => {
|
|
586
|
+
if (cell === '*') {
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
if (cell === '-') {
|
|
590
|
+
return alphabets[tapeIx]?.[0] ?? cell;
|
|
591
|
+
}
|
|
592
|
+
return cell;
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
const movementLabelToSymbol = {
|
|
597
|
+
L: movements.left,
|
|
598
|
+
R: movements.right,
|
|
599
|
+
S: movements.stay,
|
|
600
|
+
};
|
|
601
|
+
function parseMovementLabel(label) {
|
|
602
|
+
const m = movementLabelToSymbol[label];
|
|
603
|
+
if (!m) {
|
|
604
|
+
throw new Error(`unknown movement label: ${label}`);
|
|
605
|
+
}
|
|
606
|
+
return m;
|
|
607
|
+
}
|
|
608
|
+
function parseWriteSymbolLabel(label) {
|
|
609
|
+
if (label === '·') {
|
|
610
|
+
return symbolCommands.keep;
|
|
611
|
+
}
|
|
612
|
+
if (label === '⌫') {
|
|
613
|
+
return symbolCommands.erase;
|
|
614
|
+
}
|
|
615
|
+
return label;
|
|
616
|
+
}
|
|
617
|
+
function decodeWriteSymbol(symbol) {
|
|
618
|
+
if (typeof symbol === 'symbol') {
|
|
619
|
+
const description = symbol.description ?? '?';
|
|
620
|
+
return symbolCommandDescriptionToLabel[description] ?? description;
|
|
621
|
+
}
|
|
622
|
+
return symbol;
|
|
623
|
+
}
|
|
624
|
+
// Format converters (toMermaid / fromMermaid) live in ./graphFormats.
|
|
625
|
+
|
|
626
|
+
var __classPrivateFieldGet$1 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
627
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
628
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
629
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
630
|
+
};
|
|
631
|
+
var __classPrivateFieldSet$1 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
632
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
633
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
634
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
635
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
636
|
+
};
|
|
637
|
+
var _State_id, _State_name, _State_overrodeHaltState, _State_symbolToDataMap;
|
|
638
|
+
const ifOtherSymbol = Symbol('other symbol');
|
|
639
|
+
class State {
|
|
640
|
+
constructor(stateDefinition = null, name) {
|
|
641
|
+
_State_id.set(this, id(this));
|
|
642
|
+
_State_name.set(this, void 0);
|
|
643
|
+
_State_overrodeHaltState.set(this, null);
|
|
644
|
+
_State_symbolToDataMap.set(this, new Map());
|
|
645
|
+
if (stateDefinition) {
|
|
646
|
+
const keys = Object.getOwnPropertyNames(stateDefinition);
|
|
647
|
+
if (keys.length) {
|
|
648
|
+
throw new Error(`invalid state definition while constructing state #${__classPrivateFieldGet$1(this, _State_id, "f")}`);
|
|
649
|
+
}
|
|
650
|
+
const symbols = Object.getOwnPropertySymbols(stateDefinition);
|
|
651
|
+
if (symbols.length === 0) {
|
|
652
|
+
throw new Error(`invalid state definition while constructing state #${__classPrivateFieldGet$1(this, _State_id, "f")}`);
|
|
653
|
+
}
|
|
654
|
+
symbols.forEach((symbol) => {
|
|
655
|
+
const { nextState } = stateDefinition[symbol];
|
|
656
|
+
const nextStateLocal = nextState ?? this;
|
|
657
|
+
if (!(nextStateLocal instanceof State) && !(nextStateLocal instanceof Reference)) {
|
|
658
|
+
throw new Error('invalid nextState');
|
|
659
|
+
}
|
|
660
|
+
let { command } = stateDefinition[symbol];
|
|
661
|
+
if (command == null) {
|
|
662
|
+
command = new Command([
|
|
663
|
+
new TapeCommand({}),
|
|
664
|
+
]);
|
|
665
|
+
}
|
|
666
|
+
if (!(command instanceof Command) && !Array.isArray(command)) {
|
|
667
|
+
command = [command];
|
|
668
|
+
}
|
|
669
|
+
let commandLocal = command;
|
|
670
|
+
if (Array.isArray(command)) {
|
|
671
|
+
try {
|
|
672
|
+
commandLocal = new Command(command);
|
|
673
|
+
}
|
|
674
|
+
catch (error) {
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (!(commandLocal instanceof Command)) {
|
|
678
|
+
throw new Error('invalid command');
|
|
679
|
+
}
|
|
680
|
+
__classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").set(symbol, {
|
|
681
|
+
command: commandLocal,
|
|
682
|
+
nextState: nextStateLocal,
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
__classPrivateFieldSet$1(this, _State_name, name ?? `id:${__classPrivateFieldGet$1(this, _State_id, "f")}`, "f");
|
|
687
|
+
}
|
|
688
|
+
get id() {
|
|
689
|
+
return __classPrivateFieldGet$1(this, _State_id, "f");
|
|
690
|
+
}
|
|
691
|
+
get name() {
|
|
692
|
+
return __classPrivateFieldGet$1(this, _State_name, "f");
|
|
693
|
+
}
|
|
694
|
+
get isHalt() {
|
|
695
|
+
return __classPrivateFieldGet$1(this, _State_id, "f") === 0;
|
|
696
|
+
}
|
|
697
|
+
get overrodeHaltState() {
|
|
698
|
+
return __classPrivateFieldGet$1(this, _State_overrodeHaltState, "f");
|
|
699
|
+
}
|
|
700
|
+
get ref() {
|
|
701
|
+
return this;
|
|
702
|
+
}
|
|
703
|
+
getSymbol(tapeBlock) {
|
|
704
|
+
const symbol = [...__classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").keys()].find((currentSymbol) => tapeBlock.isMatched({
|
|
705
|
+
symbol: currentSymbol,
|
|
706
|
+
}));
|
|
707
|
+
if (symbol) {
|
|
708
|
+
return symbol;
|
|
709
|
+
}
|
|
710
|
+
return ifOtherSymbol;
|
|
711
|
+
}
|
|
712
|
+
getCommand(symbol) {
|
|
713
|
+
if (__classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").has(symbol)) {
|
|
714
|
+
return __classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").get(symbol).command;
|
|
715
|
+
}
|
|
716
|
+
throw new Error(`No command for symbol at state named ${__classPrivateFieldGet$1(this, _State_name, "f")}`);
|
|
717
|
+
}
|
|
718
|
+
getNextState(symbol) {
|
|
719
|
+
if (__classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").has(symbol)) {
|
|
720
|
+
return __classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").get(symbol).nextState;
|
|
721
|
+
}
|
|
722
|
+
throw new Error(`No nextState for symbol at state named ${__classPrivateFieldGet$1(this, _State_id, "f")}`);
|
|
723
|
+
}
|
|
724
|
+
withOverrodeHaltState(overrodeHaltState) {
|
|
725
|
+
const state = new State(null, `${this.name}>${overrodeHaltState.name}`);
|
|
726
|
+
__classPrivateFieldSet$1(state, _State_symbolToDataMap, __classPrivateFieldGet$1(this, _State_symbolToDataMap, "f"), "f");
|
|
727
|
+
__classPrivateFieldSet$1(state, _State_overrodeHaltState, overrodeHaltState, "f");
|
|
728
|
+
return state;
|
|
729
|
+
}
|
|
730
|
+
// Single-state introspection — no traversal, no tapeBlock required.
|
|
731
|
+
// Returns id, name, halt-status, override-halt target, and the list of
|
|
732
|
+
// transitions out of this state with decoded write/movement labels.
|
|
733
|
+
// Symbol patterns are returned as the raw description string from the
|
|
734
|
+
// interned JS Symbol (decode via decodePatternDescription if needed).
|
|
735
|
+
static inspect(state) {
|
|
736
|
+
const transitions = [];
|
|
737
|
+
for (const [sym, { command, nextState }] of __classPrivateFieldGet$1(state, _State_symbolToDataMap, "f")) {
|
|
738
|
+
let target = null;
|
|
739
|
+
try {
|
|
740
|
+
target = nextState instanceof State ? nextState : nextState.ref;
|
|
741
|
+
}
|
|
742
|
+
catch {
|
|
743
|
+
target = null; // unbound Reference
|
|
744
|
+
}
|
|
745
|
+
transitions.push({
|
|
746
|
+
rawPatternDescription: sym.description,
|
|
747
|
+
command: command.tapesCommands.map((tc) => ({
|
|
748
|
+
symbol: decodeWriteSymbol(tc.symbol),
|
|
749
|
+
movement: decodeMovement(tc.movement.description),
|
|
750
|
+
})),
|
|
751
|
+
nextState: target ? { id: target.id, name: target.name } : null,
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
return {
|
|
755
|
+
id: __classPrivateFieldGet$1(state, _State_id, "f"),
|
|
756
|
+
name: __classPrivateFieldGet$1(state, _State_name, "f"),
|
|
757
|
+
isHalt: state.isHalt,
|
|
758
|
+
overrodeHaltState: __classPrivateFieldGet$1(state, _State_overrodeHaltState, "f")
|
|
759
|
+
? { id: __classPrivateFieldGet$1(state, _State_overrodeHaltState, "f").id, name: __classPrivateFieldGet$1(state, _State_overrodeHaltState, "f").name }
|
|
760
|
+
: null,
|
|
761
|
+
transitions,
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
static toGraph(initialState, tapeBlock) {
|
|
765
|
+
const nodes = {};
|
|
766
|
+
const queue = [initialState];
|
|
767
|
+
const alphabets = tapeBlock.alphabets.map((alphabet) => alphabet.symbols);
|
|
768
|
+
while (queue.length > 0) {
|
|
769
|
+
const current = queue.shift();
|
|
770
|
+
if (__classPrivateFieldGet$1(current, _State_id, "f") in nodes) {
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
const node = {
|
|
774
|
+
id: __classPrivateFieldGet$1(current, _State_id, "f"),
|
|
775
|
+
name: __classPrivateFieldGet$1(current, _State_name, "f"),
|
|
776
|
+
isHalt: current.isHalt,
|
|
777
|
+
transitions: [],
|
|
778
|
+
overrodeHaltStateId: __classPrivateFieldGet$1(current, _State_overrodeHaltState, "f")?.id ?? null,
|
|
779
|
+
};
|
|
780
|
+
nodes[__classPrivateFieldGet$1(current, _State_id, "f")] = node;
|
|
781
|
+
if (__classPrivateFieldGet$1(current, _State_overrodeHaltState, "f")) {
|
|
782
|
+
queue.push(__classPrivateFieldGet$1(current, _State_overrodeHaltState, "f"));
|
|
783
|
+
}
|
|
784
|
+
for (const [sym, { command, nextState }] of __classPrivateFieldGet$1(current, _State_symbolToDataMap, "f")) {
|
|
785
|
+
let target;
|
|
786
|
+
try {
|
|
787
|
+
target = nextState instanceof State ? nextState : nextState.ref;
|
|
788
|
+
}
|
|
789
|
+
catch {
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
node.transitions.push({
|
|
793
|
+
pattern: decodePatternDescription(sym.description, alphabets),
|
|
794
|
+
command: command.tapesCommands.map((tc) => ({
|
|
795
|
+
symbol: decodeWriteSymbol(tc.symbol),
|
|
796
|
+
movement: decodeMovement(tc.movement.description),
|
|
797
|
+
})),
|
|
798
|
+
nextStateId: target.id,
|
|
799
|
+
});
|
|
800
|
+
queue.push(target);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return { initialId: __classPrivateFieldGet$1(initialState, _State_id, "f"), alphabets, nodes };
|
|
804
|
+
}
|
|
805
|
+
// Inverse of toGraph: rebuilds a State graph (and a fresh TapeBlock with the
|
|
806
|
+
// graph's alphabets) from a serialized Graph. Round-trips with toGraph in
|
|
807
|
+
// the sense that running the rebuilt machine on the same input gives the
|
|
808
|
+
// same output, but the rebuilt State instances have *new* internal IDs.
|
|
809
|
+
static fromGraph(graph) {
|
|
810
|
+
const alphabetObjs = graph.alphabets.map((syms) => new Alphabet(syms));
|
|
811
|
+
const tapeBlock = TapeBlock.fromAlphabets(alphabetObjs);
|
|
812
|
+
const ids = Object.keys(graph.nodes).map(Number);
|
|
813
|
+
// Pass 1: pre-create a Reference for each non-halt node so transitions can
|
|
814
|
+
// forward-declare their targets.
|
|
815
|
+
const refs = {};
|
|
816
|
+
for (const nodeId of ids) {
|
|
817
|
+
if (!graph.nodes[nodeId].isHalt) {
|
|
818
|
+
refs[nodeId] = new Reference();
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
// Convert a parsed pattern back to the symbol key the State expects.
|
|
822
|
+
const patternToKey = (parsed) => {
|
|
823
|
+
if (parsed === null) {
|
|
824
|
+
return ifOtherSymbol;
|
|
825
|
+
}
|
|
826
|
+
const flat = [];
|
|
827
|
+
for (const row of parsed) {
|
|
828
|
+
for (const cell of row) {
|
|
829
|
+
flat.push(cell === null ? ifOtherSymbol : cell);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return tapeBlock.symbol(flat);
|
|
833
|
+
};
|
|
834
|
+
// Pass 2: build a "bare" State for each non-halt node (no override yet).
|
|
835
|
+
// nextState entries point at refs so cycles work; haltState is used directly.
|
|
836
|
+
const bareStates = {};
|
|
837
|
+
for (const nodeId of ids) {
|
|
838
|
+
const node = graph.nodes[nodeId];
|
|
839
|
+
if (node.isHalt) {
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
const stateDefinition = {};
|
|
843
|
+
for (const t of node.transitions) {
|
|
844
|
+
const key = patternToKey(parsePatternString(t.pattern, graph.alphabets));
|
|
845
|
+
const target = graph.nodes[t.nextStateId];
|
|
846
|
+
const nextState = target.isHalt ? haltState : refs[t.nextStateId];
|
|
847
|
+
stateDefinition[key] = {
|
|
848
|
+
command: t.command.map((c) => ({
|
|
849
|
+
symbol: parseWriteSymbolLabel(c.symbol),
|
|
850
|
+
movement: parseMovementLabel(c.movement),
|
|
851
|
+
})),
|
|
852
|
+
nextState,
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
bareStates[nodeId] = new State(stateDefinition, node.name);
|
|
856
|
+
}
|
|
857
|
+
// Pass 3: apply overrideHaltStates transitively.
|
|
858
|
+
const finalStates = {};
|
|
859
|
+
const inProgress = new Set();
|
|
860
|
+
const getFinal = (nodeId) => {
|
|
861
|
+
if (finalStates[nodeId]) {
|
|
862
|
+
return finalStates[nodeId];
|
|
863
|
+
}
|
|
864
|
+
const node = graph.nodes[nodeId];
|
|
865
|
+
if (node.isHalt) {
|
|
866
|
+
finalStates[nodeId] = haltState;
|
|
867
|
+
return haltState;
|
|
868
|
+
}
|
|
869
|
+
if (inProgress.has(nodeId)) {
|
|
870
|
+
throw new Error(`override-halt cycle at state #${nodeId}`);
|
|
871
|
+
}
|
|
872
|
+
inProgress.add(nodeId);
|
|
873
|
+
let state = bareStates[nodeId];
|
|
874
|
+
if (node.overrodeHaltStateId !== null) {
|
|
875
|
+
state = bareStates[nodeId].withOverrodeHaltState(getFinal(node.overrodeHaltStateId));
|
|
876
|
+
}
|
|
877
|
+
inProgress.delete(nodeId);
|
|
878
|
+
finalStates[nodeId] = state;
|
|
879
|
+
return state;
|
|
880
|
+
};
|
|
881
|
+
for (const nodeId of ids) {
|
|
882
|
+
getFinal(nodeId);
|
|
883
|
+
}
|
|
884
|
+
// Pass 4: bind each ref to the FINAL (possibly wrapped) state so transitions
|
|
885
|
+
// resolve to the version that has its override-halt set.
|
|
886
|
+
for (const nodeId of ids) {
|
|
887
|
+
if (!graph.nodes[nodeId].isHalt) {
|
|
888
|
+
refs[nodeId].bind(finalStates[nodeId]);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return {
|
|
892
|
+
start: finalStates[graph.initialId],
|
|
893
|
+
tapeBlock,
|
|
894
|
+
states: finalStates,
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
_State_id = new WeakMap(), _State_name = new WeakMap(), _State_overrodeHaltState = new WeakMap(), _State_symbolToDataMap = new WeakMap();
|
|
899
|
+
const haltState = new State(null);
|
|
900
|
+
|
|
901
|
+
var __classPrivateFieldSet = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
902
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
903
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
904
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
905
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
906
|
+
};
|
|
907
|
+
var __classPrivateFieldGet = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
908
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
909
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
910
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
911
|
+
};
|
|
912
|
+
var _TuringMachine_tapeBlock, _TuringMachine_stack;
|
|
913
|
+
class TuringMachine {
|
|
914
|
+
constructor({ tapeBlock, } = {}) {
|
|
915
|
+
_TuringMachine_tapeBlock.set(this, void 0);
|
|
916
|
+
_TuringMachine_stack.set(this, []);
|
|
917
|
+
if (!tapeBlock) {
|
|
918
|
+
throw new Error('invalid tapeBlock');
|
|
919
|
+
}
|
|
920
|
+
__classPrivateFieldSet(this, _TuringMachine_tapeBlock, tapeBlock, "f");
|
|
921
|
+
}
|
|
922
|
+
get tapeBlock() {
|
|
923
|
+
return __classPrivateFieldGet(this, _TuringMachine_tapeBlock, "f");
|
|
924
|
+
}
|
|
925
|
+
run({ initialState, stepsLimit = 1e5, onStep }) {
|
|
926
|
+
const generator = this.runStepByStep({ initialState, stepsLimit });
|
|
927
|
+
for (const machineState of generator) {
|
|
928
|
+
if (onStep instanceof Function) {
|
|
929
|
+
onStep(machineState);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
*runStepByStep({ initialState, stepsLimit = 1e5 }) {
|
|
934
|
+
const executionSymbol = Symbol('execution');
|
|
935
|
+
try {
|
|
936
|
+
__classPrivateFieldGet(this, _TuringMachine_tapeBlock, "f")[lockSymbol].check(executionSymbol);
|
|
937
|
+
__classPrivateFieldGet(this, _TuringMachine_tapeBlock, "f")[lockSymbol].lock(executionSymbol);
|
|
938
|
+
const stack = __classPrivateFieldGet(this, _TuringMachine_stack, "f");
|
|
939
|
+
let state = initialState;
|
|
940
|
+
if (state.overrodeHaltState) {
|
|
941
|
+
stack.push(state.overrodeHaltState);
|
|
942
|
+
}
|
|
943
|
+
let i = 0;
|
|
944
|
+
while (!state.isHalt) {
|
|
945
|
+
if (i === stepsLimit) {
|
|
946
|
+
throw new Error('Long execution');
|
|
947
|
+
}
|
|
948
|
+
i += 1;
|
|
949
|
+
const symbol = state.getSymbol(__classPrivateFieldGet(this, _TuringMachine_tapeBlock, "f"));
|
|
950
|
+
const command = state.getCommand(symbol);
|
|
951
|
+
let nextState = state.getNextState(symbol).ref;
|
|
952
|
+
try {
|
|
953
|
+
const nextStateForYield = nextState.isHalt && stack.length
|
|
954
|
+
? stack.slice(-1)[0]
|
|
955
|
+
: nextState;
|
|
956
|
+
yield {
|
|
957
|
+
step: i,
|
|
958
|
+
state,
|
|
959
|
+
currentSymbols: __classPrivateFieldGet(this, _TuringMachine_tapeBlock, "f").currentSymbols,
|
|
960
|
+
nextSymbols: command.tapesCommands.map((tapeCommand, ix) => {
|
|
961
|
+
if (typeof tapeCommand.symbol === 'symbol') {
|
|
962
|
+
switch (tapeCommand.symbol) {
|
|
963
|
+
case symbolCommands.erase:
|
|
964
|
+
return __classPrivateFieldGet(this, _TuringMachine_tapeBlock, "f").tapes[ix].alphabet.blankSymbol;
|
|
965
|
+
case symbolCommands.keep:
|
|
966
|
+
return __classPrivateFieldGet(this, _TuringMachine_tapeBlock, "f").tapes[ix].symbol;
|
|
967
|
+
default:
|
|
968
|
+
throw new Error('invalid symbol command');
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
return tapeCommand.symbol;
|
|
972
|
+
}),
|
|
973
|
+
movements: command.tapesCommands.map((tapeCommand) => tapeCommand.movement),
|
|
974
|
+
nextState: nextStateForYield,
|
|
975
|
+
};
|
|
976
|
+
__classPrivateFieldGet(this, _TuringMachine_tapeBlock, "f").applyCommand(command, executionSymbol);
|
|
977
|
+
if (nextState.isHalt && stack.length) {
|
|
978
|
+
nextState = stack.pop();
|
|
979
|
+
}
|
|
980
|
+
if (state !== nextState && nextState.overrodeHaltState) {
|
|
981
|
+
stack.push(nextState.overrodeHaltState);
|
|
982
|
+
}
|
|
983
|
+
state = nextState;
|
|
984
|
+
}
|
|
985
|
+
catch (error) {
|
|
986
|
+
if (error !== haltState) {
|
|
987
|
+
throw error;
|
|
988
|
+
}
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
finally {
|
|
994
|
+
__classPrivateFieldGet(this, _TuringMachine_tapeBlock, "f")[lockSymbol].unlock(executionSymbol);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
_TuringMachine_tapeBlock = new WeakMap(), _TuringMachine_stack = new WeakMap();
|
|
999
|
+
|
|
1000
|
+
// Format converters between a Graph (the data model produced by State.toGraph
|
|
1001
|
+
// and consumed by State.fromGraph) and external string representations.
|
|
1002
|
+
//
|
|
1003
|
+
// Currently only Mermaid flowchart syntax is supported. Future formats
|
|
1004
|
+
// (Graphviz, JSON-LD, custom DSL) belong here too.
|
|
1005
|
+
function toMermaid(graph) {
|
|
1006
|
+
const lines = [
|
|
1007
|
+
'flowchart TD',
|
|
1008
|
+
`%% alphabets: ${JSON.stringify(graph.alphabets)}`,
|
|
1009
|
+
];
|
|
1010
|
+
for (const node of Object.values(graph.nodes)) {
|
|
1011
|
+
const id = `s${node.id}`;
|
|
1012
|
+
if (node.isHalt) {
|
|
1013
|
+
lines.push(` ${id}(((halt)))`);
|
|
1014
|
+
}
|
|
1015
|
+
else if (node.id === graph.initialId) {
|
|
1016
|
+
lines.push(` ${id}(("${node.name}"))`);
|
|
1017
|
+
}
|
|
1018
|
+
else {
|
|
1019
|
+
lines.push(` ${id}["${node.name}"]`);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
for (const node of Object.values(graph.nodes)) {
|
|
1023
|
+
for (const t of node.transitions) {
|
|
1024
|
+
// Per-tape commands separated with ',' to mirror the pattern syntax.
|
|
1025
|
+
const cmd = t.command.map((c) => `${c.symbol}/${c.movement}`).join(',');
|
|
1026
|
+
const label = `${t.pattern} → ${cmd}`;
|
|
1027
|
+
lines.push(` s${node.id} -- "${label}" --> s${t.nextStateId}`);
|
|
1028
|
+
}
|
|
1029
|
+
if (node.overrodeHaltStateId !== null) {
|
|
1030
|
+
lines.push(` s${node.id} -. onHalt .-> s${node.overrodeHaltStateId}`);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
return lines.join('\n');
|
|
1034
|
+
}
|
|
1035
|
+
// Inverse of toMermaid: parses the Mermaid output produced by toMermaid back
|
|
1036
|
+
// into a Graph. The parser is strict to the dialect toMermaid emits — it
|
|
1037
|
+
// recognises the specific node/edge shapes and the leading
|
|
1038
|
+
// `%% alphabets: [...]` comment. Hand-edited Mermaid that uses different
|
|
1039
|
+
// arrow styles or shapes will not parse.
|
|
1040
|
+
//
|
|
1041
|
+
// Caveats:
|
|
1042
|
+
// - Write-symbol cells in commands are split on '/' (last occurrence) and
|
|
1043
|
+
// per-tape segments are split on ','. If your alphabet contains '/' or ','
|
|
1044
|
+
// as literal symbols, the parser cannot disambiguate. Stick to alphabets
|
|
1045
|
+
// without those characters when round-tripping through Mermaid.
|
|
1046
|
+
const haltNodeRegex = /^s(\d+)\(\(\(halt\)\)\)$/;
|
|
1047
|
+
const initialNodeRegex = /^s(\d+)\(\("([^"]*)"\)\)$/;
|
|
1048
|
+
const regularNodeRegex = /^s(\d+)\["([^"]*)"\]$/;
|
|
1049
|
+
const transitionRegex = /^s(\d+)\s+--\s+"(.*)"\s+-->\s+s(\d+)$/;
|
|
1050
|
+
const onHaltRegex = /^s(\d+)\s+-\.\s+onHalt\s+\.->\s+s(\d+)$/;
|
|
1051
|
+
const alphabetsRegex = /^%%\s*alphabets:\s*(.+)$/;
|
|
1052
|
+
function fromMermaid(text) {
|
|
1053
|
+
const lines = text.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
1054
|
+
let alphabets = [];
|
|
1055
|
+
let initialId = null;
|
|
1056
|
+
const nodes = {};
|
|
1057
|
+
const ensureNode = (id, opts = {}) => {
|
|
1058
|
+
if (!nodes[id]) {
|
|
1059
|
+
nodes[id] = {
|
|
1060
|
+
id,
|
|
1061
|
+
name: opts.name ?? `s${id}`,
|
|
1062
|
+
isHalt: opts.isHalt ?? false,
|
|
1063
|
+
transitions: [],
|
|
1064
|
+
overrodeHaltStateId: null,
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
else {
|
|
1068
|
+
if (opts.name !== undefined) {
|
|
1069
|
+
nodes[id].name = opts.name;
|
|
1070
|
+
}
|
|
1071
|
+
if (opts.isHalt !== undefined) {
|
|
1072
|
+
nodes[id].isHalt = opts.isHalt;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
return nodes[id];
|
|
1076
|
+
};
|
|
1077
|
+
// First pass: alphabets + nodes.
|
|
1078
|
+
for (const line of lines) {
|
|
1079
|
+
if (line === 'flowchart TD') {
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
const am = line.match(alphabetsRegex);
|
|
1083
|
+
if (am) {
|
|
1084
|
+
alphabets = JSON.parse(am[1]);
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
const hm = line.match(haltNodeRegex);
|
|
1088
|
+
if (hm) {
|
|
1089
|
+
ensureNode(Number(hm[1]), { name: 'halt', isHalt: true });
|
|
1090
|
+
continue;
|
|
1091
|
+
}
|
|
1092
|
+
const im = line.match(initialNodeRegex);
|
|
1093
|
+
if (im) {
|
|
1094
|
+
const id = Number(im[1]);
|
|
1095
|
+
initialId = id;
|
|
1096
|
+
ensureNode(id, { name: im[2] });
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1099
|
+
const rm = line.match(regularNodeRegex);
|
|
1100
|
+
if (rm) {
|
|
1101
|
+
ensureNode(Number(rm[1]), { name: rm[2] });
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
// Second pass: edges.
|
|
1106
|
+
for (const line of lines) {
|
|
1107
|
+
const om = line.match(onHaltRegex);
|
|
1108
|
+
if (om) {
|
|
1109
|
+
ensureNode(Number(om[1])).overrodeHaltStateId = Number(om[2]);
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
1112
|
+
const tm = line.match(transitionRegex);
|
|
1113
|
+
if (tm) {
|
|
1114
|
+
const fromId = Number(tm[1]);
|
|
1115
|
+
const label = tm[2];
|
|
1116
|
+
const toId = Number(tm[3]);
|
|
1117
|
+
const arrowIx = label.indexOf(' → ');
|
|
1118
|
+
if (arrowIx === -1) {
|
|
1119
|
+
throw new Error(`fromMermaid: malformed edge label: "${label}"`);
|
|
1120
|
+
}
|
|
1121
|
+
const pattern = label.slice(0, arrowIx);
|
|
1122
|
+
const commandStr = label.slice(arrowIx + ' → '.length);
|
|
1123
|
+
const command = commandStr.split(',').map((part) => {
|
|
1124
|
+
const slashIx = part.lastIndexOf('/');
|
|
1125
|
+
if (slashIx === -1) {
|
|
1126
|
+
throw new Error(`fromMermaid: malformed command part: "${part}"`);
|
|
1127
|
+
}
|
|
1128
|
+
return {
|
|
1129
|
+
symbol: part.slice(0, slashIx),
|
|
1130
|
+
movement: part.slice(slashIx + 1),
|
|
1131
|
+
};
|
|
1132
|
+
});
|
|
1133
|
+
ensureNode(fromId).transitions.push({ pattern, command, nextStateId: toId });
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
if (initialId === null) {
|
|
1137
|
+
throw new Error('fromMermaid: no initial state (double-paren node) found');
|
|
1138
|
+
}
|
|
1139
|
+
return { initialId, alphabets, nodes };
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
function summarizeGraph(graph) {
|
|
1143
|
+
const nodes = Object.values(graph.nodes);
|
|
1144
|
+
let transitionCount = 0;
|
|
1145
|
+
let compositionEdgeCount = 0;
|
|
1146
|
+
let selfLoopCount = 0;
|
|
1147
|
+
for (const node of nodes) {
|
|
1148
|
+
transitionCount += node.transitions.length;
|
|
1149
|
+
if (node.overrodeHaltStateId !== null) {
|
|
1150
|
+
compositionEdgeCount += 1;
|
|
1151
|
+
}
|
|
1152
|
+
for (const t of node.transitions) {
|
|
1153
|
+
if (t.nextStateId === node.id) {
|
|
1154
|
+
selfLoopCount += 1;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
// Longest withOverrodeHaltState chain. Walks node → overrodeHaltState recursively;
|
|
1159
|
+
// a Set guards against cycles in the override graph (which throw at construction
|
|
1160
|
+
// time anyway, but being defensive costs little).
|
|
1161
|
+
const overrideDepthFrom = (id, visited) => {
|
|
1162
|
+
if (visited.has(id)) {
|
|
1163
|
+
return 0;
|
|
1164
|
+
}
|
|
1165
|
+
visited.add(id);
|
|
1166
|
+
const node = graph.nodes[id];
|
|
1167
|
+
if (!node || node.overrodeHaltStateId === null) {
|
|
1168
|
+
return 0;
|
|
1169
|
+
}
|
|
1170
|
+
return 1 + overrideDepthFrom(node.overrodeHaltStateId, visited);
|
|
1171
|
+
};
|
|
1172
|
+
const maxCompositionDepth = nodes.reduce((max, node) => Math.max(max, overrideDepthFrom(node.id, new Set())), 0);
|
|
1173
|
+
// Cycle detection: tri-color DFS over the transition graph.
|
|
1174
|
+
const WHITE = 0;
|
|
1175
|
+
const GREY = 1;
|
|
1176
|
+
const BLACK = 2;
|
|
1177
|
+
const color = new Map();
|
|
1178
|
+
for (const node of nodes) {
|
|
1179
|
+
color.set(node.id, WHITE);
|
|
1180
|
+
}
|
|
1181
|
+
let hasCycles = false;
|
|
1182
|
+
const visit = (id) => {
|
|
1183
|
+
if (hasCycles) {
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
if (color.get(id) === GREY) {
|
|
1187
|
+
hasCycles = true;
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
if (color.get(id) === BLACK) {
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
color.set(id, GREY);
|
|
1194
|
+
const node = graph.nodes[id];
|
|
1195
|
+
if (node) {
|
|
1196
|
+
for (const t of node.transitions) {
|
|
1197
|
+
visit(t.nextStateId);
|
|
1198
|
+
if (hasCycles) {
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
color.set(id, BLACK);
|
|
1204
|
+
};
|
|
1205
|
+
for (const node of nodes) {
|
|
1206
|
+
if (hasCycles) {
|
|
1207
|
+
break;
|
|
1208
|
+
}
|
|
1209
|
+
visit(node.id);
|
|
1210
|
+
}
|
|
1211
|
+
return {
|
|
1212
|
+
stateCount: nodes.length,
|
|
1213
|
+
transitionCount,
|
|
1214
|
+
compositionEdgeCount,
|
|
1215
|
+
maxCompositionDepth,
|
|
1216
|
+
selfLoopCount,
|
|
1217
|
+
hasCycles,
|
|
1218
|
+
tapeCount: graph.alphabets.length,
|
|
1219
|
+
alphabetCardinalities: graph.alphabets.map((a) => a.length),
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
// Convenience: build the graph and summarize in one step.
|
|
1223
|
+
function summarize(state, tapeBlock) {
|
|
1224
|
+
return summarizeGraph(State.toGraph(state, tapeBlock));
|
|
1225
|
+
}
|
|
1226
|
+
// Behavioral equivalence checking (the testing-tool counterpart to introspection)
|
|
1227
|
+
// lives in ./equivalence — kept separate because it runs machines and compares
|
|
1228
|
+
// outputs rather than examining structure.
|
|
1229
|
+
|
|
1230
|
+
const defaultCompare = (a, b) => a === b;
|
|
1231
|
+
function equivalentOn(reference, candidate, cases, options = {}) {
|
|
1232
|
+
const compareOutputs = options.compareOutputs ?? defaultCompare;
|
|
1233
|
+
const compareSnapshots = options.compareSnapshots === undefined
|
|
1234
|
+
? defaultCompare
|
|
1235
|
+
: options.compareSnapshots;
|
|
1236
|
+
const stepsLimit = options.stepsLimit ?? 1e5;
|
|
1237
|
+
const results = cases.map((c) => {
|
|
1238
|
+
const pair = typeof c === 'string' ? { reference: c, candidate: c } : c;
|
|
1239
|
+
const refRun = runOnce(reference, pair.reference, stepsLimit);
|
|
1240
|
+
const candRun = runOnce(candidate, pair.candidate, stepsLimit);
|
|
1241
|
+
const agree = compareOutputs(refRun.finalOutput, candRun.finalOutput);
|
|
1242
|
+
let firstDivergenceStep = null;
|
|
1243
|
+
if (!agree && compareSnapshots !== null) {
|
|
1244
|
+
const minLen = Math.min(refRun.snapshots.length, candRun.snapshots.length);
|
|
1245
|
+
for (let i = 0; i < minLen; i += 1) {
|
|
1246
|
+
if (!compareSnapshots(refRun.snapshots[i], candRun.snapshots[i])) {
|
|
1247
|
+
firstDivergenceStep = i;
|
|
1248
|
+
break;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
if (firstDivergenceStep === null && refRun.snapshots.length !== candRun.snapshots.length) {
|
|
1252
|
+
firstDivergenceStep = minLen;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
return {
|
|
1256
|
+
case: pair,
|
|
1257
|
+
agree,
|
|
1258
|
+
referenceOutput: refRun.finalOutput,
|
|
1259
|
+
candidateOutput: candRun.finalOutput,
|
|
1260
|
+
referenceSteps: refRun.stepCount,
|
|
1261
|
+
candidateSteps: candRun.stepCount,
|
|
1262
|
+
firstDivergenceStep,
|
|
1263
|
+
};
|
|
1264
|
+
});
|
|
1265
|
+
return {
|
|
1266
|
+
results,
|
|
1267
|
+
allAgree: results.every((r) => r.agree),
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
// Single-machine runner: snapshots the tape after each step and returns the
|
|
1271
|
+
// final output, the snapshot list, and the step count.
|
|
1272
|
+
function runOnce(runnable, input, stepsLimit) {
|
|
1273
|
+
const tapeBlock = runnable.getTapeBlock();
|
|
1274
|
+
const tape = new Tape({
|
|
1275
|
+
alphabet: tapeBlock.tapes[0].alphabet,
|
|
1276
|
+
symbols: input.split(''),
|
|
1277
|
+
});
|
|
1278
|
+
tapeBlock.replaceTape(tape);
|
|
1279
|
+
const machine = new TuringMachine({ tapeBlock });
|
|
1280
|
+
const snapshots = [];
|
|
1281
|
+
let stepCount = 0;
|
|
1282
|
+
// Inside the for-of body, the tape reflects the state BEFORE the current
|
|
1283
|
+
// step's command (i.e. AFTER the previous step's command — or initial for
|
|
1284
|
+
// step 1). After the loop, the tape has had every command applied.
|
|
1285
|
+
for (const _ of machine.runStepByStep({ initialState: runnable.state, stepsLimit })) {
|
|
1286
|
+
snapshots.push(tape.symbols.join(''));
|
|
1287
|
+
stepCount += 1;
|
|
1288
|
+
}
|
|
1289
|
+
snapshots.push(tape.symbols.join(''));
|
|
1290
|
+
return {
|
|
1291
|
+
finalOutput: tape.symbols.join('').trim(),
|
|
1292
|
+
snapshots,
|
|
1293
|
+
stepCount,
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
exports.Alphabet = Alphabet;
|
|
1298
|
+
exports.Command = Command;
|
|
1299
|
+
exports.Reference = Reference;
|
|
1300
|
+
exports.State = State;
|
|
1301
|
+
exports.Tape = Tape;
|
|
1302
|
+
exports.TapeBlock = TapeBlock;
|
|
1303
|
+
exports.TapeCommand = TapeCommand;
|
|
1304
|
+
exports.TuringMachine = TuringMachine;
|
|
1305
|
+
exports.equivalentOn = equivalentOn;
|
|
1306
|
+
exports.fromMermaid = fromMermaid;
|
|
1307
|
+
exports.haltState = haltState;
|
|
1308
|
+
exports.ifOtherSymbol = ifOtherSymbol;
|
|
1309
|
+
exports.movements = movements;
|
|
1310
|
+
exports.summarize = summarize;
|
|
1311
|
+
exports.summarizeGraph = summarizeGraph;
|
|
1312
|
+
exports.symbolCommands = symbolCommands;
|
|
1313
|
+
exports.toMermaid = toMermaid;
|