@turing-machine-js/machine 2.0.2 → 3.0.1

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/dist/index.cjs CHANGED
@@ -175,121 +175,13 @@ class Reference {
175
175
  }
176
176
  _Reference_referenceBinding = new WeakMap();
177
177
 
178
- var __classPrivateFieldGet$4 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
179
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
180
- 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");
181
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
182
- };
183
178
  var __classPrivateFieldSet$4 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
184
179
  if (kind === "m") throw new TypeError("Private method is not writable");
185
180
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
186
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");
187
182
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
188
183
  };
189
- var _State_id, _State_name, _State_overrodeHaltState, _State_symbolToDataMap;
190
- const ifOtherSymbol = Symbol('other symbol');
191
- class State {
192
- constructor(stateDefinition = null, name) {
193
- _State_id.set(this, id(this));
194
- _State_name.set(this, void 0);
195
- _State_overrodeHaltState.set(this, null);
196
- _State_symbolToDataMap.set(this, new Map());
197
- if (stateDefinition) {
198
- const keys = Object.getOwnPropertyNames(stateDefinition);
199
- if (keys.length) {
200
- throw new Error(`invalid state definition while constructing state #${__classPrivateFieldGet$4(this, _State_id, "f")}`);
201
- }
202
- const symbols = Object.getOwnPropertySymbols(stateDefinition);
203
- if (symbols.length === 0) {
204
- throw new Error(`invalid state definition while constructing state #${__classPrivateFieldGet$4(this, _State_id, "f")}`);
205
- }
206
- symbols.forEach((symbol) => {
207
- const { nextState } = stateDefinition[symbol];
208
- const nextStateLocal = nextState !== null && nextState !== void 0 ? nextState : this;
209
- if (!(nextStateLocal instanceof State) && !(nextStateLocal instanceof Reference)) {
210
- throw new Error('invalid nextState');
211
- }
212
- let { command } = stateDefinition[symbol];
213
- if (command == null) {
214
- command = new Command([
215
- new TapeCommand({}),
216
- ]);
217
- }
218
- if (!(command instanceof Command) && !Array.isArray(command)) {
219
- command = [command];
220
- }
221
- let commandLocal = command;
222
- if (Array.isArray(command)) {
223
- try {
224
- commandLocal = new Command(command);
225
- }
226
- catch (error) {
227
- }
228
- }
229
- if (!(commandLocal instanceof Command)) {
230
- throw new Error('invalid command');
231
- }
232
- __classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").set(symbol, {
233
- command: commandLocal,
234
- nextState: nextStateLocal,
235
- });
236
- });
237
- }
238
- __classPrivateFieldSet$4(this, _State_name, name !== null && name !== void 0 ? name : `id:${__classPrivateFieldGet$4(this, _State_id, "f")}`, "f");
239
- }
240
- get id() {
241
- return __classPrivateFieldGet$4(this, _State_id, "f");
242
- }
243
- get name() {
244
- return __classPrivateFieldGet$4(this, _State_name, "f");
245
- }
246
- get isHalt() {
247
- return __classPrivateFieldGet$4(this, _State_id, "f") === 0;
248
- }
249
- get overrodeHaltState() {
250
- return __classPrivateFieldGet$4(this, _State_overrodeHaltState, "f");
251
- }
252
- get ref() {
253
- return this;
254
- }
255
- getSymbol(tapeBlock) {
256
- const symbol = [...__classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").keys()].find((currentSymbol) => tapeBlock.isMatched({
257
- symbol: currentSymbol,
258
- }));
259
- if (symbol) {
260
- return symbol;
261
- }
262
- return ifOtherSymbol;
263
- }
264
- getCommand(symbol) {
265
- if (__classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").has(symbol)) {
266
- return __classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").get(symbol).command;
267
- }
268
- throw new Error(`No command for symbol at state named ${__classPrivateFieldGet$4(this, _State_name, "f")}`);
269
- }
270
- getNextState(symbol) {
271
- if (__classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").has(symbol)) {
272
- return __classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").get(symbol).nextState;
273
- }
274
- throw new Error(`No nextState for symbol at state named ${__classPrivateFieldGet$4(this, _State_id, "f")}`);
275
- }
276
- withOverrodeHaltState(overrodeHaltState) {
277
- const state = new State(null, `${this.name}>${overrodeHaltState.name}`);
278
- __classPrivateFieldSet$4(state, _State_symbolToDataMap, __classPrivateFieldGet$4(this, _State_symbolToDataMap, "f"), "f");
279
- __classPrivateFieldSet$4(state, _State_overrodeHaltState, overrodeHaltState, "f");
280
- return state;
281
- }
282
- }
283
- _State_id = new WeakMap(), _State_name = new WeakMap(), _State_overrodeHaltState = new WeakMap(), _State_symbolToDataMap = new WeakMap();
284
- const haltState = new State(null);
285
-
286
- var __classPrivateFieldSet$3 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
287
- if (kind === "m") throw new TypeError("Private method is not writable");
288
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
289
- 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");
290
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
291
- };
292
- var __classPrivateFieldGet$3 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
184
+ var __classPrivateFieldGet$4 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
293
185
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
294
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");
295
187
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
@@ -305,46 +197,46 @@ class Tape {
305
197
  if (!isSymbolsValid) {
306
198
  throw new Error('symbolList contains invalid symbol');
307
199
  }
308
- __classPrivateFieldSet$3(this, _Tape_alphabet, new Alphabet(alphabet), "f");
309
- __classPrivateFieldSet$3(this, _Tape_position, position, "f");
310
- __classPrivateFieldSet$3(this, _Tape_viewportWidth, viewportWidth, "f");
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");
311
203
  const symbolsCopy = Array.from(symbols);
312
204
  if (symbolsCopy.length === 0) {
313
- symbolsCopy.push(__classPrivateFieldGet$3(this, _Tape_alphabet, "f").blankSymbol);
205
+ symbolsCopy.push(__classPrivateFieldGet$4(this, _Tape_alphabet, "f").blankSymbol);
314
206
  }
315
- __classPrivateFieldSet$3(this, _Tape_symbols, symbolsCopy.map((symbol) => __classPrivateFieldGet$3(this, _Tape_alphabet, "f").index(symbol)), "f");
207
+ __classPrivateFieldSet$4(this, _Tape_symbols, symbolsCopy.map((symbol) => __classPrivateFieldGet$4(this, _Tape_alphabet, "f").index(symbol)), "f");
316
208
  }
317
209
  get alphabet() {
318
- return __classPrivateFieldGet$3(this, _Tape_alphabet, "f");
210
+ return __classPrivateFieldGet$4(this, _Tape_alphabet, "f");
319
211
  }
320
212
  get extraCellsCount() {
321
- return (__classPrivateFieldGet$3(this, _Tape_viewportWidth, "f") - 1) / 2;
213
+ return (__classPrivateFieldGet$4(this, _Tape_viewportWidth, "f") - 1) / 2;
322
214
  }
323
215
  get position() {
324
- return __classPrivateFieldGet$3(this, _Tape_position, "f");
216
+ return __classPrivateFieldGet$4(this, _Tape_position, "f");
325
217
  }
326
218
  get symbol() {
327
- return __classPrivateFieldGet$3(this, _Tape_alphabet, "f").get(__classPrivateFieldGet$3(this, _Tape_symbols, "f")[__classPrivateFieldGet$3(this, _Tape_position, "f")]);
219
+ return __classPrivateFieldGet$4(this, _Tape_alphabet, "f").get(__classPrivateFieldGet$4(this, _Tape_symbols, "f")[__classPrivateFieldGet$4(this, _Tape_position, "f")]);
328
220
  }
329
221
  set symbol(symbol) {
330
- if (!__classPrivateFieldGet$3(this, _Tape_alphabet, "f").has(symbol)) {
222
+ if (!__classPrivateFieldGet$4(this, _Tape_alphabet, "f").has(symbol)) {
331
223
  throw new Error('Invalid symbol');
332
224
  }
333
- __classPrivateFieldGet$3(this, _Tape_symbols, "f")[__classPrivateFieldGet$3(this, _Tape_position, "f")] = __classPrivateFieldGet$3(this, _Tape_alphabet, "f").index(symbol);
225
+ __classPrivateFieldGet$4(this, _Tape_symbols, "f")[__classPrivateFieldGet$4(this, _Tape_position, "f")] = __classPrivateFieldGet$4(this, _Tape_alphabet, "f").index(symbol);
334
226
  }
335
227
  get symbols() {
336
- return __classPrivateFieldGet$3(this, _Tape_symbols, "f")
337
- .map((index) => __classPrivateFieldGet$3(this, _Tape_alphabet, "f").get(index));
228
+ return __classPrivateFieldGet$4(this, _Tape_symbols, "f")
229
+ .map((index) => __classPrivateFieldGet$4(this, _Tape_alphabet, "f").get(index));
338
230
  }
339
231
  get viewport() {
340
- const startIx = __classPrivateFieldGet$3(this, _Tape_position, "f") - this.extraCellsCount;
341
- const endIx = __classPrivateFieldGet$3(this, _Tape_position, "f") + this.extraCellsCount + 1;
342
- return __classPrivateFieldGet$3(this, _Tape_symbols, "f")
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")
343
235
  .slice(startIx, endIx)
344
- .map((index) => __classPrivateFieldGet$3(this, _Tape_alphabet, "f").get(index));
236
+ .map((index) => __classPrivateFieldGet$4(this, _Tape_alphabet, "f").get(index));
345
237
  }
346
238
  get viewportWidth() {
347
- return __classPrivateFieldGet$3(this, _Tape_viewportWidth, "f");
239
+ return __classPrivateFieldGet$4(this, _Tape_viewportWidth, "f");
348
240
  }
349
241
  set viewportWidth(width) {
350
242
  let finalWidth = width;
@@ -354,35 +246,35 @@ class Tape {
354
246
  if (finalWidth % 2 === 0) {
355
247
  finalWidth += 1;
356
248
  }
357
- __classPrivateFieldSet$3(this, _Tape_viewportWidth, finalWidth, "f");
249
+ __classPrivateFieldSet$4(this, _Tape_viewportWidth, finalWidth, "f");
358
250
  this.normalise();
359
251
  }
360
252
  left() {
361
- __classPrivateFieldSet$3(this, _Tape_position, __classPrivateFieldGet$3(this, _Tape_position, "f") - 1, "f");
253
+ __classPrivateFieldSet$4(this, _Tape_position, __classPrivateFieldGet$4(this, _Tape_position, "f") - 1, "f");
362
254
  this.normalise();
363
255
  }
364
256
  normalise() {
365
- while (__classPrivateFieldGet$3(this, _Tape_position, "f") - this.extraCellsCount < 0) {
366
- __classPrivateFieldGet$3(this, _Tape_symbols, "f").unshift(0);
367
- __classPrivateFieldSet$3(this, _Tape_position, __classPrivateFieldGet$3(this, _Tape_position, "f") + 1, "f");
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");
368
260
  }
369
- while (__classPrivateFieldGet$3(this, _Tape_position, "f") + this.extraCellsCount >= __classPrivateFieldGet$3(this, _Tape_symbols, "f").length) {
370
- __classPrivateFieldGet$3(this, _Tape_symbols, "f").push(0);
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);
371
263
  }
372
264
  }
373
265
  right() {
374
- __classPrivateFieldSet$3(this, _Tape_position, __classPrivateFieldGet$3(this, _Tape_position, "f") + 1, "f");
266
+ __classPrivateFieldSet$4(this, _Tape_position, __classPrivateFieldGet$4(this, _Tape_position, "f") + 1, "f");
375
267
  this.normalise();
376
268
  }
377
269
  }
378
270
  _Tape_alphabet = new WeakMap(), _Tape_symbols = new WeakMap(), _Tape_position = new WeakMap(), _Tape_viewportWidth = new WeakMap();
379
271
 
380
- var __classPrivateFieldGet$2 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
272
+ var __classPrivateFieldGet$3 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
381
273
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
382
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");
383
275
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
384
276
  };
385
- var __classPrivateFieldSet$2 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
277
+ var __classPrivateFieldSet$3 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
386
278
  if (kind === "m") throw new TypeError("Private method is not writable");
387
279
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
388
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");
@@ -394,30 +286,30 @@ class Lock {
394
286
  _Lock_lockSymbol.set(this, null);
395
287
  }
396
288
  lock(symbol) {
397
- if (__classPrivateFieldGet$2(this, _Lock_lockSymbol, "f") === null) {
398
- __classPrivateFieldSet$2(this, _Lock_lockSymbol, symbol, "f");
289
+ if (__classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") === null) {
290
+ __classPrivateFieldSet$3(this, _Lock_lockSymbol, symbol, "f");
399
291
  }
400
292
  }
401
293
  unlock(symbol) {
402
- if (__classPrivateFieldGet$2(this, _Lock_lockSymbol, "f") === symbol) {
403
- __classPrivateFieldSet$2(this, _Lock_lockSymbol, null, "f");
294
+ if (__classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") === symbol) {
295
+ __classPrivateFieldSet$3(this, _Lock_lockSymbol, null, "f");
404
296
  }
405
297
  }
406
298
  check(symbol) {
407
- if (__classPrivateFieldGet$2(this, _Lock_lockSymbol, "f") && __classPrivateFieldGet$2(this, _Lock_lockSymbol, "f") !== symbol) {
299
+ if (__classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") && __classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") !== symbol) {
408
300
  throw new Error('Lock check failed');
409
301
  }
410
302
  }
411
303
  }
412
304
  _Lock_lockSymbol = new WeakMap();
413
305
 
414
- var __classPrivateFieldSet$1 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
306
+ var __classPrivateFieldSet$2 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
415
307
  if (kind === "m") throw new TypeError("Private method is not writable");
416
308
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
417
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");
418
310
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
419
311
  };
420
- var __classPrivateFieldGet$1 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
312
+ var __classPrivateFieldGet$2 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
421
313
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
422
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");
423
315
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
@@ -431,7 +323,7 @@ class TapeBlock {
431
323
  _TapeBlock_lock.set(this, new Lock());
432
324
  _TapeBlock_tapes.set(this, void 0);
433
325
  _TapeBlock_buildPatternList.set(this, (symbolList) => symbolList.reduce((result, symbol, ix) => {
434
- const row = Math.floor(ix / __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").length);
326
+ const row = Math.floor(ix / __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length);
435
327
  if (!Array.isArray(result[row])) {
436
328
  result[row] = [];
437
329
  }
@@ -447,7 +339,7 @@ class TapeBlock {
447
339
  if (patternList.some((pattern) => pattern.every((symbol) => symbol === ifOtherSymbol))) {
448
340
  return ifOtherSymbol;
449
341
  }
450
- const [storedPatternListSymbol] = [...__classPrivateFieldGet$1(this, _TapeBlock_symbolToPatternListMap, "f").entries()]
342
+ const [storedPatternListSymbol] = [...__classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").entries()]
451
343
  .find(([, storedPatternList]) => {
452
344
  if (storedPatternList.length !== patternList.length) {
453
345
  return false;
@@ -461,8 +353,8 @@ class TapeBlock {
461
353
  symbol = storedPatternListSymbol;
462
354
  }
463
355
  else {
464
- symbol = Symbol(__classPrivateFieldGet$1(_a, _a, "f", _TapeBlock_generateSymbolHint).call(_a, patternList));
465
- __classPrivateFieldGet$1(this, _TapeBlock_symbolToPatternListMap, "f").set(symbol, patternList);
356
+ symbol = Symbol(__classPrivateFieldGet$2(_a, _a, "f", _TapeBlock_generateSymbolHint).call(_a, patternList));
357
+ __classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").set(symbol, patternList);
466
358
  }
467
359
  return symbol;
468
360
  });
@@ -477,60 +369,60 @@ class TapeBlock {
477
369
  else if (Array.isArray(symbols)) {
478
370
  localSymbols = [...symbols];
479
371
  }
480
- if (localSymbols.length === 0 || localSymbols.length % __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").length > 0) {
372
+ if (localSymbols.length === 0 || localSymbols.length % __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length > 0) {
481
373
  throw new Error('invalid symbol parameter');
482
374
  }
483
375
  const invalidSymbolIndex = localSymbols.findIndex((symbol, ix) => (symbol !== ifOtherSymbol
484
- && !__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f")[ix % __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").length].alphabet.has(symbol)));
376
+ && !__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")[ix % __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length].alphabet.has(symbol)));
485
377
  if (invalidSymbolIndex >= 0) {
486
378
  throw new Error('invalid symbol parameter');
487
379
  }
488
380
  if (localSymbols.every((symbol) => symbol === ifOtherSymbol)) {
489
381
  return ifOtherSymbol;
490
382
  }
491
- return __classPrivateFieldGet$1(this, _TapeBlock_getSymbolForPatternList, "f").call(this, __classPrivateFieldGet$1(this, _TapeBlock_buildPatternList, "f").call(this, localSymbols));
383
+ return __classPrivateFieldGet$2(this, _TapeBlock_getSymbolForPatternList, "f").call(this, __classPrivateFieldGet$2(this, _TapeBlock_buildPatternList, "f").call(this, localSymbols));
492
384
  });
493
- __classPrivateFieldSet$1(this, _TapeBlock_tapes, [], "f");
385
+ __classPrivateFieldSet$2(this, _TapeBlock_tapes, [], "f");
494
386
  if ('alphabets' in argument) {
495
387
  const { alphabets } = argument;
496
388
  if (alphabets.length === 0) {
497
389
  throw new Error('empty alphabet list');
498
390
  }
499
- __classPrivateFieldSet$1(this, _TapeBlock_tapes, alphabets.map((alphabet) => new Tape({
391
+ __classPrivateFieldSet$2(this, _TapeBlock_tapes, alphabets.map((alphabet) => new Tape({
500
392
  alphabet,
501
393
  })), "f");
502
394
  }
503
395
  else if ('tapes' in argument) {
504
- __classPrivateFieldSet$1(this, _TapeBlock_tapes, argument.tapes, "f");
396
+ __classPrivateFieldSet$2(this, _TapeBlock_tapes, argument.tapes, "f");
505
397
  }
506
- if (__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").length === 0) {
398
+ if (__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length === 0) {
507
399
  throw new Error('empty tape list');
508
400
  }
509
401
  }
510
402
  get alphabets() {
511
- return [...__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").map((tape) => tape.alphabet)];
403
+ return [...__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").map((tape) => tape.alphabet)];
512
404
  }
513
405
  get currentSymbols() {
514
- return __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").map((tape) => tape.symbol);
406
+ return __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").map((tape) => tape.symbol);
515
407
  }
516
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)]() {
517
- return __classPrivateFieldGet$1(this, _TapeBlock_lock, "f");
409
+ return __classPrivateFieldGet$2(this, _TapeBlock_lock, "f");
518
410
  }
519
411
  get symbol() {
520
- return __classPrivateFieldGet$1(this, _TapeBlock_symbol, "f").bind(this);
412
+ return __classPrivateFieldGet$2(this, _TapeBlock_symbol, "f").bind(this);
521
413
  }
522
414
  get tapes() {
523
- return [...__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f")];
415
+ return [...__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")];
524
416
  }
525
417
  set [symbolToPatternListMapSymbol](symbolToPatternListMap) {
526
- __classPrivateFieldSet$1(this, _TapeBlock_symbolToPatternListMap, new Map(symbolToPatternListMap), "f");
418
+ __classPrivateFieldSet$2(this, _TapeBlock_symbolToPatternListMap, new Map(symbolToPatternListMap), "f");
527
419
  }
528
420
  applyCommand(command, executionSymbol = null) {
529
- __classPrivateFieldGet$1(this, _TapeBlock_lock, "f").check(executionSymbol);
530
- if (__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").length !== command.tapesCommands.length) {
421
+ __classPrivateFieldGet$2(this, _TapeBlock_lock, "f").check(executionSymbol);
422
+ if (__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length !== command.tapesCommands.length) {
531
423
  throw new Error('invalid command');
532
424
  }
533
- __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").forEach((tape, ix) => {
425
+ __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").forEach((tape, ix) => {
534
426
  const { movement, symbol } = command.tapesCommands[ix];
535
427
  if (typeof symbol === 'string') {
536
428
  tape.symbol = symbol;
@@ -566,28 +458,27 @@ class TapeBlock {
566
458
  else {
567
459
  tapeBlock = _a.fromAlphabets(this.alphabets);
568
460
  }
569
- tapeBlock[symbolToPatternListMapSymbol] = __classPrivateFieldGet$1(this, _TapeBlock_symbolToPatternListMap, "f");
461
+ tapeBlock[symbolToPatternListMapSymbol] = __classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f");
570
462
  return tapeBlock;
571
463
  }
572
464
  isMatched({ currentSymbols = this.currentSymbols, symbol }) {
573
- var _b;
574
465
  if (symbol === ifOtherSymbol) {
575
466
  return true;
576
467
  }
577
- if (!__classPrivateFieldGet$1(this, _TapeBlock_symbolToPatternListMap, "f").has(symbol)) {
468
+ if (!__classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").has(symbol)) {
578
469
  throw new Error('invalid symbol');
579
470
  }
580
- const patternList = __classPrivateFieldGet$1(this, _TapeBlock_symbolToPatternListMap, "f").get(symbol);
581
- return (_b = patternList === null || patternList === void 0 ? void 0 : patternList.some((pattern) => (pattern
471
+ const patternList = __classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").get(symbol);
472
+ return patternList?.some((pattern) => (pattern
582
473
  .every((everySymbol, ix) => (everySymbol === ifOtherSymbol
583
- || everySymbol === currentSymbols[ix]))))) !== null && _b !== void 0 ? _b : false;
474
+ || everySymbol === currentSymbols[ix])))) ?? false;
584
475
  }
585
476
  replaceTape(tape, tapeIx = 0) {
586
- if (__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f")[tapeIx] == null) {
477
+ if (__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")[tapeIx] == null) {
587
478
  throw new Error('invalid tapeIx');
588
479
  }
589
- if (tape.alphabet.symbols.join('') === __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f")[tapeIx].alphabet.symbols.join('')) {
590
- __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f")[tapeIx] = tape;
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;
591
482
  }
592
483
  else {
593
484
  throw new Error('invalid tape');
@@ -605,6 +496,408 @@ _TapeBlock_generateSymbolHint = { value: (patternList) => JSON.stringify(pattern
605
496
  .map((pattern) => pattern
606
497
  .map((symbol) => (symbol === ifOtherSymbol ? null : symbol)))) };
607
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
+
608
901
  var __classPrivateFieldSet = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
609
902
  if (kind === "m") throw new TypeError("Private method is not writable");
610
903
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
@@ -704,6 +997,310 @@ class TuringMachine {
704
997
  }
705
998
  _TuringMachine_tapeBlock = new WeakMap(), _TuringMachine_stack = new WeakMap();
706
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
+ // First capture char anchored as \S to avoid polynomial backtracking between
1052
+ // the preceding \s* and a permissive (.+); see CodeQL js/polynomial-redos.
1053
+ const alphabetsRegex = /^%%\s*alphabets:\s*(\S.*)$/;
1054
+ function fromMermaid(text) {
1055
+ const lines = text.split('\n').map((l) => l.trim()).filter(Boolean);
1056
+ let alphabets = [];
1057
+ let initialId = null;
1058
+ const nodes = {};
1059
+ const ensureNode = (id, opts = {}) => {
1060
+ if (!nodes[id]) {
1061
+ nodes[id] = {
1062
+ id,
1063
+ name: opts.name ?? `s${id}`,
1064
+ isHalt: opts.isHalt ?? false,
1065
+ transitions: [],
1066
+ overrodeHaltStateId: null,
1067
+ };
1068
+ }
1069
+ else {
1070
+ if (opts.name !== undefined) {
1071
+ nodes[id].name = opts.name;
1072
+ }
1073
+ if (opts.isHalt !== undefined) {
1074
+ nodes[id].isHalt = opts.isHalt;
1075
+ }
1076
+ }
1077
+ return nodes[id];
1078
+ };
1079
+ // First pass: alphabets + nodes.
1080
+ for (const line of lines) {
1081
+ if (line === 'flowchart TD') {
1082
+ continue;
1083
+ }
1084
+ const am = line.match(alphabetsRegex);
1085
+ if (am) {
1086
+ alphabets = JSON.parse(am[1]);
1087
+ continue;
1088
+ }
1089
+ const hm = line.match(haltNodeRegex);
1090
+ if (hm) {
1091
+ ensureNode(Number(hm[1]), { name: 'halt', isHalt: true });
1092
+ continue;
1093
+ }
1094
+ const im = line.match(initialNodeRegex);
1095
+ if (im) {
1096
+ const id = Number(im[1]);
1097
+ initialId = id;
1098
+ ensureNode(id, { name: im[2] });
1099
+ continue;
1100
+ }
1101
+ const rm = line.match(regularNodeRegex);
1102
+ if (rm) {
1103
+ ensureNode(Number(rm[1]), { name: rm[2] });
1104
+ continue;
1105
+ }
1106
+ }
1107
+ // Second pass: edges.
1108
+ for (const line of lines) {
1109
+ const om = line.match(onHaltRegex);
1110
+ if (om) {
1111
+ ensureNode(Number(om[1])).overrodeHaltStateId = Number(om[2]);
1112
+ continue;
1113
+ }
1114
+ const tm = line.match(transitionRegex);
1115
+ if (tm) {
1116
+ const fromId = Number(tm[1]);
1117
+ const label = tm[2];
1118
+ const toId = Number(tm[3]);
1119
+ const arrowIx = label.indexOf(' → ');
1120
+ if (arrowIx === -1) {
1121
+ throw new Error(`fromMermaid: malformed edge label: "${label}"`);
1122
+ }
1123
+ const pattern = label.slice(0, arrowIx);
1124
+ const commandStr = label.slice(arrowIx + ' → '.length);
1125
+ const command = commandStr.split(',').map((part) => {
1126
+ const slashIx = part.lastIndexOf('/');
1127
+ if (slashIx === -1) {
1128
+ throw new Error(`fromMermaid: malformed command part: "${part}"`);
1129
+ }
1130
+ return {
1131
+ symbol: part.slice(0, slashIx),
1132
+ movement: part.slice(slashIx + 1),
1133
+ };
1134
+ });
1135
+ ensureNode(fromId).transitions.push({ pattern, command, nextStateId: toId });
1136
+ }
1137
+ }
1138
+ if (initialId === null) {
1139
+ throw new Error('fromMermaid: no initial state (double-paren node) found');
1140
+ }
1141
+ return { initialId, alphabets, nodes };
1142
+ }
1143
+
1144
+ function summarizeGraph(graph) {
1145
+ const nodes = Object.values(graph.nodes);
1146
+ let transitionCount = 0;
1147
+ let compositionEdgeCount = 0;
1148
+ let selfLoopCount = 0;
1149
+ for (const node of nodes) {
1150
+ transitionCount += node.transitions.length;
1151
+ if (node.overrodeHaltStateId !== null) {
1152
+ compositionEdgeCount += 1;
1153
+ }
1154
+ for (const t of node.transitions) {
1155
+ if (t.nextStateId === node.id) {
1156
+ selfLoopCount += 1;
1157
+ }
1158
+ }
1159
+ }
1160
+ // Longest withOverrodeHaltState chain. Walks node → overrodeHaltState recursively;
1161
+ // a Set guards against cycles in the override graph (which throw at construction
1162
+ // time anyway, but being defensive costs little).
1163
+ const overrideDepthFrom = (id, visited) => {
1164
+ if (visited.has(id)) {
1165
+ return 0;
1166
+ }
1167
+ visited.add(id);
1168
+ const node = graph.nodes[id];
1169
+ if (!node || node.overrodeHaltStateId === null) {
1170
+ return 0;
1171
+ }
1172
+ return 1 + overrideDepthFrom(node.overrodeHaltStateId, visited);
1173
+ };
1174
+ const maxCompositionDepth = nodes.reduce((max, node) => Math.max(max, overrideDepthFrom(node.id, new Set())), 0);
1175
+ // Cycle detection: tri-color DFS over the transition graph.
1176
+ const WHITE = 0;
1177
+ const GREY = 1;
1178
+ const BLACK = 2;
1179
+ const color = new Map();
1180
+ for (const node of nodes) {
1181
+ color.set(node.id, WHITE);
1182
+ }
1183
+ let hasCycles = false;
1184
+ const visit = (id) => {
1185
+ // No `if (hasCycles) return` guard at function entry: the recursive call
1186
+ // pattern (outer for-loop checks before calling, inner loop checks after
1187
+ // each recursive call) ensures visit() is never invoked when hasCycles
1188
+ // is already true. Static analysis confirmed the guard was unreachable.
1189
+ if (color.get(id) === GREY) {
1190
+ hasCycles = true;
1191
+ return;
1192
+ }
1193
+ if (color.get(id) === BLACK) {
1194
+ return;
1195
+ }
1196
+ color.set(id, GREY);
1197
+ const node = graph.nodes[id];
1198
+ if (node) {
1199
+ for (const t of node.transitions) {
1200
+ visit(t.nextStateId);
1201
+ if (hasCycles) {
1202
+ return;
1203
+ }
1204
+ }
1205
+ }
1206
+ color.set(id, BLACK);
1207
+ };
1208
+ for (const node of nodes) {
1209
+ if (hasCycles) {
1210
+ break;
1211
+ }
1212
+ visit(node.id);
1213
+ }
1214
+ return {
1215
+ stateCount: nodes.length,
1216
+ transitionCount,
1217
+ compositionEdgeCount,
1218
+ maxCompositionDepth,
1219
+ selfLoopCount,
1220
+ hasCycles,
1221
+ tapeCount: graph.alphabets.length,
1222
+ alphabetCardinalities: graph.alphabets.map((a) => a.length),
1223
+ };
1224
+ }
1225
+ // Convenience: build the graph and summarize in one step.
1226
+ function summarize(state, tapeBlock) {
1227
+ return summarizeGraph(State.toGraph(state, tapeBlock));
1228
+ }
1229
+ // Behavioral equivalence checking (the testing-tool counterpart to introspection)
1230
+ // lives in ./equivalence — kept separate because it runs machines and compares
1231
+ // outputs rather than examining structure.
1232
+
1233
+ const defaultCompare = (a, b) => a === b;
1234
+ function equivalentOn(reference, candidate, cases, options = {}) {
1235
+ const compareOutputs = options.compareOutputs ?? defaultCompare;
1236
+ const compareSnapshots = options.compareSnapshots === undefined
1237
+ ? defaultCompare
1238
+ : options.compareSnapshots;
1239
+ const stepsLimit = options.stepsLimit ?? 1e5;
1240
+ const results = cases.map((c) => {
1241
+ const pair = typeof c === 'string' ? { reference: c, candidate: c } : c;
1242
+ const refRun = runOnce(reference, pair.reference, stepsLimit);
1243
+ const candRun = runOnce(candidate, pair.candidate, stepsLimit);
1244
+ const agree = compareOutputs(refRun.finalOutput, candRun.finalOutput);
1245
+ let firstDivergenceStep = null;
1246
+ if (!agree && compareSnapshots !== null) {
1247
+ const minLen = Math.min(refRun.snapshots.length, candRun.snapshots.length);
1248
+ for (let i = 0; i < minLen; i += 1) {
1249
+ if (!compareSnapshots(refRun.snapshots[i], candRun.snapshots[i])) {
1250
+ firstDivergenceStep = i;
1251
+ break;
1252
+ }
1253
+ }
1254
+ if (firstDivergenceStep === null && refRun.snapshots.length !== candRun.snapshots.length) {
1255
+ firstDivergenceStep = minLen;
1256
+ }
1257
+ }
1258
+ return {
1259
+ case: pair,
1260
+ agree,
1261
+ referenceOutput: refRun.finalOutput,
1262
+ candidateOutput: candRun.finalOutput,
1263
+ referenceSteps: refRun.stepCount,
1264
+ candidateSteps: candRun.stepCount,
1265
+ firstDivergenceStep,
1266
+ };
1267
+ });
1268
+ return {
1269
+ results,
1270
+ allAgree: results.every((r) => r.agree),
1271
+ };
1272
+ }
1273
+ // Single-machine runner: snapshots the tape after each step and returns the
1274
+ // final output, the snapshot list, and the step count.
1275
+ function runOnce(runnable, input, stepsLimit) {
1276
+ const tapeBlock = runnable.getTapeBlock();
1277
+ const tape = new Tape({
1278
+ alphabet: tapeBlock.tapes[0].alphabet,
1279
+ symbols: input.split(''),
1280
+ });
1281
+ tapeBlock.replaceTape(tape);
1282
+ const machine = new TuringMachine({ tapeBlock });
1283
+ const snapshots = [];
1284
+ let stepCount = 0;
1285
+ // Iterate the generator manually (the yielded MachineState isn't needed —
1286
+ // we only care about the side effects on the tape). At each yield the tape
1287
+ // reflects the state BEFORE the current step's command; after the loop
1288
+ // exits the tape has had every command applied.
1289
+ const generator = machine.runStepByStep({ initialState: runnable.state, stepsLimit });
1290
+ let result = generator.next();
1291
+ while (!result.done) {
1292
+ snapshots.push(tape.symbols.join(''));
1293
+ stepCount += 1;
1294
+ result = generator.next();
1295
+ }
1296
+ snapshots.push(tape.symbols.join(''));
1297
+ return {
1298
+ finalOutput: tape.symbols.join('').trim(),
1299
+ snapshots,
1300
+ stepCount,
1301
+ };
1302
+ }
1303
+
707
1304
  exports.Alphabet = Alphabet;
708
1305
  exports.Command = Command;
709
1306
  exports.Reference = Reference;
@@ -712,7 +1309,12 @@ exports.Tape = Tape;
712
1309
  exports.TapeBlock = TapeBlock;
713
1310
  exports.TapeCommand = TapeCommand;
714
1311
  exports.TuringMachine = TuringMachine;
1312
+ exports.equivalentOn = equivalentOn;
1313
+ exports.fromMermaid = fromMermaid;
715
1314
  exports.haltState = haltState;
716
1315
  exports.ifOtherSymbol = ifOtherSymbol;
717
1316
  exports.movements = movements;
1317
+ exports.summarize = summarize;
1318
+ exports.summarizeGraph = summarizeGraph;
718
1319
  exports.symbolCommands = symbolCommands;
1320
+ exports.toMermaid = toMermaid;