@turing-machine-js/machine 7.0.0-alpha.2 → 7.0.0-alpha.4
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 +90 -0
- package/README.md +69 -1
- package/dist/classes/State.d.ts +99 -0
- package/dist/index.cjs +1041 -522
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +1041 -522
- package/dist/utilities/graph.d.ts +1 -0
- package/dist/utilities/stateGraph.d.ts +91 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,96 @@ 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
|
+
## [7.0.0-alpha.4] - 2026-05-23
|
|
8
|
+
|
|
9
|
+
Fourth v7 pre-release. Adds an id-keyed lookup helper for the State graph ([#195](https://github.com/mellonis/turing-machine-js/issues/195)), fixes two upstream issues surfaced while wiring the new helper into downstream tooling — a `toMermaid` label-grammar bug ([#194](https://github.com/mellonis/turing-machine-js/issues/194)) and a halt-stack lifetime bug in `runStepByStep` ([#196](https://github.com/mellonis/turing-machine-js/issues/196)) — and extracts graph serialization into its own module without API change ([#180](https://github.com/mellonis/turing-machine-js/issues/180)). Published under the `next` dist-tag: `npm install @turing-machine-js/machine@next`.
|
|
10
|
+
|
|
11
|
+
**Pre-release — the API surface may still shift before stable v7.0.0.** Pin to a specific alpha for reproducibility: `@turing-machine-js/machine@7.0.0-alpha.4`.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`State.collectStates(initialState, tapeBlock)`** ([#195](https://github.com/mellonis/turing-machine-js/issues/195)). Static helper that returns a `Map<number, {state: State; transitionSymbols: symbol[]}>` keyed by engine `GraphNode.id`. Lets downstream tooling (graph renderers, debugger panels) mutate `state.debug` on a specific State by numeric id, and set per-pattern breakpoints by `GraphTransition.id`. The K-th `transitionSymbols` slot is positionally aligned with the GraphTransition whose id is `${stateId}-${K}`, so a consumer holding `(stateId, patternIx)` from the rendered graph reaches the firing Symbol with no walk.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
const stateMap = State.collectStates(initial, tapeBlock);
|
|
19
|
+
|
|
20
|
+
// State-level breakpoint by id (any pattern fires).
|
|
21
|
+
stateMap.get(clickedStateId)!.state.debug.before = true;
|
|
22
|
+
|
|
23
|
+
// Per-pattern breakpoint by GraphTransition.id ("${stateId}-${patternIx}").
|
|
24
|
+
const [n, k] = clickedEdgeId.split('-').map(Number);
|
|
25
|
+
const entry = stateMap.get(n)!;
|
|
26
|
+
entry.state.debug.before = [entry.transitionSymbols[k]!];
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Coverage: regular / bare states get the full `[...#symbolToDataMap.keys()]` including `ifOtherSymbol` at its natural slot; wrappers and the halt singleton get empty `transitionSymbols`; synthetic halt markers (`isHaltMarker: true`, id `= -frameId`) are excluded — they all collapse to `haltState` at runtime, and the named consumer surfaces halt-pause via a separate UI control, not via clicks on halt glyphs. The halt singleton entry at id `0` is the process-wide `haltState` — toggling its `.debug` affects every machine in the runtime, same caveat as direct `haltState.debug` writes.
|
|
30
|
+
|
|
31
|
+
- **`StateMap` and `StateMapEntry` types** ([#195](https://github.com/mellonis/turing-machine-js/issues/195)). Exported from the package's public surface so TypeScript consumers can annotate `collectStates` results without re-deriving the shape.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **Graph serialization extracted to `utilities/stateGraph.ts`** ([#180](https://github.com/mellonis/turing-machine-js/issues/180)). `State.toGraph` and `State.fromGraph` move out of the State class into a sibling module, alongside the new `collectStates`. Public surface preserved — `State.toGraph` / `State.fromGraph` remain as thin static delegates. State.ts shrank ~440 lines and now focuses on the runtime machinery (transitions, debug, halt-stack composition) rather than mixing in serialization concerns. A new `@internal` `STATE_INTERNAL` Symbol-keyed accessor on `State` gives sibling modules in `packages/machine/src` getter/setter access to private fields (`id`, `name`, `bareState`, `overriddenHaltState`, `symbolToDataMap`, `tags`); not re-exported from the public `index.ts`, so external consumers can't observe it.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **`toMermaid` edge labels containing literal `"` now parse correctly** ([#194](https://github.com/mellonis/turing-machine-js/issues/194)). Before: an alphabet that includes printable ASCII as literal symbols (e.g. a Brainfuck-flavored UTM whose data alphabet covers U+0020–U+007E) would emit an edge label like `s1 -- "['a'] → ['"']/[R]" --> s0`; Mermaid's parser terminated the string early on the inner `"`. After: user-supplied content (alphabet symbols, state names, tag names, frame bare names) is HTML-entity-escaped at the leaf — `&`, `"`, `<`, `>` to named entities; statement terminators (`\n`, `\r`), C0 controls minus `\t`, DEL, bidi controls, and lone UTF-16 surrogates to numeric entities. Printable Unicode (Cyrillic, CJK, accented Latin, etc.) passes through unchanged so non-ASCII alphabets stay readable in the emitted `.mmd`. `fromMermaid` mirrors with a single-pass entity decoder applied at the leaf, after structural parsing.
|
|
40
|
+
|
|
41
|
+
- **`runStepByStep` halt-stack is now run-scoped, not machine-scoped** ([#196](https://github.com/mellonis/turing-machine-js/issues/196)). Before: the `#stack` field on `TuringMachine` was an instance field; a build-time peek that didn't drain the generator (e.g. graph-construction utilities that ask for one yield to inspect the initial state) left leftover entries in the stack that were popped during the NEXT halt-bound transition, producing a "ghost iteration" and silently leaking memory across consecutive `runStepByStep` calls on the same machine. After: the halt stack is a local `const stack: State[] = []` declared inside `runStepByStep`, so each generator call starts with a clean stack and entries can't survive into the next call.
|
|
42
|
+
|
|
43
|
+
### Compatibility
|
|
44
|
+
|
|
45
|
+
- Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.3` → `^7.0.0-alpha.4` on `@turing-machine-js/builder`, `@turing-machine-js/library-binary-numbers`, `@turing-machine-js/library-binary-numbers-bare`.
|
|
46
|
+
|
|
47
|
+
### Migration from alpha.3
|
|
48
|
+
|
|
49
|
+
Purely additive — no breaking changes. Existing code that doesn't call `State.collectStates` continues to work identically. `State.toGraph` / `State.fromGraph` behave identically (the delegate-to-stateGraph wiring is internal); no consumer changes needed.
|
|
50
|
+
|
|
51
|
+
The `STATE_INTERNAL` accessor is `@internal` and not part of the supported surface; ignore it unless you're authoring a sibling module inside `packages/machine/src`.
|
|
52
|
+
|
|
53
|
+
### Out of v7-alpha.4 (still pending for stable v7.0.0)
|
|
54
|
+
|
|
55
|
+
- **[#102](https://github.com/mellonis/turing-machine-js/issues/102)** — debugger step-in / step-out / step-over primitives.
|
|
56
|
+
|
|
57
|
+
## [7.0.0-alpha.3] - 2026-05-21
|
|
58
|
+
|
|
59
|
+
Third v7 pre-release. Adds first-class out-of-band tags on `State` ([#186](https://github.com/mellonis/turing-machine-js/issues/186)) — a metadata channel for visualization grouping and debugger labels that survives `toGraph` / `fromGraph` / `toMermaid` / `fromMermaid` round-trips. Driven by downstream [post-machine-js#86](https://github.com/mellonis/post-machine-js/issues/86), which will build a path-based registry and inline pseudo-command on top once this ships. Published under the `next` dist-tag: `npm install @turing-machine-js/machine@next`.
|
|
60
|
+
|
|
61
|
+
**Pre-release — the API surface may still shift before stable v7.0.0.** Pin to a specific alpha for reproducibility: `@turing-machine-js/machine@7.0.0-alpha.3`.
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- **`State.tag(...) / .untag(...) / .tags` API** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). Fluent post-construct API for attaching string tags to a State. Tags are out-of-band metadata — they don't affect runtime transition lookup, `equivalentOn` comparisons, or any structural identity. Storage lives on the State INSTANCE (not on the shared `#symbolToDataMap`), so engine [#175](https://github.com/mellonis/turing-machine-js/issues/175) memoization doesn't leak tags across wrappers that share a bare: `A.wohs(t1).tag('hot')` does NOT propagate to `A.wohs(t2)`.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const s = new State({...}, 'foo')
|
|
69
|
+
.tag('hot', 'sampled')
|
|
70
|
+
.untag('sampled');
|
|
71
|
+
s.tags; // ['hot'] ← frozen snapshot, in insertion order
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- **`GraphNode.tags: string[]`** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). New field on the serialized graph node. Survives `State.toGraph` / `State.fromGraph` round-trip; empty array for untagged states.
|
|
75
|
+
|
|
76
|
+
- **`toMermaid` tag rendering** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). Two surfaces:
|
|
77
|
+
- **Visible labels via `<br>`**: tagged node labels include the tag names inline (`s5["A<br>hot, sampled"]`). Uses Mermaid's universal `<br>` line break — works across mermaid.js, the live editor, machines-demo, and any other renderer; no CSS-pseudo-element tricks.
|
|
78
|
+
- **Color grouping via `classDef` + `class`**: each unique tag gets a `classDef tag_<sanitized> fill:#...,stroke:#...` line (palette of 6 colors selected by tag-name hash) and a `class s5,s6 tag_<sanitized>` line listing every node carrying the tag.
|
|
79
|
+
|
|
80
|
+
- **`fromMermaid` tag parse** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). Splits the node label on `<br>` to extract tags as the source of truth; `class` lines are decorative and discarded on parse (they regenerate on the next `toMermaid` emit from the tag set).
|
|
81
|
+
|
|
82
|
+
### Compatibility
|
|
83
|
+
|
|
84
|
+
- Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.2` → `^7.0.0-alpha.3` on `@turing-machine-js/builder`, `@turing-machine-js/library-binary-numbers`, `@turing-machine-js/library-binary-numbers-bare`.
|
|
85
|
+
|
|
86
|
+
### Migration from alpha.2
|
|
87
|
+
|
|
88
|
+
Purely additive — no breaking changes. Existing code that doesn't call `state.tag(...)` or read `state.tags` / `GraphNode.tags` continues to work identically. Mermaid emit for untagged states is bytewise unchanged.
|
|
89
|
+
|
|
90
|
+
If you serialize `GraphNode` JSON, note that the new `tags: string[]` field is now required by the type (always emitted; empty array if no tags).
|
|
91
|
+
|
|
92
|
+
### Out of v7-alpha.3 (still pending for stable v7.0.0)
|
|
93
|
+
|
|
94
|
+
- **[#102](https://github.com/mellonis/turing-machine-js/issues/102)** — debugger step-in / step-out / step-over primitives.
|
|
95
|
+
- **[#180](https://github.com/mellonis/turing-machine-js/issues/180)** — extract `State.toGraph`/`fromGraph` to its own module.
|
|
96
|
+
|
|
7
97
|
## [7.0.0-alpha.2] - 2026-05-21
|
|
8
98
|
|
|
9
99
|
Second v7 pre-release. Refines alpha.1's `toMermaid` wrapped-state emit into the function-call model ([#174](https://github.com/mellonis/turing-machine-js/issues/174)) and adds two construction-time improvements to `withOverriddenHaltState` ([#175](https://github.com/mellonis/turing-machine-js/issues/175), [#176](https://github.com/mellonis/turing-machine-js/issues/176)). Published to npm under the `next` dist-tag: `npm install @turing-machine-js/machine@next`.
|
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ A composable Turing-machine engine for JavaScript: multi-tape, subroutine compos
|
|
|
13
13
|
- [Building from a state table](#building-from-a-state-table)
|
|
14
14
|
- [Classes](#classes) — [`Alphabet`](#alphabet) · [`Tape`](#tape) · [`TapeBlock`](#tapeblock) · [`TapeCommand`](#tapecommand) · [`Command`](#command) · [`State`](#state) · [`Reference`](#reference) · [`TuringMachine`](#turingmachine)
|
|
15
15
|
- [Subroutine composition with `withOverriddenHaltState`](#subroutine-composition-with-withoverriddenhaltstate)
|
|
16
|
+
- [State tags](#state-tags)
|
|
16
17
|
- [Debugging breakpoints](#debugging-breakpoints)
|
|
17
18
|
- [Special objects](#special-objects) — [`haltState`](#haltstate) · [`ifOtherSymbol`](#ifothersymbol) · [`movements`](#movements) · [`symbolCommands`](#symbolcommands)
|
|
18
19
|
- [Introspection and testing](#introspection-and-testing)
|
|
@@ -241,6 +242,7 @@ Notable members and statics:
|
|
|
241
242
|
- **`state.withOverriddenHaltState(other)`** — returns a copy whose would-be halt transitions fall through to `other`. The subroutine-call composition mechanism (see `library-binary-numbers/src/index.ts` for examples).
|
|
242
243
|
- **`State.toGraph(state, tapeBlock)`** — walks the reachable graph from `state` and returns a serializable `Graph` (states, transitions, alphabets).
|
|
243
244
|
- **`State.fromGraph(graph)`** — inverse of `toGraph`: rebuilds `State` instances + a fresh `TapeBlock` from a `Graph`. Round-trips together with `toMermaid` / `fromMermaid`.
|
|
245
|
+
- **`State.collectStates(state, tapeBlock)`** — walks the same graph and returns a `Map<number, {state, transitionSymbols}>` keyed by `GraphNode.id`. Use when downstream tooling holds a numeric id (e.g. a clicked node in a rendered graph) and needs the live `State` instance or the per-pattern `Symbol` for breakpoint setup. See [Setting breakpoints by graph id](#setting-breakpoints-by-graph-id).
|
|
244
246
|
|
|
245
247
|
For visualization, pair `State.toGraph` with `toMermaid` to render the graph in any Mermaid-aware viewer (GitHub, VS Code, mermaid.live):
|
|
246
248
|
|
|
@@ -427,6 +429,25 @@ flowchart TD
|
|
|
427
429
|
|
|
428
430
|
Wrappers nest: `inner.withOverriddenHaltState(middle).withOverriddenHaltState(outer)` chains halt-redirects through `middle → outer → halt`. `library-binary-numbers/src/index.ts`'s `minusOne` (the `~(~x + 1)` composition) uses a 4-deep nest of wrappers.
|
|
429
431
|
|
|
432
|
+
## State tags
|
|
433
|
+
|
|
434
|
+
A State carries an optional set of string tags — out-of-band metadata for visualization grouping and debugger labels. Tags don't affect runtime transition lookup, `equivalentOn` comparisons, or any structural identity; they ride alongside the State.
|
|
435
|
+
|
|
436
|
+
```ts
|
|
437
|
+
const s = new State({...}, 'walkToBlank::1')
|
|
438
|
+
.tag('hot', 'subroutine-entry');
|
|
439
|
+
|
|
440
|
+
s.tags; // readonly ['hot', 'subroutine-entry'] — frozen snapshot
|
|
441
|
+
s.untag('hot');
|
|
442
|
+
s.tags; // readonly ['subroutine-entry']
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
**Scoped to the wrapper instance.** Under [`withOverriddenHaltState` memoization (#175)](https://github.com/mellonis/turing-machine-js/issues/175), `A.wohs(t1)` and `A.wohs(t2)` are distinct wrapper instances even though they share `A`'s `#symbolToDataMap`. Tags live on the instance, so tagging one wrapper doesn't propagate to siblings sharing the same bare. Wrappers from `withOverriddenHaltState` start with an empty tag set (do not inherit from bare); the caller tags explicitly as needed.
|
|
446
|
+
|
|
447
|
+
**Round-trip preserved.** `state.toGraph` writes the tag set to `GraphNode.tags`; `state.fromGraph` reads it back and reapplies. `toMermaid` renders tags two ways: inline in the node label (`sN["name<br>tag1, tag2"]`, universal Mermaid line break) and as `classDef tag_<sanitized>` + `class sN tag_<sanitized>` lines for color grouping. `fromMermaid` splits the label on `<br>` as source of truth; the `class` lines are decorative and discarded on parse.
|
|
448
|
+
|
|
449
|
+
See [§Diagram conventions § Tags](#tags) for the full emit shape.
|
|
450
|
+
|
|
430
451
|
## Debugging breakpoints
|
|
431
452
|
|
|
432
453
|
Any `State` can carry a runtime-mutable `debug` config that pauses execution at chosen points.
|
|
@@ -479,6 +500,36 @@ If `onPause` is not provided, breaks fire-and-resume invisibly — the trajector
|
|
|
479
500
|
|
|
480
501
|
**Caveat:** `haltState` is a module-level singleton. Setting `haltState.debug` affects every machine in the process; clear in `afterEach` / `finally` for test isolation.
|
|
481
502
|
|
|
503
|
+
### Setting breakpoints by graph id
|
|
504
|
+
|
|
505
|
+
Downstream UIs (graph renderers, debugger panels) often have only a numeric `GraphNode.id` — the user clicked a state node, or a transition edge in a rendered SVG. `State.collectStates(initial, tapeBlock)` returns a `Map` keyed by that numeric id, with the live `State` instance and the per-pattern `Symbol` array as its value:
|
|
506
|
+
|
|
507
|
+
```ts
|
|
508
|
+
import { State, ifOtherSymbol } from '@turing-machine-js/machine';
|
|
509
|
+
|
|
510
|
+
const stateMap = State.collectStates(initial, tapeBlock);
|
|
511
|
+
|
|
512
|
+
// Toggle a state-level breakpoint by id (any pattern triggers).
|
|
513
|
+
const entry = stateMap.get(clickedStateId);
|
|
514
|
+
if (entry) {
|
|
515
|
+
entry.state.debug.before = true;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Per-pattern breakpoint by GraphTransition.id — the contract is
|
|
519
|
+
// positional: `transitionSymbols[K]` is the Symbol that the
|
|
520
|
+
// `${stateId}-${K}` GraphTransition fires on.
|
|
521
|
+
const [n, k] = clickedEdgeId.split('-').map(Number);
|
|
522
|
+
const e = stateMap.get(n);
|
|
523
|
+
const sym = e?.transitionSymbols[k];
|
|
524
|
+
if (e && sym) {
|
|
525
|
+
e.state.debug.before = [sym];
|
|
526
|
+
}
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
**Coverage rules:** regular / bare states get the full `[...#symbolToDataMap.keys()]` including `ifOtherSymbol` at its natural slot; wrappers and the halt singleton get empty `transitionSymbols`; synthetic halt markers (Graph nodes with `id = -frameId`, one per callable-subtree frame) are excluded from the map. See `State.collectStates` JSDoc for the full contract.
|
|
530
|
+
|
|
531
|
+
> ⚠️ `stateMap.get(0)!.state === haltState` — the entry at id `0` is the process-wide halt singleton. Toggling its `debug` affects every machine in the runtime, same caveat as direct `haltState.debug` writes.
|
|
532
|
+
|
|
482
533
|
### Throttle pattern
|
|
483
534
|
|
|
484
535
|
For per-iter throttle / animation / "wait between steps" UIs, use the **`onIter`** hook — an awaited callback that fires once at the end of every iter, after both `onPause` dispatches on the same yield. It's the engine-native shape for per-iter coordination:
|
|
@@ -599,6 +650,15 @@ The `&` ribbon syntax (`s_W1 & s_W2 == "call" ==> s_A`) collapses multiple wrapp
|
|
|
599
650
|
|
|
600
651
|
`subgraph w_N["callable subtree of NAME"] … end` wraps a bare + its body + a halt marker — the callable scope of code that runs when a wrapper "calls" the bare. Multi-bare frames (union-find merged from shared body states) use the label `"callable scope: A ∪ B"`.
|
|
601
652
|
|
|
653
|
+
### Tags
|
|
654
|
+
|
|
655
|
+
Tagged states (via `state.tag('hot', 'sampled')` — see [§State tags](#state-tags)) render two ways simultaneously:
|
|
656
|
+
|
|
657
|
+
- **Inline in the node label**: `sN["name<br>tag1, tag2"]` — the `<br>` is Mermaid's universal line break, so the tags display as a second line under the state name in any renderer.
|
|
658
|
+
- **As a color class**: `classDef tag_<sanitized> fill:#...,stroke:#...` per unique tag (6-color palette selected by tag-name hash), plus `class sN,sM tag_<sanitized>` listing all nodes carrying the tag. Lets the eye group related states by color even when their names are scattered across the diagram.
|
|
659
|
+
|
|
660
|
+
The `<br>`-embedded label is the source of truth for `fromMermaid` round-trip; the `classDef`/`class` lines are decorative and regenerate on the next `toMermaid` emit. Tag-name sanitization in `classDef` identifiers: any char outside `[A-Za-z0-9_-]` is replaced with `_`. Labels preserve the raw tag names.
|
|
661
|
+
|
|
602
662
|
### Edge label format
|
|
603
663
|
|
|
604
664
|
`[reads] → [writes]/[moves]`. Each bracketed list is a tape-block reading — one entry per tape; brackets always present, even single-tape.
|
|
@@ -654,7 +714,15 @@ API surface changes since v3, in past tense so the timing of each piece is expli
|
|
|
654
714
|
- **v6.2** *(superseded by v6.3.0)* — widened `onStep`'s signature to `(m) => void | Promise<void>` and added an inline `await onStep(...)` in the run loop, enabling throttle-in-`onStep` patterns. This overturned the docstring-stated contract that `onStep` is sync (microtask-free); the right place for per-iter throttling is `onPause` with self-rearm (see [Throttle pattern](#throttle-pattern)). Restored in v6.3.0.
|
|
655
715
|
- **v6.3** — `onStep` reverted to its v6.0–v6.1 sync contract — `(m) => void`, called synchronously inside the run loop. The Throttle pattern section documents the engine-native shape for per-iter throttle / "wait between iters" UIs. No other API changes.
|
|
656
716
|
- **v6.4** — New **`onIter`** hook on `run()`: awaited, fires once at the end of every iter (after both `onPause` dispatches on the same yield), unaffected by the `debug` master switch. Use for per-iter throttle / animation / coordination needing a suspend point; complements the existing sync `onStep` (tracing) and conditional `onPause` (user breakpoints). Three-hook contract is now `onStep` (sync, mid-iter) / `onPause` (awaited, on `state.debug` match) / `onIter` (awaited, end-of-iter). Additive — peer-deps unchanged. The v6.3.0 README's `onPause`-rearm throttle workaround is superseded.
|
|
657
|
-
- **v7** *(alpha
|
|
717
|
+
- **v7** *(latest alpha: alpha.4, 2026-05-23)* — Composition-representation overhaul + first-class state tags + id-keyed `State.collectStates` lookup. **Pre-release on the `next` dist-tag:** `npm install @turing-machine-js/machine@next` (or pin `@7.0.0-alpha.4`). Stable v7.0.0 still pending [#102](https://github.com/mellonis/turing-machine-js/issues/102) (debugger step-in/over/out primitives). Highlights across alphas:
|
|
718
|
+
|
|
719
|
+
**alpha.4** — **`State.collectStates(initial, tapeBlock)`** ([#195](https://github.com/mellonis/turing-machine-js/issues/195)) returns a `Map<number, {state, transitionSymbols}>` keyed by `GraphNode.id` so downstream tooling can mutate `state.debug` by numeric id and set per-pattern breakpoints by `GraphTransition.id`. Graph serialization extracted to `utilities/stateGraph.ts` with a Symbol-keyed `@internal` accessor on `State` ([#180](https://github.com/mellonis/turing-machine-js/issues/180); no public-API change — the `State.toGraph` / `.fromGraph` statics remain as thin delegates). Two upstream fixes: `toMermaid` HTML-entity-escapes user content in labels so alphabets containing `"`, `<`, etc. parse correctly ([#194](https://github.com/mellonis/turing-machine-js/issues/194)); `runStepByStep`'s halt stack is now run-scoped, fixing a memory leak / ghost-iteration when the same `TuringMachine` instance is reused across calls ([#196](https://github.com/mellonis/turing-machine-js/issues/196)). See [§Setting breakpoints by graph id](#setting-breakpoints-by-graph-id).
|
|
720
|
+
|
|
721
|
+
**alpha.3** — first-class **State tags** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). `state.tag(...) / .untag(...) / .tags` API; `GraphNode.tags: string[]` round-trips through `toGraph`/`fromGraph`; `toMermaid` emits tags two ways simultaneously — inline via `<br>` in node labels (`sN["name<br>tag1, tag2"]`) and as `classDef`/`class` for color grouping. Tags live on the State instance (not on the shared `#symbolToDataMap`), so engine [#175](https://github.com/mellonis/turing-machine-js/issues/175) memoization doesn't leak tags across wrappers sharing a bare. See [§State tags](#state-tags).
|
|
722
|
+
|
|
723
|
+
**alpha.2** — callable-subtree `toMermaid` emit refinement ([#174](https://github.com/mellonis/turing-machine-js/issues/174)). The wrapper is a separate `[[composite-name]]` node OUTSIDE the subgraph; the bare's reachable subtree becomes a `subgraph w_${frameId}["callable subtree of NAME"]` block. Frames computed via union-find — shared bares dedupe with `&` ribbons on call arrows. Bold `==> "call"` reserved for wrapper-to-bare; dotted `-.->` for frame dispatch (`return` / `halt` / `enter`). Plus engine memoization ([#175](https://github.com/mellonis/turing-machine-js/issues/175)) and nested-chain collapse ([#176](https://github.com/mellonis/turing-machine-js/issues/176)) for `.wohs()`.
|
|
724
|
+
|
|
725
|
+
**alpha.1** — initial v7 composition-representation overhaul:
|
|
658
726
|
- **`withOverrodeHaltState` → `withOverriddenHaltState`** ([#149](https://github.com/mellonis/turing-machine-js/issues/149)). Grammar fix on a name introduced in 2019: the past-participle `overridden` fits the "with a halt-state that has been ___" naming idiom; `overrode` (simple past) didn't. Hard cutover — no deprecated alias. The getter (`state.overrodeHaltState` → `state.overriddenHaltState`) and the serialized `Graph` data field (`node.overrodeHaltStateId` → `node.overriddenHaltStateId`) rename in lockstep. Consumer migration: global find/replace `OverrodeHaltState` → `OverriddenHaltState` and `overrodeHaltState` → `overriddenHaltState`. Persisted `State.toGraph` JSON dumps would need the same field-rename treatment, but persistence isn't a known consumer pattern.
|
|
659
727
|
- **Paren-based wrapped-state naming** ([#148](https://github.com/mellonis/turing-machine-js/issues/148)). `withOverriddenHaltState`'s composite name format changed from flat `bare>override` to nested `bare(override)`. Same nesting depth reads as `A(B(A))` (bare = `A`, override = `B(A)`) versus `A(B)(A)` (bare = `A(B)`, override = `A`) — two structurally-different wrap-trees that the old `>`-flat notation collided into the single string `A>B>A`. As a consequence, **user-provided state names must not contain `(` or `)`** — `State` now throws at construction time if a user passes such a name. The `>` character stays valid in user names (no longer reserved). The `inspect()` / `toGraph` / `toMermaid` outputs carry the new format. `states.md` files in `library-binary-numbers` regenerate accordingly.
|
|
660
728
|
- **`toMermaid` callable-subtree emit** ([#174](https://github.com/mellonis/turing-machine-js/issues/174), supersedes the alpha.1 collapsed-bare shape from #138/#139). `withOverriddenHaltState` is modeled as a function call: the wrapper is a `[[composite-name]]` call site OUTSIDE any subgraph, the bare's reachable subtree becomes a `subgraph w_${frameId}["callable subtree of NAME"] … end` block containing the bare + body states + a per-frame halt marker `c${frameId}(((halt)))`. Frames are computed via union-find on bare-reachability — overlapping reach sets merge into a single union frame, so shared bares (`library-binary-numbers/minusOne`'s `invertNumber`) appear ONCE with `& `-joined call arrows from each wrapper. Bold `==> "call"` arrows are reserved for the wrapper-to-bare call; dotted `-.->` is reserved for frame-level dispatch (`return` / `halt` / `enter`). The retired `-. onHalt .->` keyword is replaced by a solid `--> override` arrow (just an ordinary transition under the call/return mental model). `GraphNode` gains `isWrapper`, `bareStateId`, `frameId` fields (and drops `isWrapped`). Bytewise round-trip stability now holds for all wrapped states including shared-bare cases (no per-context duplication).
|
package/dist/classes/State.d.ts
CHANGED
|
@@ -3,8 +3,31 @@ import Reference from './Reference';
|
|
|
3
3
|
import TapeBlock from './TapeBlock';
|
|
4
4
|
import TapeCommand from './TapeCommand';
|
|
5
5
|
import { type Graph } from '../utilities/graph';
|
|
6
|
+
import { type StateMap } from '../utilities/stateGraph';
|
|
6
7
|
export declare const ifOtherSymbol: unique symbol;
|
|
7
8
|
declare const validateDebugFilter: unique symbol;
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*
|
|
12
|
+
* Package-private accessor key for sibling modules in
|
|
13
|
+
* `packages/machine/src` (e.g. `utilities/stateGraph.ts`, and the planned
|
|
14
|
+
* `utilities/stateCollect.ts` for #195). Re-exported from this module so
|
|
15
|
+
* sibling files can import it; intentionally NOT re-exported from the
|
|
16
|
+
* package's public `index.ts`, so downstream consumers don't see it on
|
|
17
|
+
* the supported surface.
|
|
18
|
+
*
|
|
19
|
+
* Calling `state[STATE_INTERNAL]()` returns a getter/setter view onto the
|
|
20
|
+
* State's private fields. Reads are live (they close over `this`), so the
|
|
21
|
+
* view stays in sync with subsequent mutations on the State. There's one
|
|
22
|
+
* mutating setter on the view — `name` — used exclusively by
|
|
23
|
+
* `fromGraph` to assign graph-sourced composite names (e.g. `A(target)`)
|
|
24
|
+
* that the public name validator would reject; see the JSDoc on the
|
|
25
|
+
* accessor itself.
|
|
26
|
+
*
|
|
27
|
+
* Designed in #180 with #195 in mind so its surface doesn't need to grow
|
|
28
|
+
* when `collectStates` lands.
|
|
29
|
+
*/
|
|
30
|
+
export declare const STATE_INTERNAL: unique symbol;
|
|
8
31
|
export declare class DebugConfig {
|
|
9
32
|
#private;
|
|
10
33
|
constructor(ownerState: State, initial?: {
|
|
@@ -32,12 +55,65 @@ export default class State {
|
|
|
32
55
|
before?: symbol[] | readonly symbol[] | true;
|
|
33
56
|
after?: symbol[] | readonly symbol[] | true;
|
|
34
57
|
} | null);
|
|
58
|
+
/**
|
|
59
|
+
* Add one or more tags to this State (#186). Tags are out-of-band metadata
|
|
60
|
+
* used by visualization (`toMermaid` emits `classDef`/`class` lines) and
|
|
61
|
+
* debugger tooling — they don't affect runtime transition lookup,
|
|
62
|
+
* `equivalentOn` comparisons, or any structural identity. Chainable.
|
|
63
|
+
*/
|
|
64
|
+
tag(...tags: string[]): this;
|
|
65
|
+
/**
|
|
66
|
+
* Remove one or more tags from this State (#186). Untagging a tag the
|
|
67
|
+
* State doesn't carry is a no-op. Chainable.
|
|
68
|
+
*/
|
|
69
|
+
untag(...tags: string[]): this;
|
|
70
|
+
/**
|
|
71
|
+
* Frozen snapshot of this State's current tags (#186). The returned array
|
|
72
|
+
* is `Object.freeze`d — mutating it throws in strict mode (which TS-emitted
|
|
73
|
+
* code uses). Order matches insertion order of the underlying Set.
|
|
74
|
+
*/
|
|
75
|
+
get tags(): readonly string[];
|
|
35
76
|
/** @internal — invoked by DebugConfig setters via module-private symbol. */
|
|
36
77
|
[validateDebugFilter](fieldName: 'before' | 'after', filter: readonly symbol[] | true | undefined): void;
|
|
37
78
|
getSymbol(tapeBlock: TapeBlock): symbol;
|
|
38
79
|
getCommand(symbol: symbol): Command;
|
|
39
80
|
getNextState(symbol: symbol): State | Reference;
|
|
40
81
|
withOverriddenHaltState(overriddenHaltState: State): State;
|
|
82
|
+
/**
|
|
83
|
+
* @internal
|
|
84
|
+
*
|
|
85
|
+
* Package-private getter/setter view onto this State's private fields,
|
|
86
|
+
* for sibling modules in `packages/machine/src` (currently `stateGraph.ts`
|
|
87
|
+
* for `toGraph` / `fromGraph`, and the planned `stateCollect.ts` for
|
|
88
|
+
* #195's `collectStates`).
|
|
89
|
+
*
|
|
90
|
+
* Read access is live — the getters close over `this`, so the view
|
|
91
|
+
* stays in sync with subsequent mutations on this State. There's a
|
|
92
|
+
* single mutating setter on the view, `name`, which exists to let
|
|
93
|
+
* `fromGraph` assign graph-sourced composite names (e.g. `A(target)`)
|
|
94
|
+
* to freshly-constructed bare States. The constructor's name validator
|
|
95
|
+
* rejects parens (reserved as wrapper-composition delimiters in
|
|
96
|
+
* `withOverriddenHaltState`); the setter intentionally bypasses that
|
|
97
|
+
* check because the same delimiters appear in legitimate wrapper-bare
|
|
98
|
+
* names round-tripped through the graph.
|
|
99
|
+
*
|
|
100
|
+
* Returns a fresh view object on every call — cheap enough for the
|
|
101
|
+
* BFS-once-per-build callers, and avoids holding a reference object on
|
|
102
|
+
* every State instance. Keep this surface tight: callers should only
|
|
103
|
+
* read what they need. Adding fields here is a deliberate decision —
|
|
104
|
+
* each adds to the implicit contract sibling modules can rely on.
|
|
105
|
+
*/
|
|
106
|
+
[STATE_INTERNAL](): {
|
|
107
|
+
readonly id: number;
|
|
108
|
+
name: string;
|
|
109
|
+
readonly bareState: State | null;
|
|
110
|
+
readonly overriddenHaltState: State | null;
|
|
111
|
+
readonly symbolToDataMap: Map<symbol, {
|
|
112
|
+
command: Command;
|
|
113
|
+
nextState: State | Reference;
|
|
114
|
+
}>;
|
|
115
|
+
readonly tags: ReadonlySet<string>;
|
|
116
|
+
};
|
|
41
117
|
static inspect(state: State): {
|
|
42
118
|
id: number;
|
|
43
119
|
name: string;
|
|
@@ -58,12 +134,35 @@ export default class State {
|
|
|
58
134
|
} | null;
|
|
59
135
|
}>;
|
|
60
136
|
};
|
|
137
|
+
/**
|
|
138
|
+
* Walks the reachable State graph from `initialState` and returns a
|
|
139
|
+
* serializable `Graph`. Thin delegate to `utilities/stateGraph.ts`'s
|
|
140
|
+
* `toGraph` (extracted in #180); see that module for the BFS shape and
|
|
141
|
+
* v7 callable-subtree emit semantics.
|
|
142
|
+
*/
|
|
61
143
|
static toGraph(initialState: State, tapeBlock: TapeBlock): Graph;
|
|
144
|
+
/**
|
|
145
|
+
* Inverse of `toGraph`: rebuilds a State graph and a fresh TapeBlock
|
|
146
|
+
* from a serialized `Graph`. Thin delegate to `utilities/stateGraph.ts`'s
|
|
147
|
+
* `fromGraph` (extracted in #180); see that module for the
|
|
148
|
+
* reconstruction pass shape (Reference pre-create, bare build, wrapper
|
|
149
|
+
* resolution via `withOverriddenHaltState`, ref binding).
|
|
150
|
+
*/
|
|
62
151
|
static fromGraph(graph: Graph): {
|
|
63
152
|
start: State;
|
|
64
153
|
tapeBlock: TapeBlock;
|
|
65
154
|
states: Record<number, State>;
|
|
66
155
|
};
|
|
156
|
+
/**
|
|
157
|
+
* Returns a `Map<number, {state, transitionSymbols}>` keyed by engine
|
|
158
|
+
* `GraphNode.id`, exposing the live `State` instance + per-pattern
|
|
159
|
+
* Symbol references for each node so downstream tooling can mutate
|
|
160
|
+
* `state.debug` by numeric id and set per-pattern breakpoints by
|
|
161
|
+
* `GraphTransition.id` (#195). Thin delegate to
|
|
162
|
+
* `utilities/stateGraph.ts`'s `collectStates`; see that module for
|
|
163
|
+
* the alignment contract, coverage rules, and halt-singleton warning.
|
|
164
|
+
*/
|
|
165
|
+
static collectStates(initialState: State, tapeBlock: TapeBlock): StateMap;
|
|
67
166
|
}
|
|
68
167
|
export declare const haltState: State;
|
|
69
168
|
export {};
|