@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.mjs CHANGED
@@ -173,121 +173,13 @@ class Reference {
173
173
  }
174
174
  _Reference_referenceBinding = new WeakMap();
175
175
 
176
- var __classPrivateFieldGet$4 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
177
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
178
- 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");
179
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
180
- };
181
176
  var __classPrivateFieldSet$4 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
182
177
  if (kind === "m") throw new TypeError("Private method is not writable");
183
178
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
184
179
  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");
185
180
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
186
181
  };
187
- var _State_id, _State_name, _State_overrodeHaltState, _State_symbolToDataMap;
188
- const ifOtherSymbol = Symbol('other symbol');
189
- class State {
190
- constructor(stateDefinition = null, name) {
191
- _State_id.set(this, id(this));
192
- _State_name.set(this, void 0);
193
- _State_overrodeHaltState.set(this, null);
194
- _State_symbolToDataMap.set(this, new Map());
195
- if (stateDefinition) {
196
- const keys = Object.getOwnPropertyNames(stateDefinition);
197
- if (keys.length) {
198
- throw new Error(`invalid state definition while constructing state #${__classPrivateFieldGet$4(this, _State_id, "f")}`);
199
- }
200
- const symbols = Object.getOwnPropertySymbols(stateDefinition);
201
- if (symbols.length === 0) {
202
- throw new Error(`invalid state definition while constructing state #${__classPrivateFieldGet$4(this, _State_id, "f")}`);
203
- }
204
- symbols.forEach((symbol) => {
205
- const { nextState } = stateDefinition[symbol];
206
- const nextStateLocal = nextState !== null && nextState !== void 0 ? nextState : this;
207
- if (!(nextStateLocal instanceof State) && !(nextStateLocal instanceof Reference)) {
208
- throw new Error('invalid nextState');
209
- }
210
- let { command } = stateDefinition[symbol];
211
- if (command == null) {
212
- command = new Command([
213
- new TapeCommand({}),
214
- ]);
215
- }
216
- if (!(command instanceof Command) && !Array.isArray(command)) {
217
- command = [command];
218
- }
219
- let commandLocal = command;
220
- if (Array.isArray(command)) {
221
- try {
222
- commandLocal = new Command(command);
223
- }
224
- catch (error) {
225
- }
226
- }
227
- if (!(commandLocal instanceof Command)) {
228
- throw new Error('invalid command');
229
- }
230
- __classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").set(symbol, {
231
- command: commandLocal,
232
- nextState: nextStateLocal,
233
- });
234
- });
235
- }
236
- __classPrivateFieldSet$4(this, _State_name, name !== null && name !== void 0 ? name : `id:${__classPrivateFieldGet$4(this, _State_id, "f")}`, "f");
237
- }
238
- get id() {
239
- return __classPrivateFieldGet$4(this, _State_id, "f");
240
- }
241
- get name() {
242
- return __classPrivateFieldGet$4(this, _State_name, "f");
243
- }
244
- get isHalt() {
245
- return __classPrivateFieldGet$4(this, _State_id, "f") === 0;
246
- }
247
- get overrodeHaltState() {
248
- return __classPrivateFieldGet$4(this, _State_overrodeHaltState, "f");
249
- }
250
- get ref() {
251
- return this;
252
- }
253
- getSymbol(tapeBlock) {
254
- const symbol = [...__classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").keys()].find((currentSymbol) => tapeBlock.isMatched({
255
- symbol: currentSymbol,
256
- }));
257
- if (symbol) {
258
- return symbol;
259
- }
260
- return ifOtherSymbol;
261
- }
262
- getCommand(symbol) {
263
- if (__classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").has(symbol)) {
264
- return __classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").get(symbol).command;
265
- }
266
- throw new Error(`No command for symbol at state named ${__classPrivateFieldGet$4(this, _State_name, "f")}`);
267
- }
268
- getNextState(symbol) {
269
- if (__classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").has(symbol)) {
270
- return __classPrivateFieldGet$4(this, _State_symbolToDataMap, "f").get(symbol).nextState;
271
- }
272
- throw new Error(`No nextState for symbol at state named ${__classPrivateFieldGet$4(this, _State_id, "f")}`);
273
- }
274
- withOverrodeHaltState(overrodeHaltState) {
275
- const state = new State(null, `${this.name}>${overrodeHaltState.name}`);
276
- __classPrivateFieldSet$4(state, _State_symbolToDataMap, __classPrivateFieldGet$4(this, _State_symbolToDataMap, "f"), "f");
277
- __classPrivateFieldSet$4(state, _State_overrodeHaltState, overrodeHaltState, "f");
278
- return state;
279
- }
280
- }
281
- _State_id = new WeakMap(), _State_name = new WeakMap(), _State_overrodeHaltState = new WeakMap(), _State_symbolToDataMap = new WeakMap();
282
- const haltState = new State(null);
283
-
284
- var __classPrivateFieldSet$3 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
285
- if (kind === "m") throw new TypeError("Private method is not writable");
286
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
287
- 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");
288
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
289
- };
290
- var __classPrivateFieldGet$3 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
182
+ var __classPrivateFieldGet$4 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
291
183
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
292
184
  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");
293
185
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
@@ -303,46 +195,46 @@ class Tape {
303
195
  if (!isSymbolsValid) {
304
196
  throw new Error('symbolList contains invalid symbol');
305
197
  }
306
- __classPrivateFieldSet$3(this, _Tape_alphabet, new Alphabet(alphabet), "f");
307
- __classPrivateFieldSet$3(this, _Tape_position, position, "f");
308
- __classPrivateFieldSet$3(this, _Tape_viewportWidth, viewportWidth, "f");
198
+ __classPrivateFieldSet$4(this, _Tape_alphabet, new Alphabet(alphabet), "f");
199
+ __classPrivateFieldSet$4(this, _Tape_position, position, "f");
200
+ __classPrivateFieldSet$4(this, _Tape_viewportWidth, viewportWidth, "f");
309
201
  const symbolsCopy = Array.from(symbols);
310
202
  if (symbolsCopy.length === 0) {
311
- symbolsCopy.push(__classPrivateFieldGet$3(this, _Tape_alphabet, "f").blankSymbol);
203
+ symbolsCopy.push(__classPrivateFieldGet$4(this, _Tape_alphabet, "f").blankSymbol);
312
204
  }
313
- __classPrivateFieldSet$3(this, _Tape_symbols, symbolsCopy.map((symbol) => __classPrivateFieldGet$3(this, _Tape_alphabet, "f").index(symbol)), "f");
205
+ __classPrivateFieldSet$4(this, _Tape_symbols, symbolsCopy.map((symbol) => __classPrivateFieldGet$4(this, _Tape_alphabet, "f").index(symbol)), "f");
314
206
  }
315
207
  get alphabet() {
316
- return __classPrivateFieldGet$3(this, _Tape_alphabet, "f");
208
+ return __classPrivateFieldGet$4(this, _Tape_alphabet, "f");
317
209
  }
318
210
  get extraCellsCount() {
319
- return (__classPrivateFieldGet$3(this, _Tape_viewportWidth, "f") - 1) / 2;
211
+ return (__classPrivateFieldGet$4(this, _Tape_viewportWidth, "f") - 1) / 2;
320
212
  }
321
213
  get position() {
322
- return __classPrivateFieldGet$3(this, _Tape_position, "f");
214
+ return __classPrivateFieldGet$4(this, _Tape_position, "f");
323
215
  }
324
216
  get symbol() {
325
- return __classPrivateFieldGet$3(this, _Tape_alphabet, "f").get(__classPrivateFieldGet$3(this, _Tape_symbols, "f")[__classPrivateFieldGet$3(this, _Tape_position, "f")]);
217
+ return __classPrivateFieldGet$4(this, _Tape_alphabet, "f").get(__classPrivateFieldGet$4(this, _Tape_symbols, "f")[__classPrivateFieldGet$4(this, _Tape_position, "f")]);
326
218
  }
327
219
  set symbol(symbol) {
328
- if (!__classPrivateFieldGet$3(this, _Tape_alphabet, "f").has(symbol)) {
220
+ if (!__classPrivateFieldGet$4(this, _Tape_alphabet, "f").has(symbol)) {
329
221
  throw new Error('Invalid symbol');
330
222
  }
331
- __classPrivateFieldGet$3(this, _Tape_symbols, "f")[__classPrivateFieldGet$3(this, _Tape_position, "f")] = __classPrivateFieldGet$3(this, _Tape_alphabet, "f").index(symbol);
223
+ __classPrivateFieldGet$4(this, _Tape_symbols, "f")[__classPrivateFieldGet$4(this, _Tape_position, "f")] = __classPrivateFieldGet$4(this, _Tape_alphabet, "f").index(symbol);
332
224
  }
333
225
  get symbols() {
334
- return __classPrivateFieldGet$3(this, _Tape_symbols, "f")
335
- .map((index) => __classPrivateFieldGet$3(this, _Tape_alphabet, "f").get(index));
226
+ return __classPrivateFieldGet$4(this, _Tape_symbols, "f")
227
+ .map((index) => __classPrivateFieldGet$4(this, _Tape_alphabet, "f").get(index));
336
228
  }
337
229
  get viewport() {
338
- const startIx = __classPrivateFieldGet$3(this, _Tape_position, "f") - this.extraCellsCount;
339
- const endIx = __classPrivateFieldGet$3(this, _Tape_position, "f") + this.extraCellsCount + 1;
340
- return __classPrivateFieldGet$3(this, _Tape_symbols, "f")
230
+ const startIx = __classPrivateFieldGet$4(this, _Tape_position, "f") - this.extraCellsCount;
231
+ const endIx = __classPrivateFieldGet$4(this, _Tape_position, "f") + this.extraCellsCount + 1;
232
+ return __classPrivateFieldGet$4(this, _Tape_symbols, "f")
341
233
  .slice(startIx, endIx)
342
- .map((index) => __classPrivateFieldGet$3(this, _Tape_alphabet, "f").get(index));
234
+ .map((index) => __classPrivateFieldGet$4(this, _Tape_alphabet, "f").get(index));
343
235
  }
344
236
  get viewportWidth() {
345
- return __classPrivateFieldGet$3(this, _Tape_viewportWidth, "f");
237
+ return __classPrivateFieldGet$4(this, _Tape_viewportWidth, "f");
346
238
  }
347
239
  set viewportWidth(width) {
348
240
  let finalWidth = width;
@@ -352,35 +244,35 @@ class Tape {
352
244
  if (finalWidth % 2 === 0) {
353
245
  finalWidth += 1;
354
246
  }
355
- __classPrivateFieldSet$3(this, _Tape_viewportWidth, finalWidth, "f");
247
+ __classPrivateFieldSet$4(this, _Tape_viewportWidth, finalWidth, "f");
356
248
  this.normalise();
357
249
  }
358
250
  left() {
359
- __classPrivateFieldSet$3(this, _Tape_position, __classPrivateFieldGet$3(this, _Tape_position, "f") - 1, "f");
251
+ __classPrivateFieldSet$4(this, _Tape_position, __classPrivateFieldGet$4(this, _Tape_position, "f") - 1, "f");
360
252
  this.normalise();
361
253
  }
362
254
  normalise() {
363
- while (__classPrivateFieldGet$3(this, _Tape_position, "f") - this.extraCellsCount < 0) {
364
- __classPrivateFieldGet$3(this, _Tape_symbols, "f").unshift(0);
365
- __classPrivateFieldSet$3(this, _Tape_position, __classPrivateFieldGet$3(this, _Tape_position, "f") + 1, "f");
255
+ while (__classPrivateFieldGet$4(this, _Tape_position, "f") - this.extraCellsCount < 0) {
256
+ __classPrivateFieldGet$4(this, _Tape_symbols, "f").unshift(0);
257
+ __classPrivateFieldSet$4(this, _Tape_position, __classPrivateFieldGet$4(this, _Tape_position, "f") + 1, "f");
366
258
  }
367
- while (__classPrivateFieldGet$3(this, _Tape_position, "f") + this.extraCellsCount >= __classPrivateFieldGet$3(this, _Tape_symbols, "f").length) {
368
- __classPrivateFieldGet$3(this, _Tape_symbols, "f").push(0);
259
+ while (__classPrivateFieldGet$4(this, _Tape_position, "f") + this.extraCellsCount >= __classPrivateFieldGet$4(this, _Tape_symbols, "f").length) {
260
+ __classPrivateFieldGet$4(this, _Tape_symbols, "f").push(0);
369
261
  }
370
262
  }
371
263
  right() {
372
- __classPrivateFieldSet$3(this, _Tape_position, __classPrivateFieldGet$3(this, _Tape_position, "f") + 1, "f");
264
+ __classPrivateFieldSet$4(this, _Tape_position, __classPrivateFieldGet$4(this, _Tape_position, "f") + 1, "f");
373
265
  this.normalise();
374
266
  }
375
267
  }
376
268
  _Tape_alphabet = new WeakMap(), _Tape_symbols = new WeakMap(), _Tape_position = new WeakMap(), _Tape_viewportWidth = new WeakMap();
377
269
 
378
- var __classPrivateFieldGet$2 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
270
+ var __classPrivateFieldGet$3 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
379
271
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
380
272
  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");
381
273
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
382
274
  };
383
- var __classPrivateFieldSet$2 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
275
+ var __classPrivateFieldSet$3 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
384
276
  if (kind === "m") throw new TypeError("Private method is not writable");
385
277
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
386
278
  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");
@@ -392,30 +284,30 @@ class Lock {
392
284
  _Lock_lockSymbol.set(this, null);
393
285
  }
394
286
  lock(symbol) {
395
- if (__classPrivateFieldGet$2(this, _Lock_lockSymbol, "f") === null) {
396
- __classPrivateFieldSet$2(this, _Lock_lockSymbol, symbol, "f");
287
+ if (__classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") === null) {
288
+ __classPrivateFieldSet$3(this, _Lock_lockSymbol, symbol, "f");
397
289
  }
398
290
  }
399
291
  unlock(symbol) {
400
- if (__classPrivateFieldGet$2(this, _Lock_lockSymbol, "f") === symbol) {
401
- __classPrivateFieldSet$2(this, _Lock_lockSymbol, null, "f");
292
+ if (__classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") === symbol) {
293
+ __classPrivateFieldSet$3(this, _Lock_lockSymbol, null, "f");
402
294
  }
403
295
  }
404
296
  check(symbol) {
405
- if (__classPrivateFieldGet$2(this, _Lock_lockSymbol, "f") && __classPrivateFieldGet$2(this, _Lock_lockSymbol, "f") !== symbol) {
297
+ if (__classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") && __classPrivateFieldGet$3(this, _Lock_lockSymbol, "f") !== symbol) {
406
298
  throw new Error('Lock check failed');
407
299
  }
408
300
  }
409
301
  }
410
302
  _Lock_lockSymbol = new WeakMap();
411
303
 
412
- var __classPrivateFieldSet$1 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
304
+ var __classPrivateFieldSet$2 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
413
305
  if (kind === "m") throw new TypeError("Private method is not writable");
414
306
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
415
307
  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");
416
308
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
417
309
  };
418
- var __classPrivateFieldGet$1 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
310
+ var __classPrivateFieldGet$2 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
419
311
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
420
312
  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");
421
313
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
@@ -429,7 +321,7 @@ class TapeBlock {
429
321
  _TapeBlock_lock.set(this, new Lock());
430
322
  _TapeBlock_tapes.set(this, void 0);
431
323
  _TapeBlock_buildPatternList.set(this, (symbolList) => symbolList.reduce((result, symbol, ix) => {
432
- const row = Math.floor(ix / __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").length);
324
+ const row = Math.floor(ix / __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length);
433
325
  if (!Array.isArray(result[row])) {
434
326
  result[row] = [];
435
327
  }
@@ -445,7 +337,7 @@ class TapeBlock {
445
337
  if (patternList.some((pattern) => pattern.every((symbol) => symbol === ifOtherSymbol))) {
446
338
  return ifOtherSymbol;
447
339
  }
448
- const [storedPatternListSymbol] = [...__classPrivateFieldGet$1(this, _TapeBlock_symbolToPatternListMap, "f").entries()]
340
+ const [storedPatternListSymbol] = [...__classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").entries()]
449
341
  .find(([, storedPatternList]) => {
450
342
  if (storedPatternList.length !== patternList.length) {
451
343
  return false;
@@ -459,8 +351,8 @@ class TapeBlock {
459
351
  symbol = storedPatternListSymbol;
460
352
  }
461
353
  else {
462
- symbol = Symbol(__classPrivateFieldGet$1(_a, _a, "f", _TapeBlock_generateSymbolHint).call(_a, patternList));
463
- __classPrivateFieldGet$1(this, _TapeBlock_symbolToPatternListMap, "f").set(symbol, patternList);
354
+ symbol = Symbol(__classPrivateFieldGet$2(_a, _a, "f", _TapeBlock_generateSymbolHint).call(_a, patternList));
355
+ __classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").set(symbol, patternList);
464
356
  }
465
357
  return symbol;
466
358
  });
@@ -475,60 +367,60 @@ class TapeBlock {
475
367
  else if (Array.isArray(symbols)) {
476
368
  localSymbols = [...symbols];
477
369
  }
478
- if (localSymbols.length === 0 || localSymbols.length % __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").length > 0) {
370
+ if (localSymbols.length === 0 || localSymbols.length % __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length > 0) {
479
371
  throw new Error('invalid symbol parameter');
480
372
  }
481
373
  const invalidSymbolIndex = localSymbols.findIndex((symbol, ix) => (symbol !== ifOtherSymbol
482
- && !__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f")[ix % __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").length].alphabet.has(symbol)));
374
+ && !__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")[ix % __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length].alphabet.has(symbol)));
483
375
  if (invalidSymbolIndex >= 0) {
484
376
  throw new Error('invalid symbol parameter');
485
377
  }
486
378
  if (localSymbols.every((symbol) => symbol === ifOtherSymbol)) {
487
379
  return ifOtherSymbol;
488
380
  }
489
- return __classPrivateFieldGet$1(this, _TapeBlock_getSymbolForPatternList, "f").call(this, __classPrivateFieldGet$1(this, _TapeBlock_buildPatternList, "f").call(this, localSymbols));
381
+ return __classPrivateFieldGet$2(this, _TapeBlock_getSymbolForPatternList, "f").call(this, __classPrivateFieldGet$2(this, _TapeBlock_buildPatternList, "f").call(this, localSymbols));
490
382
  });
491
- __classPrivateFieldSet$1(this, _TapeBlock_tapes, [], "f");
383
+ __classPrivateFieldSet$2(this, _TapeBlock_tapes, [], "f");
492
384
  if ('alphabets' in argument) {
493
385
  const { alphabets } = argument;
494
386
  if (alphabets.length === 0) {
495
387
  throw new Error('empty alphabet list');
496
388
  }
497
- __classPrivateFieldSet$1(this, _TapeBlock_tapes, alphabets.map((alphabet) => new Tape({
389
+ __classPrivateFieldSet$2(this, _TapeBlock_tapes, alphabets.map((alphabet) => new Tape({
498
390
  alphabet,
499
391
  })), "f");
500
392
  }
501
393
  else if ('tapes' in argument) {
502
- __classPrivateFieldSet$1(this, _TapeBlock_tapes, argument.tapes, "f");
394
+ __classPrivateFieldSet$2(this, _TapeBlock_tapes, argument.tapes, "f");
503
395
  }
504
- if (__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").length === 0) {
396
+ if (__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length === 0) {
505
397
  throw new Error('empty tape list');
506
398
  }
507
399
  }
508
400
  get alphabets() {
509
- return [...__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").map((tape) => tape.alphabet)];
401
+ return [...__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").map((tape) => tape.alphabet)];
510
402
  }
511
403
  get currentSymbols() {
512
- return __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").map((tape) => tape.symbol);
404
+ return __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").map((tape) => tape.symbol);
513
405
  }
514
406
  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)]() {
515
- return __classPrivateFieldGet$1(this, _TapeBlock_lock, "f");
407
+ return __classPrivateFieldGet$2(this, _TapeBlock_lock, "f");
516
408
  }
517
409
  get symbol() {
518
- return __classPrivateFieldGet$1(this, _TapeBlock_symbol, "f").bind(this);
410
+ return __classPrivateFieldGet$2(this, _TapeBlock_symbol, "f").bind(this);
519
411
  }
520
412
  get tapes() {
521
- return [...__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f")];
413
+ return [...__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")];
522
414
  }
523
415
  set [symbolToPatternListMapSymbol](symbolToPatternListMap) {
524
- __classPrivateFieldSet$1(this, _TapeBlock_symbolToPatternListMap, new Map(symbolToPatternListMap), "f");
416
+ __classPrivateFieldSet$2(this, _TapeBlock_symbolToPatternListMap, new Map(symbolToPatternListMap), "f");
525
417
  }
526
418
  applyCommand(command, executionSymbol = null) {
527
- __classPrivateFieldGet$1(this, _TapeBlock_lock, "f").check(executionSymbol);
528
- if (__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").length !== command.tapesCommands.length) {
419
+ __classPrivateFieldGet$2(this, _TapeBlock_lock, "f").check(executionSymbol);
420
+ if (__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").length !== command.tapesCommands.length) {
529
421
  throw new Error('invalid command');
530
422
  }
531
- __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f").forEach((tape, ix) => {
423
+ __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f").forEach((tape, ix) => {
532
424
  const { movement, symbol } = command.tapesCommands[ix];
533
425
  if (typeof symbol === 'string') {
534
426
  tape.symbol = symbol;
@@ -564,28 +456,27 @@ class TapeBlock {
564
456
  else {
565
457
  tapeBlock = _a.fromAlphabets(this.alphabets);
566
458
  }
567
- tapeBlock[symbolToPatternListMapSymbol] = __classPrivateFieldGet$1(this, _TapeBlock_symbolToPatternListMap, "f");
459
+ tapeBlock[symbolToPatternListMapSymbol] = __classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f");
568
460
  return tapeBlock;
569
461
  }
570
462
  isMatched({ currentSymbols = this.currentSymbols, symbol }) {
571
- var _b;
572
463
  if (symbol === ifOtherSymbol) {
573
464
  return true;
574
465
  }
575
- if (!__classPrivateFieldGet$1(this, _TapeBlock_symbolToPatternListMap, "f").has(symbol)) {
466
+ if (!__classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").has(symbol)) {
576
467
  throw new Error('invalid symbol');
577
468
  }
578
- const patternList = __classPrivateFieldGet$1(this, _TapeBlock_symbolToPatternListMap, "f").get(symbol);
579
- return (_b = patternList === null || patternList === void 0 ? void 0 : patternList.some((pattern) => (pattern
469
+ const patternList = __classPrivateFieldGet$2(this, _TapeBlock_symbolToPatternListMap, "f").get(symbol);
470
+ return patternList?.some((pattern) => (pattern
580
471
  .every((everySymbol, ix) => (everySymbol === ifOtherSymbol
581
- || everySymbol === currentSymbols[ix]))))) !== null && _b !== void 0 ? _b : false;
472
+ || everySymbol === currentSymbols[ix])))) ?? false;
582
473
  }
583
474
  replaceTape(tape, tapeIx = 0) {
584
- if (__classPrivateFieldGet$1(this, _TapeBlock_tapes, "f")[tapeIx] == null) {
475
+ if (__classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")[tapeIx] == null) {
585
476
  throw new Error('invalid tapeIx');
586
477
  }
587
- if (tape.alphabet.symbols.join('') === __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f")[tapeIx].alphabet.symbols.join('')) {
588
- __classPrivateFieldGet$1(this, _TapeBlock_tapes, "f")[tapeIx] = tape;
478
+ if (tape.alphabet.symbols.join('') === __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")[tapeIx].alphabet.symbols.join('')) {
479
+ __classPrivateFieldGet$2(this, _TapeBlock_tapes, "f")[tapeIx] = tape;
589
480
  }
590
481
  else {
591
482
  throw new Error('invalid tape');
@@ -603,6 +494,408 @@ _TapeBlock_generateSymbolHint = { value: (patternList) => JSON.stringify(pattern
603
494
  .map((pattern) => pattern
604
495
  .map((symbol) => (symbol === ifOtherSymbol ? null : symbol)))) };
605
496
 
497
+ const movementDescriptionToLabel = {
498
+ 'move caret left command': 'L',
499
+ 'move caret right command': 'R',
500
+ 'do not move carer': 'S',
501
+ };
502
+ const symbolCommandDescriptionToLabel = {
503
+ 'keep symbol command': '·',
504
+ 'erase symbol command': '⌫',
505
+ };
506
+ // Reserved characters in the encoded pattern string:
507
+ // '*' per-cell ifOtherSymbol (matches any symbol on that tape)
508
+ // '-' the tape's blank symbol
509
+ // ',' separates per-tape cells inside one pattern
510
+ // '|' separates alternative patterns
511
+ // '\\' escape prefix — to represent any of '*', '-', ',', '|', or '\\' as a
512
+ // *literal* alphabet symbol, prefix it with '\\' (e.g. '\\*' for literal '*').
513
+ function escapeAlphabetSymbol(s) {
514
+ return s
515
+ .replace(/\\/g, '\\\\')
516
+ .replace(/\*/g, '\\*')
517
+ .replace(/-/g, '\\-')
518
+ .replace(/,/g, '\\,')
519
+ .replace(/\|/g, '\\|');
520
+ }
521
+ function decodePatternDescription(description, alphabets) {
522
+ if (!description) {
523
+ return '?';
524
+ }
525
+ if (description === 'other symbol') {
526
+ return '*';
527
+ }
528
+ try {
529
+ const patternList = JSON.parse(description);
530
+ return patternList
531
+ .map((pattern) => pattern
532
+ .map((s, tapeIx) => {
533
+ if (s === null) {
534
+ return '*';
535
+ }
536
+ if (s === alphabets[tapeIx]?.[0]) {
537
+ return '-';
538
+ }
539
+ return escapeAlphabetSymbol(s);
540
+ })
541
+ .join(','))
542
+ .join('|');
543
+ }
544
+ catch {
545
+ return description;
546
+ }
547
+ }
548
+ function decodeMovement(description) {
549
+ if (!description) {
550
+ return '?';
551
+ }
552
+ return movementDescriptionToLabel[description] ?? description;
553
+ }
554
+ function splitUnescaped(s, sep) {
555
+ const parts = [];
556
+ let current = '';
557
+ let i = 0;
558
+ while (i < s.length) {
559
+ if (s[i] === '\\' && i + 1 < s.length) {
560
+ current += s[i + 1];
561
+ i += 2;
562
+ }
563
+ else if (s[i] === sep) {
564
+ parts.push(current);
565
+ current = '';
566
+ i += 1;
567
+ }
568
+ else {
569
+ current += s[i];
570
+ i += 1;
571
+ }
572
+ }
573
+ parts.push(current);
574
+ return parts;
575
+ }
576
+ function parsePatternString(s, alphabets) {
577
+ if (s === '*') {
578
+ return null;
579
+ }
580
+ const alternatives = splitUnescaped(s, '|');
581
+ return alternatives.map((alt) => {
582
+ const cells = splitUnescaped(alt, ',');
583
+ return cells.map((cell, tapeIx) => {
584
+ if (cell === '*') {
585
+ return null;
586
+ }
587
+ if (cell === '-') {
588
+ return alphabets[tapeIx]?.[0] ?? cell;
589
+ }
590
+ return cell;
591
+ });
592
+ });
593
+ }
594
+ const movementLabelToSymbol = {
595
+ L: movements.left,
596
+ R: movements.right,
597
+ S: movements.stay,
598
+ };
599
+ function parseMovementLabel(label) {
600
+ const m = movementLabelToSymbol[label];
601
+ if (!m) {
602
+ throw new Error(`unknown movement label: ${label}`);
603
+ }
604
+ return m;
605
+ }
606
+ function parseWriteSymbolLabel(label) {
607
+ if (label === '·') {
608
+ return symbolCommands.keep;
609
+ }
610
+ if (label === '⌫') {
611
+ return symbolCommands.erase;
612
+ }
613
+ return label;
614
+ }
615
+ function decodeWriteSymbol(symbol) {
616
+ if (typeof symbol === 'symbol') {
617
+ const description = symbol.description ?? '?';
618
+ return symbolCommandDescriptionToLabel[description] ?? description;
619
+ }
620
+ return symbol;
621
+ }
622
+ // Format converters (toMermaid / fromMermaid) live in ./graphFormats.
623
+
624
+ var __classPrivateFieldGet$1 = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
625
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
626
+ 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");
627
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
628
+ };
629
+ var __classPrivateFieldSet$1 = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
630
+ if (kind === "m") throw new TypeError("Private method is not writable");
631
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
632
+ 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");
633
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
634
+ };
635
+ var _State_id, _State_name, _State_overrodeHaltState, _State_symbolToDataMap;
636
+ const ifOtherSymbol = Symbol('other symbol');
637
+ class State {
638
+ constructor(stateDefinition = null, name) {
639
+ _State_id.set(this, id(this));
640
+ _State_name.set(this, void 0);
641
+ _State_overrodeHaltState.set(this, null);
642
+ _State_symbolToDataMap.set(this, new Map());
643
+ if (stateDefinition) {
644
+ const keys = Object.getOwnPropertyNames(stateDefinition);
645
+ if (keys.length) {
646
+ throw new Error(`invalid state definition while constructing state #${__classPrivateFieldGet$1(this, _State_id, "f")}`);
647
+ }
648
+ const symbols = Object.getOwnPropertySymbols(stateDefinition);
649
+ if (symbols.length === 0) {
650
+ throw new Error(`invalid state definition while constructing state #${__classPrivateFieldGet$1(this, _State_id, "f")}`);
651
+ }
652
+ symbols.forEach((symbol) => {
653
+ const { nextState } = stateDefinition[symbol];
654
+ const nextStateLocal = nextState ?? this;
655
+ if (!(nextStateLocal instanceof State) && !(nextStateLocal instanceof Reference)) {
656
+ throw new Error('invalid nextState');
657
+ }
658
+ let { command } = stateDefinition[symbol];
659
+ if (command == null) {
660
+ command = new Command([
661
+ new TapeCommand({}),
662
+ ]);
663
+ }
664
+ if (!(command instanceof Command) && !Array.isArray(command)) {
665
+ command = [command];
666
+ }
667
+ let commandLocal = command;
668
+ if (Array.isArray(command)) {
669
+ try {
670
+ commandLocal = new Command(command);
671
+ }
672
+ catch (error) {
673
+ }
674
+ }
675
+ if (!(commandLocal instanceof Command)) {
676
+ throw new Error('invalid command');
677
+ }
678
+ __classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").set(symbol, {
679
+ command: commandLocal,
680
+ nextState: nextStateLocal,
681
+ });
682
+ });
683
+ }
684
+ __classPrivateFieldSet$1(this, _State_name, name ?? `id:${__classPrivateFieldGet$1(this, _State_id, "f")}`, "f");
685
+ }
686
+ get id() {
687
+ return __classPrivateFieldGet$1(this, _State_id, "f");
688
+ }
689
+ get name() {
690
+ return __classPrivateFieldGet$1(this, _State_name, "f");
691
+ }
692
+ get isHalt() {
693
+ return __classPrivateFieldGet$1(this, _State_id, "f") === 0;
694
+ }
695
+ get overrodeHaltState() {
696
+ return __classPrivateFieldGet$1(this, _State_overrodeHaltState, "f");
697
+ }
698
+ get ref() {
699
+ return this;
700
+ }
701
+ getSymbol(tapeBlock) {
702
+ const symbol = [...__classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").keys()].find((currentSymbol) => tapeBlock.isMatched({
703
+ symbol: currentSymbol,
704
+ }));
705
+ if (symbol) {
706
+ return symbol;
707
+ }
708
+ return ifOtherSymbol;
709
+ }
710
+ getCommand(symbol) {
711
+ if (__classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").has(symbol)) {
712
+ return __classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").get(symbol).command;
713
+ }
714
+ throw new Error(`No command for symbol at state named ${__classPrivateFieldGet$1(this, _State_name, "f")}`);
715
+ }
716
+ getNextState(symbol) {
717
+ if (__classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").has(symbol)) {
718
+ return __classPrivateFieldGet$1(this, _State_symbolToDataMap, "f").get(symbol).nextState;
719
+ }
720
+ throw new Error(`No nextState for symbol at state named ${__classPrivateFieldGet$1(this, _State_id, "f")}`);
721
+ }
722
+ withOverrodeHaltState(overrodeHaltState) {
723
+ const state = new State(null, `${this.name}>${overrodeHaltState.name}`);
724
+ __classPrivateFieldSet$1(state, _State_symbolToDataMap, __classPrivateFieldGet$1(this, _State_symbolToDataMap, "f"), "f");
725
+ __classPrivateFieldSet$1(state, _State_overrodeHaltState, overrodeHaltState, "f");
726
+ return state;
727
+ }
728
+ // Single-state introspection — no traversal, no tapeBlock required.
729
+ // Returns id, name, halt-status, override-halt target, and the list of
730
+ // transitions out of this state with decoded write/movement labels.
731
+ // Symbol patterns are returned as the raw description string from the
732
+ // interned JS Symbol (decode via decodePatternDescription if needed).
733
+ static inspect(state) {
734
+ const transitions = [];
735
+ for (const [sym, { command, nextState }] of __classPrivateFieldGet$1(state, _State_symbolToDataMap, "f")) {
736
+ let target = null;
737
+ try {
738
+ target = nextState instanceof State ? nextState : nextState.ref;
739
+ }
740
+ catch {
741
+ target = null; // unbound Reference
742
+ }
743
+ transitions.push({
744
+ rawPatternDescription: sym.description,
745
+ command: command.tapesCommands.map((tc) => ({
746
+ symbol: decodeWriteSymbol(tc.symbol),
747
+ movement: decodeMovement(tc.movement.description),
748
+ })),
749
+ nextState: target ? { id: target.id, name: target.name } : null,
750
+ });
751
+ }
752
+ return {
753
+ id: __classPrivateFieldGet$1(state, _State_id, "f"),
754
+ name: __classPrivateFieldGet$1(state, _State_name, "f"),
755
+ isHalt: state.isHalt,
756
+ overrodeHaltState: __classPrivateFieldGet$1(state, _State_overrodeHaltState, "f")
757
+ ? { id: __classPrivateFieldGet$1(state, _State_overrodeHaltState, "f").id, name: __classPrivateFieldGet$1(state, _State_overrodeHaltState, "f").name }
758
+ : null,
759
+ transitions,
760
+ };
761
+ }
762
+ static toGraph(initialState, tapeBlock) {
763
+ const nodes = {};
764
+ const queue = [initialState];
765
+ const alphabets = tapeBlock.alphabets.map((alphabet) => alphabet.symbols);
766
+ while (queue.length > 0) {
767
+ const current = queue.shift();
768
+ if (__classPrivateFieldGet$1(current, _State_id, "f") in nodes) {
769
+ continue;
770
+ }
771
+ const node = {
772
+ id: __classPrivateFieldGet$1(current, _State_id, "f"),
773
+ name: __classPrivateFieldGet$1(current, _State_name, "f"),
774
+ isHalt: current.isHalt,
775
+ transitions: [],
776
+ overrodeHaltStateId: __classPrivateFieldGet$1(current, _State_overrodeHaltState, "f")?.id ?? null,
777
+ };
778
+ nodes[__classPrivateFieldGet$1(current, _State_id, "f")] = node;
779
+ if (__classPrivateFieldGet$1(current, _State_overrodeHaltState, "f")) {
780
+ queue.push(__classPrivateFieldGet$1(current, _State_overrodeHaltState, "f"));
781
+ }
782
+ for (const [sym, { command, nextState }] of __classPrivateFieldGet$1(current, _State_symbolToDataMap, "f")) {
783
+ let target;
784
+ try {
785
+ target = nextState instanceof State ? nextState : nextState.ref;
786
+ }
787
+ catch {
788
+ continue;
789
+ }
790
+ node.transitions.push({
791
+ pattern: decodePatternDescription(sym.description, alphabets),
792
+ command: command.tapesCommands.map((tc) => ({
793
+ symbol: decodeWriteSymbol(tc.symbol),
794
+ movement: decodeMovement(tc.movement.description),
795
+ })),
796
+ nextStateId: target.id,
797
+ });
798
+ queue.push(target);
799
+ }
800
+ }
801
+ return { initialId: __classPrivateFieldGet$1(initialState, _State_id, "f"), alphabets, nodes };
802
+ }
803
+ // Inverse of toGraph: rebuilds a State graph (and a fresh TapeBlock with the
804
+ // graph's alphabets) from a serialized Graph. Round-trips with toGraph in
805
+ // the sense that running the rebuilt machine on the same input gives the
806
+ // same output, but the rebuilt State instances have *new* internal IDs.
807
+ static fromGraph(graph) {
808
+ const alphabetObjs = graph.alphabets.map((syms) => new Alphabet(syms));
809
+ const tapeBlock = TapeBlock.fromAlphabets(alphabetObjs);
810
+ const ids = Object.keys(graph.nodes).map(Number);
811
+ // Pass 1: pre-create a Reference for each non-halt node so transitions can
812
+ // forward-declare their targets.
813
+ const refs = {};
814
+ for (const nodeId of ids) {
815
+ if (!graph.nodes[nodeId].isHalt) {
816
+ refs[nodeId] = new Reference();
817
+ }
818
+ }
819
+ // Convert a parsed pattern back to the symbol key the State expects.
820
+ const patternToKey = (parsed) => {
821
+ if (parsed === null) {
822
+ return ifOtherSymbol;
823
+ }
824
+ const flat = [];
825
+ for (const row of parsed) {
826
+ for (const cell of row) {
827
+ flat.push(cell === null ? ifOtherSymbol : cell);
828
+ }
829
+ }
830
+ return tapeBlock.symbol(flat);
831
+ };
832
+ // Pass 2: build a "bare" State for each non-halt node (no override yet).
833
+ // nextState entries point at refs so cycles work; haltState is used directly.
834
+ const bareStates = {};
835
+ for (const nodeId of ids) {
836
+ const node = graph.nodes[nodeId];
837
+ if (node.isHalt) {
838
+ continue;
839
+ }
840
+ const stateDefinition = {};
841
+ for (const t of node.transitions) {
842
+ const key = patternToKey(parsePatternString(t.pattern, graph.alphabets));
843
+ const target = graph.nodes[t.nextStateId];
844
+ const nextState = target.isHalt ? haltState : refs[t.nextStateId];
845
+ stateDefinition[key] = {
846
+ command: t.command.map((c) => ({
847
+ symbol: parseWriteSymbolLabel(c.symbol),
848
+ movement: parseMovementLabel(c.movement),
849
+ })),
850
+ nextState,
851
+ };
852
+ }
853
+ bareStates[nodeId] = new State(stateDefinition, node.name);
854
+ }
855
+ // Pass 3: apply overrideHaltStates transitively.
856
+ const finalStates = {};
857
+ const inProgress = new Set();
858
+ const getFinal = (nodeId) => {
859
+ if (finalStates[nodeId]) {
860
+ return finalStates[nodeId];
861
+ }
862
+ const node = graph.nodes[nodeId];
863
+ if (node.isHalt) {
864
+ finalStates[nodeId] = haltState;
865
+ return haltState;
866
+ }
867
+ if (inProgress.has(nodeId)) {
868
+ throw new Error(`override-halt cycle at state #${nodeId}`);
869
+ }
870
+ inProgress.add(nodeId);
871
+ let state = bareStates[nodeId];
872
+ if (node.overrodeHaltStateId !== null) {
873
+ state = bareStates[nodeId].withOverrodeHaltState(getFinal(node.overrodeHaltStateId));
874
+ }
875
+ inProgress.delete(nodeId);
876
+ finalStates[nodeId] = state;
877
+ return state;
878
+ };
879
+ for (const nodeId of ids) {
880
+ getFinal(nodeId);
881
+ }
882
+ // Pass 4: bind each ref to the FINAL (possibly wrapped) state so transitions
883
+ // resolve to the version that has its override-halt set.
884
+ for (const nodeId of ids) {
885
+ if (!graph.nodes[nodeId].isHalt) {
886
+ refs[nodeId].bind(finalStates[nodeId]);
887
+ }
888
+ }
889
+ return {
890
+ start: finalStates[graph.initialId],
891
+ tapeBlock,
892
+ states: finalStates,
893
+ };
894
+ }
895
+ }
896
+ _State_id = new WeakMap(), _State_name = new WeakMap(), _State_overrodeHaltState = new WeakMap(), _State_symbolToDataMap = new WeakMap();
897
+ const haltState = new State(null);
898
+
606
899
  var __classPrivateFieldSet = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
607
900
  if (kind === "m") throw new TypeError("Private method is not writable");
608
901
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
@@ -702,4 +995,308 @@ class TuringMachine {
702
995
  }
703
996
  _TuringMachine_tapeBlock = new WeakMap(), _TuringMachine_stack = new WeakMap();
704
997
 
705
- export { Alphabet, Command, Reference, State, Tape, TapeBlock, TapeCommand, TuringMachine, haltState, ifOtherSymbol, movements, symbolCommands };
998
+ // Format converters between a Graph (the data model produced by State.toGraph
999
+ // and consumed by State.fromGraph) and external string representations.
1000
+ //
1001
+ // Currently only Mermaid flowchart syntax is supported. Future formats
1002
+ // (Graphviz, JSON-LD, custom DSL) belong here too.
1003
+ function toMermaid(graph) {
1004
+ const lines = [
1005
+ 'flowchart TD',
1006
+ `%% alphabets: ${JSON.stringify(graph.alphabets)}`,
1007
+ ];
1008
+ for (const node of Object.values(graph.nodes)) {
1009
+ const id = `s${node.id}`;
1010
+ if (node.isHalt) {
1011
+ lines.push(` ${id}(((halt)))`);
1012
+ }
1013
+ else if (node.id === graph.initialId) {
1014
+ lines.push(` ${id}(("${node.name}"))`);
1015
+ }
1016
+ else {
1017
+ lines.push(` ${id}["${node.name}"]`);
1018
+ }
1019
+ }
1020
+ for (const node of Object.values(graph.nodes)) {
1021
+ for (const t of node.transitions) {
1022
+ // Per-tape commands separated with ',' to mirror the pattern syntax.
1023
+ const cmd = t.command.map((c) => `${c.symbol}/${c.movement}`).join(',');
1024
+ const label = `${t.pattern} → ${cmd}`;
1025
+ lines.push(` s${node.id} -- "${label}" --> s${t.nextStateId}`);
1026
+ }
1027
+ if (node.overrodeHaltStateId !== null) {
1028
+ lines.push(` s${node.id} -. onHalt .-> s${node.overrodeHaltStateId}`);
1029
+ }
1030
+ }
1031
+ return lines.join('\n');
1032
+ }
1033
+ // Inverse of toMermaid: parses the Mermaid output produced by toMermaid back
1034
+ // into a Graph. The parser is strict to the dialect toMermaid emits — it
1035
+ // recognises the specific node/edge shapes and the leading
1036
+ // `%% alphabets: [...]` comment. Hand-edited Mermaid that uses different
1037
+ // arrow styles or shapes will not parse.
1038
+ //
1039
+ // Caveats:
1040
+ // - Write-symbol cells in commands are split on '/' (last occurrence) and
1041
+ // per-tape segments are split on ','. If your alphabet contains '/' or ','
1042
+ // as literal symbols, the parser cannot disambiguate. Stick to alphabets
1043
+ // without those characters when round-tripping through Mermaid.
1044
+ const haltNodeRegex = /^s(\d+)\(\(\(halt\)\)\)$/;
1045
+ const initialNodeRegex = /^s(\d+)\(\("([^"]*)"\)\)$/;
1046
+ const regularNodeRegex = /^s(\d+)\["([^"]*)"\]$/;
1047
+ const transitionRegex = /^s(\d+)\s+--\s+"(.*)"\s+-->\s+s(\d+)$/;
1048
+ const onHaltRegex = /^s(\d+)\s+-\.\s+onHalt\s+\.->\s+s(\d+)$/;
1049
+ // First capture char anchored as \S to avoid polynomial backtracking between
1050
+ // the preceding \s* and a permissive (.+); see CodeQL js/polynomial-redos.
1051
+ const alphabetsRegex = /^%%\s*alphabets:\s*(\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
+ // No `if (hasCycles) return` guard at function entry: the recursive call
1184
+ // pattern (outer for-loop checks before calling, inner loop checks after
1185
+ // each recursive call) ensures visit() is never invoked when hasCycles
1186
+ // is already true. Static analysis confirmed the guard was unreachable.
1187
+ if (color.get(id) === GREY) {
1188
+ hasCycles = true;
1189
+ return;
1190
+ }
1191
+ if (color.get(id) === BLACK) {
1192
+ return;
1193
+ }
1194
+ color.set(id, GREY);
1195
+ const node = graph.nodes[id];
1196
+ if (node) {
1197
+ for (const t of node.transitions) {
1198
+ visit(t.nextStateId);
1199
+ if (hasCycles) {
1200
+ return;
1201
+ }
1202
+ }
1203
+ }
1204
+ color.set(id, BLACK);
1205
+ };
1206
+ for (const node of nodes) {
1207
+ if (hasCycles) {
1208
+ break;
1209
+ }
1210
+ visit(node.id);
1211
+ }
1212
+ return {
1213
+ stateCount: nodes.length,
1214
+ transitionCount,
1215
+ compositionEdgeCount,
1216
+ maxCompositionDepth,
1217
+ selfLoopCount,
1218
+ hasCycles,
1219
+ tapeCount: graph.alphabets.length,
1220
+ alphabetCardinalities: graph.alphabets.map((a) => a.length),
1221
+ };
1222
+ }
1223
+ // Convenience: build the graph and summarize in one step.
1224
+ function summarize(state, tapeBlock) {
1225
+ return summarizeGraph(State.toGraph(state, tapeBlock));
1226
+ }
1227
+ // Behavioral equivalence checking (the testing-tool counterpart to introspection)
1228
+ // lives in ./equivalence — kept separate because it runs machines and compares
1229
+ // outputs rather than examining structure.
1230
+
1231
+ const defaultCompare = (a, b) => a === b;
1232
+ function equivalentOn(reference, candidate, cases, options = {}) {
1233
+ const compareOutputs = options.compareOutputs ?? defaultCompare;
1234
+ const compareSnapshots = options.compareSnapshots === undefined
1235
+ ? defaultCompare
1236
+ : options.compareSnapshots;
1237
+ const stepsLimit = options.stepsLimit ?? 1e5;
1238
+ const results = cases.map((c) => {
1239
+ const pair = typeof c === 'string' ? { reference: c, candidate: c } : c;
1240
+ const refRun = runOnce(reference, pair.reference, stepsLimit);
1241
+ const candRun = runOnce(candidate, pair.candidate, stepsLimit);
1242
+ const agree = compareOutputs(refRun.finalOutput, candRun.finalOutput);
1243
+ let firstDivergenceStep = null;
1244
+ if (!agree && compareSnapshots !== null) {
1245
+ const minLen = Math.min(refRun.snapshots.length, candRun.snapshots.length);
1246
+ for (let i = 0; i < minLen; i += 1) {
1247
+ if (!compareSnapshots(refRun.snapshots[i], candRun.snapshots[i])) {
1248
+ firstDivergenceStep = i;
1249
+ break;
1250
+ }
1251
+ }
1252
+ if (firstDivergenceStep === null && refRun.snapshots.length !== candRun.snapshots.length) {
1253
+ firstDivergenceStep = minLen;
1254
+ }
1255
+ }
1256
+ return {
1257
+ case: pair,
1258
+ agree,
1259
+ referenceOutput: refRun.finalOutput,
1260
+ candidateOutput: candRun.finalOutput,
1261
+ referenceSteps: refRun.stepCount,
1262
+ candidateSteps: candRun.stepCount,
1263
+ firstDivergenceStep,
1264
+ };
1265
+ });
1266
+ return {
1267
+ results,
1268
+ allAgree: results.every((r) => r.agree),
1269
+ };
1270
+ }
1271
+ // Single-machine runner: snapshots the tape after each step and returns the
1272
+ // final output, the snapshot list, and the step count.
1273
+ function runOnce(runnable, input, stepsLimit) {
1274
+ const tapeBlock = runnable.getTapeBlock();
1275
+ const tape = new Tape({
1276
+ alphabet: tapeBlock.tapes[0].alphabet,
1277
+ symbols: input.split(''),
1278
+ });
1279
+ tapeBlock.replaceTape(tape);
1280
+ const machine = new TuringMachine({ tapeBlock });
1281
+ const snapshots = [];
1282
+ let stepCount = 0;
1283
+ // Iterate the generator manually (the yielded MachineState isn't needed —
1284
+ // we only care about the side effects on the tape). At each yield the tape
1285
+ // reflects the state BEFORE the current step's command; after the loop
1286
+ // exits the tape has had every command applied.
1287
+ const generator = machine.runStepByStep({ initialState: runnable.state, stepsLimit });
1288
+ let result = generator.next();
1289
+ while (!result.done) {
1290
+ snapshots.push(tape.symbols.join(''));
1291
+ stepCount += 1;
1292
+ result = generator.next();
1293
+ }
1294
+ snapshots.push(tape.symbols.join(''));
1295
+ return {
1296
+ finalOutput: tape.symbols.join('').trim(),
1297
+ snapshots,
1298
+ stepCount,
1299
+ };
1300
+ }
1301
+
1302
+ export { Alphabet, Command, Reference, State, Tape, TapeBlock, TapeCommand, TuringMachine, equivalentOn, fromMermaid, haltState, ifOtherSymbol, movements, summarize, summarizeGraph, symbolCommands, toMermaid };