@turing-machine-js/machine 3.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/dist/index.cjs +17 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +17 -9
- package/package.json +2 -2
- package/dist/classes/Alphabet.js +0 -50
- package/dist/classes/Command.js +0 -38
- package/dist/classes/Lock.js +0 -34
- package/dist/classes/Reference.js +0 -31
- package/dist/classes/State.js +0 -283
- package/dist/classes/Tape.js +0 -95
- package/dist/classes/TapeBlock.js +0 -197
- package/dist/classes/TapeCommand.js +0 -46
- package/dist/classes/TuringMachine.js +0 -102
- package/dist/index.js +0 -11
- package/dist/utilities/equivalence.js +0 -68
- package/dist/utilities/functions.js +0 -13
- package/dist/utilities/graph.js +0 -127
- package/dist/utilities/graphFormats.js +0 -141
- package/dist/utilities/introspection.js +0 -88
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,31 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [3.0.2] - 2026-05-04
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **`Tape` constructor now normalises and validates `viewportWidth`** ([#95](https://github.com/mellonis/turing-machine-js/issues/95)). The constructor stored `viewportWidth` raw — the setter padded `#symbols` via `normalise()` and validated `>= 1` / bumped even values to odd, but the constructor did neither. As a result, `new Tape({ alphabet, symbols: ['a','b','a','b'], viewportWidth: 23 }).viewport.length` was `4` instead of `23`, and `viewportWidth: 0` or `viewportWidth: 4` silently produced an invalid tape. The constructor now defers to the setter, so a single source of truth handles validation + normalisation.
|
|
12
|
+
|
|
13
|
+
### Changed (observable)
|
|
14
|
+
|
|
15
|
+
- A constructor call with an out-of-range `position` (e.g. `position: 5` with `symbols: ['x']`) now normalises at construction — the symbol array is padded with blanks so `position` lands inside the viewport. Previously the tape was left in a malformed state until the first head movement triggered `normalise()`. Strictly a fix, but observable if user code inspected `.symbols.length` immediately after construction.
|
|
16
|
+
|
|
17
|
+
## [3.0.1] - 2026-04-30
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **`graphFormats.ts` — polynomial-time regex (CodeQL `js/polynomial-redos`).** `alphabetsRegex` was `/^%%\s*alphabets:\s*(.+)$/`, where the `\s*` and `(.+)` both match whitespace, letting the engine try N+1 split points on inputs like `"%%alphabets:"` followed by many trailing spaces. Anchored the first captured char as `\S` to remove the ambiguity.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **`MachineState` re-export** from `index.ts`. The type was always `export type MachineState = ...` in `classes/TuringMachine.ts`, but the package barrel didn't surface it. Consumers (notably `@post-machine-js/machine`'s `PostMachine` overrides of `run` / `runStepByStep`) can now `import { type MachineState } from '@turing-machine-js/machine'` directly, dropping any local `Generator<infer T>` workaround.
|
|
26
|
+
|
|
27
|
+
### Changed (internal)
|
|
28
|
+
|
|
29
|
+
- Removed the unreachable `if (hasCycles) return` guard at the top of `summarizeGraph`'s `visit()` cycle-detection. The recursive call pattern (outer for-loop checks before calling, inner loop checks after each recursive call) ensures `visit()` is never invoked when `hasCycles` is already true. Static analysis confirmed the guard was dead code.
|
|
30
|
+
- Tightened test coverage on `State.ts` and the v3 utilities — overall coverage rose from 95.64% / 88.39% / 95.5% (statements/branches/lines) to 98.39% / 94.01% / 98.34%. New tests cover invalid-input paths in the `State` constructor (string-keyed definitions, non-`State`/`Reference` `nextState`, empty-array commands), the `getSymbol` fallback to `ifOtherSymbol`, the `toGraph` skip of unbound `Reference` transitions, the `fromGraph` cyclic-override-halt error, and the previously-unexercised branches in `splitUnescaped`, `parsePatternString`, `parseMovementLabel`, and `fromMermaid`'s ensureNode update / error paths.
|
|
31
|
+
|
|
7
32
|
## [3.0.0] - 2026-04-30
|
|
8
33
|
|
|
9
34
|
### Added
|
package/dist/index.cjs
CHANGED
|
@@ -199,12 +199,13 @@ class Tape {
|
|
|
199
199
|
}
|
|
200
200
|
__classPrivateFieldSet$4(this, _Tape_alphabet, new Alphabet(alphabet), "f");
|
|
201
201
|
__classPrivateFieldSet$4(this, _Tape_position, position, "f");
|
|
202
|
-
__classPrivateFieldSet$4(this, _Tape_viewportWidth,
|
|
202
|
+
__classPrivateFieldSet$4(this, _Tape_viewportWidth, 1, "f");
|
|
203
203
|
const symbolsCopy = Array.from(symbols);
|
|
204
204
|
if (symbolsCopy.length === 0) {
|
|
205
205
|
symbolsCopy.push(__classPrivateFieldGet$4(this, _Tape_alphabet, "f").blankSymbol);
|
|
206
206
|
}
|
|
207
207
|
__classPrivateFieldSet$4(this, _Tape_symbols, symbolsCopy.map((symbol) => __classPrivateFieldGet$4(this, _Tape_alphabet, "f").index(symbol)), "f");
|
|
208
|
+
this.viewportWidth = viewportWidth;
|
|
208
209
|
}
|
|
209
210
|
get alphabet() {
|
|
210
211
|
return __classPrivateFieldGet$4(this, _Tape_alphabet, "f");
|
|
@@ -1048,7 +1049,9 @@ const initialNodeRegex = /^s(\d+)\(\("([^"]*)"\)\)$/;
|
|
|
1048
1049
|
const regularNodeRegex = /^s(\d+)\["([^"]*)"\]$/;
|
|
1049
1050
|
const transitionRegex = /^s(\d+)\s+--\s+"(.*)"\s+-->\s+s(\d+)$/;
|
|
1050
1051
|
const onHaltRegex = /^s(\d+)\s+-\.\s+onHalt\s+\.->\s+s(\d+)$/;
|
|
1051
|
-
|
|
1052
|
+
// First capture char anchored as \S to avoid polynomial backtracking between
|
|
1053
|
+
// the preceding \s* and a permissive (.+); see CodeQL js/polynomial-redos.
|
|
1054
|
+
const alphabetsRegex = /^%%\s*alphabets:\s*(\S.*)$/;
|
|
1052
1055
|
function fromMermaid(text) {
|
|
1053
1056
|
const lines = text.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
1054
1057
|
let alphabets = [];
|
|
@@ -1180,9 +1183,10 @@ function summarizeGraph(graph) {
|
|
|
1180
1183
|
}
|
|
1181
1184
|
let hasCycles = false;
|
|
1182
1185
|
const visit = (id) => {
|
|
1183
|
-
if (hasCycles)
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
+
// No `if (hasCycles) return` guard at function entry: the recursive call
|
|
1187
|
+
// pattern (outer for-loop checks before calling, inner loop checks after
|
|
1188
|
+
// each recursive call) ensures visit() is never invoked when hasCycles
|
|
1189
|
+
// is already true. Static analysis confirmed the guard was unreachable.
|
|
1186
1190
|
if (color.get(id) === GREY) {
|
|
1187
1191
|
hasCycles = true;
|
|
1188
1192
|
return;
|
|
@@ -1279,12 +1283,16 @@ function runOnce(runnable, input, stepsLimit) {
|
|
|
1279
1283
|
const machine = new TuringMachine({ tapeBlock });
|
|
1280
1284
|
const snapshots = [];
|
|
1281
1285
|
let stepCount = 0;
|
|
1282
|
-
//
|
|
1283
|
-
//
|
|
1284
|
-
//
|
|
1285
|
-
|
|
1286
|
+
// Iterate the generator manually (the yielded MachineState isn't needed —
|
|
1287
|
+
// we only care about the side effects on the tape). At each yield the tape
|
|
1288
|
+
// reflects the state BEFORE the current step's command; after the loop
|
|
1289
|
+
// exits the tape has had every command applied.
|
|
1290
|
+
const generator = machine.runStepByStep({ initialState: runnable.state, stepsLimit });
|
|
1291
|
+
let result = generator.next();
|
|
1292
|
+
while (!result.done) {
|
|
1286
1293
|
snapshots.push(tape.symbols.join(''));
|
|
1287
1294
|
stepCount += 1;
|
|
1295
|
+
result = generator.next();
|
|
1288
1296
|
}
|
|
1289
1297
|
snapshots.push(tape.symbols.join(''));
|
|
1290
1298
|
return {
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { default as State, haltState, ifOtherSymbol } from './classes/State';
|
|
|
5
5
|
export { default as Tape } from './classes/Tape';
|
|
6
6
|
export { default as TapeBlock } from './classes/TapeBlock';
|
|
7
7
|
export { default as TapeCommand, movements, symbolCommands } from './classes/TapeCommand';
|
|
8
|
-
export { default as TuringMachine } from './classes/TuringMachine';
|
|
8
|
+
export { default as TuringMachine, type MachineState } from './classes/TuringMachine';
|
|
9
9
|
export { type Graph, type GraphNode, type GraphTransition, type GraphCommand } from './utilities/graph';
|
|
10
10
|
export { toMermaid, fromMermaid } from './utilities/graphFormats';
|
|
11
11
|
export { summarize, summarizeGraph, type GraphSummary } from './utilities/introspection';
|
package/dist/index.mjs
CHANGED
|
@@ -197,12 +197,13 @@ class Tape {
|
|
|
197
197
|
}
|
|
198
198
|
__classPrivateFieldSet$4(this, _Tape_alphabet, new Alphabet(alphabet), "f");
|
|
199
199
|
__classPrivateFieldSet$4(this, _Tape_position, position, "f");
|
|
200
|
-
__classPrivateFieldSet$4(this, _Tape_viewportWidth,
|
|
200
|
+
__classPrivateFieldSet$4(this, _Tape_viewportWidth, 1, "f");
|
|
201
201
|
const symbolsCopy = Array.from(symbols);
|
|
202
202
|
if (symbolsCopy.length === 0) {
|
|
203
203
|
symbolsCopy.push(__classPrivateFieldGet$4(this, _Tape_alphabet, "f").blankSymbol);
|
|
204
204
|
}
|
|
205
205
|
__classPrivateFieldSet$4(this, _Tape_symbols, symbolsCopy.map((symbol) => __classPrivateFieldGet$4(this, _Tape_alphabet, "f").index(symbol)), "f");
|
|
206
|
+
this.viewportWidth = viewportWidth;
|
|
206
207
|
}
|
|
207
208
|
get alphabet() {
|
|
208
209
|
return __classPrivateFieldGet$4(this, _Tape_alphabet, "f");
|
|
@@ -1046,7 +1047,9 @@ const initialNodeRegex = /^s(\d+)\(\("([^"]*)"\)\)$/;
|
|
|
1046
1047
|
const regularNodeRegex = /^s(\d+)\["([^"]*)"\]$/;
|
|
1047
1048
|
const transitionRegex = /^s(\d+)\s+--\s+"(.*)"\s+-->\s+s(\d+)$/;
|
|
1048
1049
|
const onHaltRegex = /^s(\d+)\s+-\.\s+onHalt\s+\.->\s+s(\d+)$/;
|
|
1049
|
-
|
|
1050
|
+
// First capture char anchored as \S to avoid polynomial backtracking between
|
|
1051
|
+
// the preceding \s* and a permissive (.+); see CodeQL js/polynomial-redos.
|
|
1052
|
+
const alphabetsRegex = /^%%\s*alphabets:\s*(\S.*)$/;
|
|
1050
1053
|
function fromMermaid(text) {
|
|
1051
1054
|
const lines = text.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
1052
1055
|
let alphabets = [];
|
|
@@ -1178,9 +1181,10 @@ function summarizeGraph(graph) {
|
|
|
1178
1181
|
}
|
|
1179
1182
|
let hasCycles = false;
|
|
1180
1183
|
const visit = (id) => {
|
|
1181
|
-
if (hasCycles)
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
+
// No `if (hasCycles) return` guard at function entry: the recursive call
|
|
1185
|
+
// pattern (outer for-loop checks before calling, inner loop checks after
|
|
1186
|
+
// each recursive call) ensures visit() is never invoked when hasCycles
|
|
1187
|
+
// is already true. Static analysis confirmed the guard was unreachable.
|
|
1184
1188
|
if (color.get(id) === GREY) {
|
|
1185
1189
|
hasCycles = true;
|
|
1186
1190
|
return;
|
|
@@ -1277,12 +1281,16 @@ function runOnce(runnable, input, stepsLimit) {
|
|
|
1277
1281
|
const machine = new TuringMachine({ tapeBlock });
|
|
1278
1282
|
const snapshots = [];
|
|
1279
1283
|
let stepCount = 0;
|
|
1280
|
-
//
|
|
1281
|
-
//
|
|
1282
|
-
//
|
|
1283
|
-
|
|
1284
|
+
// Iterate the generator manually (the yielded MachineState isn't needed —
|
|
1285
|
+
// we only care about the side effects on the tape). At each yield the tape
|
|
1286
|
+
// reflects the state BEFORE the current step's command; after the loop
|
|
1287
|
+
// exits the tape has had every command applied.
|
|
1288
|
+
const generator = machine.runStepByStep({ initialState: runnable.state, stepsLimit });
|
|
1289
|
+
let result = generator.next();
|
|
1290
|
+
while (!result.done) {
|
|
1284
1291
|
snapshots.push(tape.symbols.join(''));
|
|
1285
1292
|
stepCount += 1;
|
|
1293
|
+
result = generator.next();
|
|
1286
1294
|
}
|
|
1287
1295
|
snapshots.push(tape.symbols.join(''));
|
|
1288
1296
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@turing-machine-js/machine",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "A convenient Turing machine",
|
|
5
5
|
"engines": {
|
|
6
6
|
"npm": ">=7.0.0"
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"default": "./dist/index.mjs"
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "0a78b4d0d40bbdbf79e3c53694adf930f0567b0a"
|
|
38
38
|
}
|
package/dist/classes/Alphabet.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
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");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
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");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _Alphabet_symbols;
|
|
13
|
-
import { uniquePredicate } from '../utilities/functions';
|
|
14
|
-
class Alphabet {
|
|
15
|
-
constructor(symbols) {
|
|
16
|
-
_Alphabet_symbols.set(this, void 0);
|
|
17
|
-
if (symbols instanceof Alphabet) {
|
|
18
|
-
symbols = symbols.symbols;
|
|
19
|
-
}
|
|
20
|
-
const uniqueSymbols = symbols.filter(uniquePredicate);
|
|
21
|
-
if (uniqueSymbols.length < 2) {
|
|
22
|
-
throw new Error('Invalid symbols length');
|
|
23
|
-
}
|
|
24
|
-
const isSymbolsValid = uniqueSymbols.every((symbol) => symbol.length === 1);
|
|
25
|
-
if (!isSymbolsValid) {
|
|
26
|
-
throw new Error('symbols contains invalid symbol');
|
|
27
|
-
}
|
|
28
|
-
__classPrivateFieldSet(this, _Alphabet_symbols, Array.from(uniqueSymbols), "f");
|
|
29
|
-
}
|
|
30
|
-
get symbols() {
|
|
31
|
-
return Array.from(__classPrivateFieldGet(this, _Alphabet_symbols, "f"));
|
|
32
|
-
}
|
|
33
|
-
get blankSymbol() {
|
|
34
|
-
return __classPrivateFieldGet(this, _Alphabet_symbols, "f")[0];
|
|
35
|
-
}
|
|
36
|
-
has(symbol) {
|
|
37
|
-
return __classPrivateFieldGet(this, _Alphabet_symbols, "f").indexOf(symbol) >= 0;
|
|
38
|
-
}
|
|
39
|
-
get(index) {
|
|
40
|
-
if (index < 0 || index >= __classPrivateFieldGet(this, _Alphabet_symbols, "f").length) {
|
|
41
|
-
throw new Error('Invalid index');
|
|
42
|
-
}
|
|
43
|
-
return __classPrivateFieldGet(this, _Alphabet_symbols, "f")[index];
|
|
44
|
-
}
|
|
45
|
-
index(symbol) {
|
|
46
|
-
return __classPrivateFieldGet(this, _Alphabet_symbols, "f").indexOf(symbol);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
_Alphabet_symbols = new WeakMap();
|
|
50
|
-
export default Alphabet;
|
package/dist/classes/Command.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
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");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
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");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _Command_tapesCommands;
|
|
13
|
-
import TapeCommand from './TapeCommand';
|
|
14
|
-
class Command {
|
|
15
|
-
constructor(tapesCommands) {
|
|
16
|
-
_Command_tapesCommands.set(this, void 0);
|
|
17
|
-
if (tapesCommands.length === 0) {
|
|
18
|
-
throw new Error('invalid parameter');
|
|
19
|
-
}
|
|
20
|
-
try {
|
|
21
|
-
__classPrivateFieldSet(this, _Command_tapesCommands, tapesCommands.map((tapeCommand) => {
|
|
22
|
-
if (tapeCommand instanceof TapeCommand) {
|
|
23
|
-
return tapeCommand;
|
|
24
|
-
}
|
|
25
|
-
return new TapeCommand(tapeCommand);
|
|
26
|
-
}), "f");
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
void error;
|
|
30
|
-
throw new Error('invalid tapeCommand');
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
get tapesCommands() {
|
|
34
|
-
return [...__classPrivateFieldGet(this, _Command_tapesCommands, "f")];
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
_Command_tapesCommands = new WeakMap();
|
|
38
|
-
export default Command;
|
package/dist/classes/Lock.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
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");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
-
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");
|
|
10
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
-
};
|
|
12
|
-
var _Lock_lockSymbol;
|
|
13
|
-
class Lock {
|
|
14
|
-
constructor() {
|
|
15
|
-
_Lock_lockSymbol.set(this, null);
|
|
16
|
-
}
|
|
17
|
-
lock(symbol) {
|
|
18
|
-
if (__classPrivateFieldGet(this, _Lock_lockSymbol, "f") === null) {
|
|
19
|
-
__classPrivateFieldSet(this, _Lock_lockSymbol, symbol, "f");
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
unlock(symbol) {
|
|
23
|
-
if (__classPrivateFieldGet(this, _Lock_lockSymbol, "f") === symbol) {
|
|
24
|
-
__classPrivateFieldSet(this, _Lock_lockSymbol, null, "f");
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
check(symbol) {
|
|
28
|
-
if (__classPrivateFieldGet(this, _Lock_lockSymbol, "f") && __classPrivateFieldGet(this, _Lock_lockSymbol, "f") !== symbol) {
|
|
29
|
-
throw new Error('Lock check failed');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
_Lock_lockSymbol = new WeakMap();
|
|
34
|
-
export default Lock;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
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");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
-
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");
|
|
10
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
-
};
|
|
12
|
-
var _Reference_referenceBinding;
|
|
13
|
-
class Reference {
|
|
14
|
-
constructor() {
|
|
15
|
-
_Reference_referenceBinding.set(this, null);
|
|
16
|
-
}
|
|
17
|
-
get ref() {
|
|
18
|
-
if (!__classPrivateFieldGet(this, _Reference_referenceBinding, "f")) {
|
|
19
|
-
throw new Error('unbounded reference');
|
|
20
|
-
}
|
|
21
|
-
return __classPrivateFieldGet(this, _Reference_referenceBinding, "f");
|
|
22
|
-
}
|
|
23
|
-
bind(state) {
|
|
24
|
-
if (__classPrivateFieldGet(this, _Reference_referenceBinding, "f") == null) {
|
|
25
|
-
__classPrivateFieldSet(this, _Reference_referenceBinding, state, "f");
|
|
26
|
-
}
|
|
27
|
-
return __classPrivateFieldGet(this, _Reference_referenceBinding, "f");
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
_Reference_referenceBinding = new WeakMap();
|
|
31
|
-
export default Reference;
|
package/dist/classes/State.js
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
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");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
-
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");
|
|
10
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
-
};
|
|
12
|
-
var _State_id, _State_name, _State_overrodeHaltState, _State_symbolToDataMap;
|
|
13
|
-
import Alphabet from './Alphabet';
|
|
14
|
-
import Command from './Command';
|
|
15
|
-
import Reference from './Reference';
|
|
16
|
-
import TapeBlock from './TapeBlock';
|
|
17
|
-
import TapeCommand from './TapeCommand';
|
|
18
|
-
import { id } from '../utilities/functions';
|
|
19
|
-
import { decodeMovement, decodePatternDescription, decodeWriteSymbol, parseMovementLabel, parsePatternString, parseWriteSymbolLabel, } from '../utilities/graph';
|
|
20
|
-
export const ifOtherSymbol = Symbol('other symbol');
|
|
21
|
-
class State {
|
|
22
|
-
constructor(stateDefinition = null, name) {
|
|
23
|
-
_State_id.set(this, id(this));
|
|
24
|
-
_State_name.set(this, void 0);
|
|
25
|
-
_State_overrodeHaltState.set(this, null);
|
|
26
|
-
_State_symbolToDataMap.set(this, new Map());
|
|
27
|
-
if (stateDefinition) {
|
|
28
|
-
const keys = Object.getOwnPropertyNames(stateDefinition);
|
|
29
|
-
if (keys.length) {
|
|
30
|
-
throw new Error(`invalid state definition while constructing state #${__classPrivateFieldGet(this, _State_id, "f")}`);
|
|
31
|
-
}
|
|
32
|
-
const symbols = Object.getOwnPropertySymbols(stateDefinition);
|
|
33
|
-
if (symbols.length === 0) {
|
|
34
|
-
throw new Error(`invalid state definition while constructing state #${__classPrivateFieldGet(this, _State_id, "f")}`);
|
|
35
|
-
}
|
|
36
|
-
symbols.forEach((symbol) => {
|
|
37
|
-
const { nextState } = stateDefinition[symbol];
|
|
38
|
-
const nextStateLocal = nextState ?? this;
|
|
39
|
-
if (!(nextStateLocal instanceof State) && !(nextStateLocal instanceof Reference)) {
|
|
40
|
-
throw new Error('invalid nextState');
|
|
41
|
-
}
|
|
42
|
-
let { command } = stateDefinition[symbol];
|
|
43
|
-
if (command == null) {
|
|
44
|
-
command = new Command([
|
|
45
|
-
new TapeCommand({}),
|
|
46
|
-
]);
|
|
47
|
-
}
|
|
48
|
-
if (!(command instanceof Command) && !Array.isArray(command)) {
|
|
49
|
-
command = [command];
|
|
50
|
-
}
|
|
51
|
-
let commandLocal = command;
|
|
52
|
-
if (Array.isArray(command)) {
|
|
53
|
-
try {
|
|
54
|
-
commandLocal = new Command(command);
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
void error;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (!(commandLocal instanceof Command)) {
|
|
61
|
-
throw new Error('invalid command');
|
|
62
|
-
}
|
|
63
|
-
__classPrivateFieldGet(this, _State_symbolToDataMap, "f").set(symbol, {
|
|
64
|
-
command: commandLocal,
|
|
65
|
-
nextState: nextStateLocal,
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
__classPrivateFieldSet(this, _State_name, name ?? `id:${__classPrivateFieldGet(this, _State_id, "f")}`, "f");
|
|
70
|
-
}
|
|
71
|
-
get id() {
|
|
72
|
-
return __classPrivateFieldGet(this, _State_id, "f");
|
|
73
|
-
}
|
|
74
|
-
get name() {
|
|
75
|
-
return __classPrivateFieldGet(this, _State_name, "f");
|
|
76
|
-
}
|
|
77
|
-
get isHalt() {
|
|
78
|
-
return __classPrivateFieldGet(this, _State_id, "f") === 0;
|
|
79
|
-
}
|
|
80
|
-
get overrodeHaltState() {
|
|
81
|
-
return __classPrivateFieldGet(this, _State_overrodeHaltState, "f");
|
|
82
|
-
}
|
|
83
|
-
get ref() {
|
|
84
|
-
return this;
|
|
85
|
-
}
|
|
86
|
-
getSymbol(tapeBlock) {
|
|
87
|
-
const symbol = [...__classPrivateFieldGet(this, _State_symbolToDataMap, "f").keys()].find((currentSymbol) => tapeBlock.isMatched({
|
|
88
|
-
symbol: currentSymbol,
|
|
89
|
-
}));
|
|
90
|
-
if (symbol) {
|
|
91
|
-
return symbol;
|
|
92
|
-
}
|
|
93
|
-
return ifOtherSymbol;
|
|
94
|
-
}
|
|
95
|
-
getCommand(symbol) {
|
|
96
|
-
if (__classPrivateFieldGet(this, _State_symbolToDataMap, "f").has(symbol)) {
|
|
97
|
-
return __classPrivateFieldGet(this, _State_symbolToDataMap, "f").get(symbol).command;
|
|
98
|
-
}
|
|
99
|
-
throw new Error(`No command for symbol at state named ${__classPrivateFieldGet(this, _State_name, "f")}`);
|
|
100
|
-
}
|
|
101
|
-
getNextState(symbol) {
|
|
102
|
-
if (__classPrivateFieldGet(this, _State_symbolToDataMap, "f").has(symbol)) {
|
|
103
|
-
return __classPrivateFieldGet(this, _State_symbolToDataMap, "f").get(symbol).nextState;
|
|
104
|
-
}
|
|
105
|
-
throw new Error(`No nextState for symbol at state named ${__classPrivateFieldGet(this, _State_id, "f")}`);
|
|
106
|
-
}
|
|
107
|
-
withOverrodeHaltState(overrodeHaltState) {
|
|
108
|
-
const state = new State(null, `${this.name}>${overrodeHaltState.name}`);
|
|
109
|
-
__classPrivateFieldSet(state, _State_symbolToDataMap, __classPrivateFieldGet(this, _State_symbolToDataMap, "f"), "f");
|
|
110
|
-
__classPrivateFieldSet(state, _State_overrodeHaltState, overrodeHaltState, "f");
|
|
111
|
-
return state;
|
|
112
|
-
}
|
|
113
|
-
// Single-state introspection — no traversal, no tapeBlock required.
|
|
114
|
-
// Returns id, name, halt-status, override-halt target, and the list of
|
|
115
|
-
// transitions out of this state with decoded write/movement labels.
|
|
116
|
-
// Symbol patterns are returned as the raw description string from the
|
|
117
|
-
// interned JS Symbol (decode via decodePatternDescription if needed).
|
|
118
|
-
static inspect(state) {
|
|
119
|
-
const transitions = [];
|
|
120
|
-
for (const [sym, { command, nextState }] of __classPrivateFieldGet(state, _State_symbolToDataMap, "f")) {
|
|
121
|
-
let target = null;
|
|
122
|
-
try {
|
|
123
|
-
target = nextState instanceof State ? nextState : nextState.ref;
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
target = null; // unbound Reference
|
|
127
|
-
}
|
|
128
|
-
transitions.push({
|
|
129
|
-
rawPatternDescription: sym.description,
|
|
130
|
-
command: command.tapesCommands.map((tc) => ({
|
|
131
|
-
symbol: decodeWriteSymbol(tc.symbol),
|
|
132
|
-
movement: decodeMovement(tc.movement.description),
|
|
133
|
-
})),
|
|
134
|
-
nextState: target ? { id: target.id, name: target.name } : null,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
return {
|
|
138
|
-
id: __classPrivateFieldGet(state, _State_id, "f"),
|
|
139
|
-
name: __classPrivateFieldGet(state, _State_name, "f"),
|
|
140
|
-
isHalt: state.isHalt,
|
|
141
|
-
overrodeHaltState: __classPrivateFieldGet(state, _State_overrodeHaltState, "f")
|
|
142
|
-
? { id: __classPrivateFieldGet(state, _State_overrodeHaltState, "f").id, name: __classPrivateFieldGet(state, _State_overrodeHaltState, "f").name }
|
|
143
|
-
: null,
|
|
144
|
-
transitions,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
static toGraph(initialState, tapeBlock) {
|
|
148
|
-
const nodes = {};
|
|
149
|
-
const queue = [initialState];
|
|
150
|
-
const alphabets = tapeBlock.alphabets.map((alphabet) => alphabet.symbols);
|
|
151
|
-
while (queue.length > 0) {
|
|
152
|
-
const current = queue.shift();
|
|
153
|
-
if (__classPrivateFieldGet(current, _State_id, "f") in nodes) {
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
const node = {
|
|
157
|
-
id: __classPrivateFieldGet(current, _State_id, "f"),
|
|
158
|
-
name: __classPrivateFieldGet(current, _State_name, "f"),
|
|
159
|
-
isHalt: current.isHalt,
|
|
160
|
-
transitions: [],
|
|
161
|
-
overrodeHaltStateId: __classPrivateFieldGet(current, _State_overrodeHaltState, "f")?.id ?? null,
|
|
162
|
-
};
|
|
163
|
-
nodes[__classPrivateFieldGet(current, _State_id, "f")] = node;
|
|
164
|
-
if (__classPrivateFieldGet(current, _State_overrodeHaltState, "f")) {
|
|
165
|
-
queue.push(__classPrivateFieldGet(current, _State_overrodeHaltState, "f"));
|
|
166
|
-
}
|
|
167
|
-
for (const [sym, { command, nextState }] of __classPrivateFieldGet(current, _State_symbolToDataMap, "f")) {
|
|
168
|
-
let target;
|
|
169
|
-
try {
|
|
170
|
-
target = nextState instanceof State ? nextState : nextState.ref;
|
|
171
|
-
}
|
|
172
|
-
catch {
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
node.transitions.push({
|
|
176
|
-
pattern: decodePatternDescription(sym.description, alphabets),
|
|
177
|
-
command: command.tapesCommands.map((tc) => ({
|
|
178
|
-
symbol: decodeWriteSymbol(tc.symbol),
|
|
179
|
-
movement: decodeMovement(tc.movement.description),
|
|
180
|
-
})),
|
|
181
|
-
nextStateId: target.id,
|
|
182
|
-
});
|
|
183
|
-
queue.push(target);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return { initialId: __classPrivateFieldGet(initialState, _State_id, "f"), alphabets, nodes };
|
|
187
|
-
}
|
|
188
|
-
// Inverse of toGraph: rebuilds a State graph (and a fresh TapeBlock with the
|
|
189
|
-
// graph's alphabets) from a serialized Graph. Round-trips with toGraph in
|
|
190
|
-
// the sense that running the rebuilt machine on the same input gives the
|
|
191
|
-
// same output, but the rebuilt State instances have *new* internal IDs.
|
|
192
|
-
static fromGraph(graph) {
|
|
193
|
-
const alphabetObjs = graph.alphabets.map((syms) => new Alphabet(syms));
|
|
194
|
-
const tapeBlock = TapeBlock.fromAlphabets(alphabetObjs);
|
|
195
|
-
const ids = Object.keys(graph.nodes).map(Number);
|
|
196
|
-
// Pass 1: pre-create a Reference for each non-halt node so transitions can
|
|
197
|
-
// forward-declare their targets.
|
|
198
|
-
const refs = {};
|
|
199
|
-
for (const nodeId of ids) {
|
|
200
|
-
if (!graph.nodes[nodeId].isHalt) {
|
|
201
|
-
refs[nodeId] = new Reference();
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
// Convert a parsed pattern back to the symbol key the State expects.
|
|
205
|
-
const patternToKey = (parsed) => {
|
|
206
|
-
if (parsed === null) {
|
|
207
|
-
return ifOtherSymbol;
|
|
208
|
-
}
|
|
209
|
-
const flat = [];
|
|
210
|
-
for (const row of parsed) {
|
|
211
|
-
for (const cell of row) {
|
|
212
|
-
flat.push(cell === null ? ifOtherSymbol : cell);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return tapeBlock.symbol(flat);
|
|
216
|
-
};
|
|
217
|
-
// Pass 2: build a "bare" State for each non-halt node (no override yet).
|
|
218
|
-
// nextState entries point at refs so cycles work; haltState is used directly.
|
|
219
|
-
const bareStates = {};
|
|
220
|
-
for (const nodeId of ids) {
|
|
221
|
-
const node = graph.nodes[nodeId];
|
|
222
|
-
if (node.isHalt) {
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
const stateDefinition = {};
|
|
226
|
-
for (const t of node.transitions) {
|
|
227
|
-
const key = patternToKey(parsePatternString(t.pattern, graph.alphabets));
|
|
228
|
-
const target = graph.nodes[t.nextStateId];
|
|
229
|
-
const nextState = target.isHalt ? haltState : refs[t.nextStateId];
|
|
230
|
-
stateDefinition[key] = {
|
|
231
|
-
command: t.command.map((c) => ({
|
|
232
|
-
symbol: parseWriteSymbolLabel(c.symbol),
|
|
233
|
-
movement: parseMovementLabel(c.movement),
|
|
234
|
-
})),
|
|
235
|
-
nextState,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
bareStates[nodeId] = new State(stateDefinition, node.name);
|
|
239
|
-
}
|
|
240
|
-
// Pass 3: apply overrideHaltStates transitively.
|
|
241
|
-
const finalStates = {};
|
|
242
|
-
const inProgress = new Set();
|
|
243
|
-
const getFinal = (nodeId) => {
|
|
244
|
-
if (finalStates[nodeId]) {
|
|
245
|
-
return finalStates[nodeId];
|
|
246
|
-
}
|
|
247
|
-
const node = graph.nodes[nodeId];
|
|
248
|
-
if (node.isHalt) {
|
|
249
|
-
finalStates[nodeId] = haltState;
|
|
250
|
-
return haltState;
|
|
251
|
-
}
|
|
252
|
-
if (inProgress.has(nodeId)) {
|
|
253
|
-
throw new Error(`override-halt cycle at state #${nodeId}`);
|
|
254
|
-
}
|
|
255
|
-
inProgress.add(nodeId);
|
|
256
|
-
let state = bareStates[nodeId];
|
|
257
|
-
if (node.overrodeHaltStateId !== null) {
|
|
258
|
-
state = bareStates[nodeId].withOverrodeHaltState(getFinal(node.overrodeHaltStateId));
|
|
259
|
-
}
|
|
260
|
-
inProgress.delete(nodeId);
|
|
261
|
-
finalStates[nodeId] = state;
|
|
262
|
-
return state;
|
|
263
|
-
};
|
|
264
|
-
for (const nodeId of ids) {
|
|
265
|
-
getFinal(nodeId);
|
|
266
|
-
}
|
|
267
|
-
// Pass 4: bind each ref to the FINAL (possibly wrapped) state so transitions
|
|
268
|
-
// resolve to the version that has its override-halt set.
|
|
269
|
-
for (const nodeId of ids) {
|
|
270
|
-
if (!graph.nodes[nodeId].isHalt) {
|
|
271
|
-
refs[nodeId].bind(finalStates[nodeId]);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
return {
|
|
275
|
-
start: finalStates[graph.initialId],
|
|
276
|
-
tapeBlock,
|
|
277
|
-
states: finalStates,
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
_State_id = new WeakMap(), _State_name = new WeakMap(), _State_overrodeHaltState = new WeakMap(), _State_symbolToDataMap = new WeakMap();
|
|
282
|
-
export default State;
|
|
283
|
-
export const haltState = new State(null);
|