@turing-machine-js/visuals 7.0.0-alpha.6 → 7.0.0-alpha.6.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/CHANGELOG.md +15 -0
- package/dist/format.d.ts +115 -0
- package/dist/index.cjs +100 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +98 -1
- package/package.json +2 -2
- package/dist/applyHighlight.js +0 -191
- package/dist/format.js +0 -46
- package/dist/graphIndexes.js +0 -58
- package/dist/graphUtils.js +0 -76
- package/dist/highlightOps.js +0 -36
- package/dist/index.js +0 -6
- package/dist/recordSnippet.js +0 -92
- package/dist/types.js +0 -1
- package/src/applyHighlight.spec.ts +0 -331
- package/src/applyHighlight.ts +0 -217
- package/src/fixtures/graphs/post-walk-mark.json +0 -108
- package/src/fixtures/graphs/turing-callable-subtree.json +0 -108
- package/src/fixtures/graphs/turing-copy-two-tapes.json +0 -87
- package/src/fixtures/graphs/turing-replace-b.json +0 -72
- package/src/format.spec.ts +0 -100
- package/src/format.ts +0 -51
- package/src/graphIndexes.ts +0 -84
- package/src/graphUtils.spec.ts +0 -112
- package/src/graphUtils.ts +0 -74
- package/src/highlightOps.ts +0 -94
- package/src/index.ts +0 -10
- package/src/recordSnippet.spec.ts +0 -275
- package/src/recordSnippet.ts +0 -141
- package/src/types.ts +0 -96
- package/tsconfig.build.json +0 -11
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -10
package/src/applyHighlight.ts
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import type { GraphHighlight } from './types';
|
|
2
|
-
import type { Graph } from '@turing-machine-js/machine';
|
|
3
|
-
import type { GraphIndexes } from './graphIndexes';
|
|
4
|
-
import type { HighlightOps, IndicatorOps, NodeKey } from './highlightOps';
|
|
5
|
-
import { bareIdOf, highlightExpand } from './graphUtils';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Pure highlight-rule evaluator. Given the current `highlight` (from
|
|
9
|
-
* `MachineView`'s `$derived`), the engine `graph`, derived `indexes`,
|
|
10
|
-
* and the previous strong-id (for pause-revisit pulse detection), emit
|
|
11
|
-
* a sequence of `ops` calls describing the resulting visual state.
|
|
12
|
-
*
|
|
13
|
-
* Strictly additive — the caller is expected to clear previously-applied
|
|
14
|
-
* highlight classes / edge marks / cluster activations BEFORE invoking
|
|
15
|
-
* this function. The function never reads back from the consumer.
|
|
16
|
-
*
|
|
17
|
-
* Returns the new prev-strong-id to thread into the next call. Pulse
|
|
18
|
-
* comparison uses the RAW strong id (not canonical), so wrapper-pause
|
|
19
|
-
* and bare-pause register as different positions and don't pulse each
|
|
20
|
-
* other. Updates only when `highlight.paused === true`; non-paused
|
|
21
|
-
* events (idle / RUNNING_AUTO ticks) leave it untouched. Null highlight
|
|
22
|
-
* resets it to null.
|
|
23
|
-
*
|
|
24
|
-
* See `docs/graph-highlight-and-breakpoints.md` for the 16 rules
|
|
25
|
-
* enumerated.
|
|
26
|
-
*/
|
|
27
|
-
export function applyHighlight(
|
|
28
|
-
highlight: GraphHighlight | null,
|
|
29
|
-
graph: Graph | null,
|
|
30
|
-
indexes: GraphIndexes,
|
|
31
|
-
prevStrongId: NodeKey | null,
|
|
32
|
-
ops: HighlightOps,
|
|
33
|
-
): { nextPrevStrongId: NodeKey | null } {
|
|
34
|
-
if (!highlight || !graph) {
|
|
35
|
-
return { nextPrevStrongId: null };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// §5 Halt-target retargeting: real halt (id 0) reached from an in-frame
|
|
39
|
-
// state retargets to the frame's halt marker (id = -frameId), so the
|
|
40
|
-
// visible edge lands inside the cluster.
|
|
41
|
-
let toId: number | null = highlight.toId;
|
|
42
|
-
if (toId === 0 && typeof highlight.fromId === 'number') {
|
|
43
|
-
const fromFrameId = indexes.nodeFrameMap.get(highlight.fromId);
|
|
44
|
-
if (fromFrameId !== undefined) toId = -fromFrameId;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// §2 Equivalence-class expansion (asymmetric, via highlightExpand):
|
|
48
|
-
// wrapper → [wrapper, bare] (joined visual pair for wrapper-entry pause)
|
|
49
|
-
// bare → [bare] (engine genuinely on the bare; no wrapper sync)
|
|
50
|
-
// From-side expansion only fires for positive numeric ids; the 'idle'
|
|
51
|
-
// sentinel is handled directly below. Halt markers / singleton fall
|
|
52
|
-
// through the direct-lookup branches.
|
|
53
|
-
const fromEqIds = typeof highlight.fromId === 'number'
|
|
54
|
-
? highlightExpand(highlight.fromId, graph)
|
|
55
|
-
: [];
|
|
56
|
-
const toEqIds = toId !== null && toId > 0
|
|
57
|
-
? highlightExpand(toId, graph)
|
|
58
|
-
: [];
|
|
59
|
-
|
|
60
|
-
// §3 Class application — from side.
|
|
61
|
-
if (highlight.fromId === 'idle') {
|
|
62
|
-
ops.addNodeClass('idle', 'mg-highlight-from');
|
|
63
|
-
if (highlight.strong === 'from') ops.addNodeClass('idle', 'mg-highlight-strong');
|
|
64
|
-
}
|
|
65
|
-
for (const id of fromEqIds) {
|
|
66
|
-
ops.addNodeClass(id, 'mg-highlight-from');
|
|
67
|
-
if (highlight.strong === 'from') ops.addNodeClass(id, 'mg-highlight-strong');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// §3 + §8 Class application — to side. Halt markers (toId < 0) and the
|
|
71
|
-
// real halt singleton (toId === 0; only possible when §5 didn't retarget)
|
|
72
|
-
// bypass the equivalence-class expansion via direct lookup.
|
|
73
|
-
if (toId !== null && toId <= 0) {
|
|
74
|
-
ops.addNodeClass(toId, 'mg-highlight-to');
|
|
75
|
-
if (highlight.strong === 'to') ops.addNodeClass(toId, 'mg-highlight-strong');
|
|
76
|
-
}
|
|
77
|
-
for (const id of toEqIds) {
|
|
78
|
-
ops.addNodeClass(id, 'mg-highlight-to');
|
|
79
|
-
if (highlight.strong === 'to') ops.addNodeClass(id, 'mg-highlight-strong');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Edge highlight: the data-id token form mermaid emits.
|
|
83
|
-
const fromKey = highlight.fromId === 'idle' ? 'idle' : `s${highlight.fromId}`;
|
|
84
|
-
const toKey =
|
|
85
|
-
toId === null ? null
|
|
86
|
-
: toId < 0 ? `c${-toId}` // halt marker
|
|
87
|
-
: `s${toId}`;
|
|
88
|
-
if (toKey !== null) ops.highlightEdge(fromKey, toKey);
|
|
89
|
-
|
|
90
|
-
// §10 Wrapper-entry "call" edge: when to-side expanded to [wrapper, bare],
|
|
91
|
-
// light up the wrapper→bare connector so the joined pair has a visible link.
|
|
92
|
-
if (toEqIds.length > 1) {
|
|
93
|
-
const wrapperId = toEqIds.find((id) => graph.nodes[id]?.isWrapper);
|
|
94
|
-
const bareId = toEqIds.find((id) => !graph.nodes[id]?.isWrapper);
|
|
95
|
-
if (wrapperId !== undefined && bareId !== undefined) {
|
|
96
|
-
ops.highlightEdge(`s${wrapperId}`, `s${bareId}`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// §6 Source return chain: just-fired transition landed on a frame's
|
|
101
|
-
// halt marker. Light up the post-pop trajectory before the next iter
|
|
102
|
-
// moves the strong node.
|
|
103
|
-
if (toId !== null && toId < 0) {
|
|
104
|
-
const frameId = -toId;
|
|
105
|
-
const wrappers = indexes.frameWrappersMap.get(frameId) ?? [];
|
|
106
|
-
for (const { wrapperId, overrideId } of wrappers) {
|
|
107
|
-
ops.highlightEdge(`w_${frameId}`, `s${wrapperId}`);
|
|
108
|
-
ops.addNodeClass(wrapperId, 'mg-highlight-to');
|
|
109
|
-
if (overrideId !== null) {
|
|
110
|
-
ops.highlightEdge(`s${wrapperId}`, `s${overrideId}`);
|
|
111
|
-
ops.addNodeClass(overrideId, 'mg-highlight-to');
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// §7 Destination return chain: paused at a positive toId that's some
|
|
117
|
-
// wrapper W's override AND fromId is in W's frame — the engine just
|
|
118
|
-
// popped. The straight bare→override edge doesn't exist in the graph;
|
|
119
|
-
// light up the actual visible path bare → halt-marker → return →
|
|
120
|
-
// wrapper → override, plus the frame cluster.
|
|
121
|
-
if (typeof highlight.fromId === 'number' && toId !== null && toId > 0) {
|
|
122
|
-
const fromFrameId = indexes.nodeFrameMap.get(highlight.fromId);
|
|
123
|
-
if (fromFrameId !== undefined) {
|
|
124
|
-
const wrappers = indexes.frameWrappersMap.get(fromFrameId) ?? [];
|
|
125
|
-
const matching = wrappers.filter((w) => w.overrideId === toId);
|
|
126
|
-
if (matching.length > 0) {
|
|
127
|
-
ops.addNodeClass(-fromFrameId, 'mg-highlight-to');
|
|
128
|
-
ops.highlightEdge(`s${highlight.fromId}`, `c${fromFrameId}`);
|
|
129
|
-
for (const { wrapperId } of matching) {
|
|
130
|
-
ops.highlightEdge(`w_${fromFrameId}`, `s${wrapperId}`);
|
|
131
|
-
ops.addNodeClass(wrapperId, 'mg-highlight-to');
|
|
132
|
-
ops.highlightEdge(`s${wrapperId}`, `s${toId}`);
|
|
133
|
-
}
|
|
134
|
-
ops.markFrameActive(fromFrameId);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// §9 Frame-active for the strong node. Wrappers are outside any frame
|
|
140
|
-
// so canonicalize via bareIdOf so the wrapper-entry pause still lights
|
|
141
|
-
// up the bare's enclosing cluster.
|
|
142
|
-
const strongId = highlight.strong === 'from' ? highlight.fromId : highlight.toId;
|
|
143
|
-
const strongIdCanonical = typeof strongId === 'number'
|
|
144
|
-
? bareIdOf(strongId, graph)
|
|
145
|
-
: strongId;
|
|
146
|
-
if (typeof strongIdCanonical === 'number') {
|
|
147
|
-
const frameId = indexes.nodeFrameMap.get(strongIdCanonical);
|
|
148
|
-
if (frameId !== undefined) ops.markFrameActive(frameId);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// §11 Pulse on same-state revisit. Uses RAW strongId — wrapper-pause
|
|
152
|
-
// and bare-pause are visually distinct positions even though they
|
|
153
|
-
// share #debugRef; pausing at wrapper then continuing into bare must
|
|
154
|
-
// not pulse. Idles never pulse and never update prevStrongId.
|
|
155
|
-
if (
|
|
156
|
-
highlight.paused
|
|
157
|
-
&& strongId !== null
|
|
158
|
-
&& strongId === prevStrongId
|
|
159
|
-
&& strongId !== undefined
|
|
160
|
-
) {
|
|
161
|
-
ops.pulse(strongId);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Scroll-into-view target: for wrapper-entry pauses, scroll to the
|
|
165
|
-
// BARE (not the wrapper) so the focus matches the displayed state
|
|
166
|
-
// name. The worker's `resolveDisplayName` returns the bare's name
|
|
167
|
-
// for wrapper iters (so the log reads "paused at walkToBlank ..."),
|
|
168
|
-
// but `toId` is the wrapper's id and `highlightExpand` lights up
|
|
169
|
-
// both nodes as strong. Without this canonicalization the scroll
|
|
170
|
-
// lands on the wrapper while the log line and user's mental focus
|
|
171
|
-
// are on the bare. Halt-related ids (≤ 0) are scrolled to as-is —
|
|
172
|
-
// `bareIdOf` would collapse them all to the halt singleton, which
|
|
173
|
-
// is structurally separate from the in-frame halt marker the user
|
|
174
|
-
// is paused near.
|
|
175
|
-
if (strongId !== null) {
|
|
176
|
-
let scrollTarget: NodeKey = strongId;
|
|
177
|
-
if (typeof strongId === 'number' && strongId > 0) {
|
|
178
|
-
const node = graph.nodes[strongId];
|
|
179
|
-
if (node?.isWrapper && node.bareStateId !== null) {
|
|
180
|
-
scrollTarget = node.bareStateId;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
ops.scrollIntoView(scrollTarget);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const nextPrevStrongId = highlight.paused ? strongId : prevStrongId;
|
|
187
|
-
return { nextPrevStrongId };
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Pure breakpoint-indicator rule evaluator. For each cached node key,
|
|
192
|
-
* emit `ops.setBreakpoint(key, on)` reflecting whether the node's
|
|
193
|
-
* canonical bare-id is in the `breakpoints` set.
|
|
194
|
-
*
|
|
195
|
-
* The 'idle' string sentinel never carries a breakpoint. All numeric
|
|
196
|
-
* keys are valid BP-class members:
|
|
197
|
-
* - positive id → regular state; canonical via bareIdOf (wrappers
|
|
198
|
-
* collapse to bare)
|
|
199
|
-
* - 0 → haltState singleton (engine-wide; canonical = 0)
|
|
200
|
-
* - negative id → halt marker (per-frame visualization sentinel;
|
|
201
|
-
* bareIdOf maps to 0 — same class as the singleton)
|
|
202
|
-
* Consumers pass their iterable of cached node keys (e.g. `nodeCache.keys()`).
|
|
203
|
-
*/
|
|
204
|
-
export function applyIndicator(
|
|
205
|
-
breakpoints: ReadonlySet<number>,
|
|
206
|
-
graph: Graph | null,
|
|
207
|
-
nodeIds: Iterable<NodeKey>,
|
|
208
|
-
ops: IndicatorOps,
|
|
209
|
-
): void {
|
|
210
|
-
for (const key of nodeIds) {
|
|
211
|
-
const on =
|
|
212
|
-
typeof key === 'number'
|
|
213
|
-
&& graph !== null
|
|
214
|
-
&& breakpoints.has(bareIdOf(key, graph));
|
|
215
|
-
ops.setBreakpoint(key, on);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"initialId": 6,
|
|
3
|
-
"alphabets": [
|
|
4
|
-
[
|
|
5
|
-
"␣",
|
|
6
|
-
"•"
|
|
7
|
-
]
|
|
8
|
-
],
|
|
9
|
-
"nodes": {
|
|
10
|
-
"0": {
|
|
11
|
-
"id": 0,
|
|
12
|
-
"name": "id:0",
|
|
13
|
-
"isHalt": true,
|
|
14
|
-
"isHaltMarker": false,
|
|
15
|
-
"isWrapper": false,
|
|
16
|
-
"bareStateId": null,
|
|
17
|
-
"frameId": null,
|
|
18
|
-
"transitions": [],
|
|
19
|
-
"overriddenHaltStateId": null,
|
|
20
|
-
"tags": []
|
|
21
|
-
},
|
|
22
|
-
"6": {
|
|
23
|
-
"id": 6,
|
|
24
|
-
"name": "10",
|
|
25
|
-
"isHalt": false,
|
|
26
|
-
"isHaltMarker": false,
|
|
27
|
-
"isWrapper": false,
|
|
28
|
-
"bareStateId": null,
|
|
29
|
-
"frameId": null,
|
|
30
|
-
"transitions": [
|
|
31
|
-
{
|
|
32
|
-
"pattern": "'•'",
|
|
33
|
-
"command": [
|
|
34
|
-
{
|
|
35
|
-
"symbol": "K",
|
|
36
|
-
"movement": "S"
|
|
37
|
-
}
|
|
38
|
-
],
|
|
39
|
-
"nextStateId": 7,
|
|
40
|
-
"id": "6.0"
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"pattern": "B",
|
|
44
|
-
"command": [
|
|
45
|
-
{
|
|
46
|
-
"symbol": "K",
|
|
47
|
-
"movement": "S"
|
|
48
|
-
}
|
|
49
|
-
],
|
|
50
|
-
"nextStateId": 8,
|
|
51
|
-
"id": "6.1"
|
|
52
|
-
}
|
|
53
|
-
],
|
|
54
|
-
"overriddenHaltStateId": null,
|
|
55
|
-
"tags": [
|
|
56
|
-
"main"
|
|
57
|
-
]
|
|
58
|
-
},
|
|
59
|
-
"7": {
|
|
60
|
-
"id": 7,
|
|
61
|
-
"name": "20",
|
|
62
|
-
"isHalt": false,
|
|
63
|
-
"isHaltMarker": false,
|
|
64
|
-
"isWrapper": false,
|
|
65
|
-
"bareStateId": null,
|
|
66
|
-
"frameId": null,
|
|
67
|
-
"transitions": [
|
|
68
|
-
{
|
|
69
|
-
"pattern": "*",
|
|
70
|
-
"command": [
|
|
71
|
-
{
|
|
72
|
-
"symbol": "K",
|
|
73
|
-
"movement": "R"
|
|
74
|
-
}
|
|
75
|
-
],
|
|
76
|
-
"nextStateId": 6,
|
|
77
|
-
"id": "7.0"
|
|
78
|
-
}
|
|
79
|
-
],
|
|
80
|
-
"overriddenHaltStateId": null,
|
|
81
|
-
"tags": []
|
|
82
|
-
},
|
|
83
|
-
"8": {
|
|
84
|
-
"id": 8,
|
|
85
|
-
"name": "30",
|
|
86
|
-
"isHalt": false,
|
|
87
|
-
"isHaltMarker": false,
|
|
88
|
-
"isWrapper": false,
|
|
89
|
-
"bareStateId": null,
|
|
90
|
-
"frameId": null,
|
|
91
|
-
"transitions": [
|
|
92
|
-
{
|
|
93
|
-
"pattern": "*",
|
|
94
|
-
"command": [
|
|
95
|
-
{
|
|
96
|
-
"symbol": "'•'",
|
|
97
|
-
"movement": "S"
|
|
98
|
-
}
|
|
99
|
-
],
|
|
100
|
-
"nextStateId": 0,
|
|
101
|
-
"id": "8.0"
|
|
102
|
-
}
|
|
103
|
-
],
|
|
104
|
-
"overriddenHaltStateId": null,
|
|
105
|
-
"tags": []
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"initialId": 5,
|
|
3
|
-
"alphabets": [
|
|
4
|
-
[
|
|
5
|
-
" ",
|
|
6
|
-
"a",
|
|
7
|
-
"b",
|
|
8
|
-
"*"
|
|
9
|
-
]
|
|
10
|
-
],
|
|
11
|
-
"nodes": {
|
|
12
|
-
"0": {
|
|
13
|
-
"id": 0,
|
|
14
|
-
"name": "id:0",
|
|
15
|
-
"isHalt": true,
|
|
16
|
-
"isHaltMarker": false,
|
|
17
|
-
"isWrapper": false,
|
|
18
|
-
"bareStateId": null,
|
|
19
|
-
"frameId": null,
|
|
20
|
-
"transitions": [],
|
|
21
|
-
"overriddenHaltStateId": null,
|
|
22
|
-
"tags": []
|
|
23
|
-
},
|
|
24
|
-
"3": {
|
|
25
|
-
"id": 3,
|
|
26
|
-
"name": "walkToBlank",
|
|
27
|
-
"isHalt": false,
|
|
28
|
-
"isHaltMarker": false,
|
|
29
|
-
"isWrapper": false,
|
|
30
|
-
"bareStateId": null,
|
|
31
|
-
"frameId": 3,
|
|
32
|
-
"transitions": [
|
|
33
|
-
{
|
|
34
|
-
"pattern": "B",
|
|
35
|
-
"command": [
|
|
36
|
-
{
|
|
37
|
-
"symbol": "K",
|
|
38
|
-
"movement": "S"
|
|
39
|
-
}
|
|
40
|
-
],
|
|
41
|
-
"nextStateId": -3,
|
|
42
|
-
"id": "3.0"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"pattern": "*",
|
|
46
|
-
"command": [
|
|
47
|
-
{
|
|
48
|
-
"symbol": "K",
|
|
49
|
-
"movement": "R"
|
|
50
|
-
}
|
|
51
|
-
],
|
|
52
|
-
"nextStateId": 3,
|
|
53
|
-
"id": "3.1"
|
|
54
|
-
}
|
|
55
|
-
],
|
|
56
|
-
"overriddenHaltStateId": null,
|
|
57
|
-
"tags": []
|
|
58
|
-
},
|
|
59
|
-
"4": {
|
|
60
|
-
"id": 4,
|
|
61
|
-
"name": "writeMarker",
|
|
62
|
-
"isHalt": false,
|
|
63
|
-
"isHaltMarker": false,
|
|
64
|
-
"isWrapper": false,
|
|
65
|
-
"bareStateId": null,
|
|
66
|
-
"frameId": null,
|
|
67
|
-
"transitions": [
|
|
68
|
-
{
|
|
69
|
-
"pattern": "*",
|
|
70
|
-
"command": [
|
|
71
|
-
{
|
|
72
|
-
"symbol": "'*'",
|
|
73
|
-
"movement": "S"
|
|
74
|
-
}
|
|
75
|
-
],
|
|
76
|
-
"nextStateId": 0,
|
|
77
|
-
"id": "4.0"
|
|
78
|
-
}
|
|
79
|
-
],
|
|
80
|
-
"overriddenHaltStateId": null,
|
|
81
|
-
"tags": []
|
|
82
|
-
},
|
|
83
|
-
"5": {
|
|
84
|
-
"id": 5,
|
|
85
|
-
"name": "walkToBlank(writeMarker)",
|
|
86
|
-
"isHalt": false,
|
|
87
|
-
"isHaltMarker": false,
|
|
88
|
-
"isWrapper": true,
|
|
89
|
-
"bareStateId": 3,
|
|
90
|
-
"frameId": null,
|
|
91
|
-
"transitions": [],
|
|
92
|
-
"overriddenHaltStateId": 4,
|
|
93
|
-
"tags": []
|
|
94
|
-
},
|
|
95
|
-
"-3": {
|
|
96
|
-
"id": -3,
|
|
97
|
-
"name": "halt",
|
|
98
|
-
"isHalt": true,
|
|
99
|
-
"isHaltMarker": true,
|
|
100
|
-
"isWrapper": false,
|
|
101
|
-
"bareStateId": null,
|
|
102
|
-
"frameId": 3,
|
|
103
|
-
"transitions": [],
|
|
104
|
-
"overriddenHaltStateId": null,
|
|
105
|
-
"tags": []
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"initialId": 2,
|
|
3
|
-
"alphabets": [
|
|
4
|
-
[
|
|
5
|
-
"␣",
|
|
6
|
-
"a",
|
|
7
|
-
"b"
|
|
8
|
-
],
|
|
9
|
-
[
|
|
10
|
-
"␣",
|
|
11
|
-
"a",
|
|
12
|
-
"b"
|
|
13
|
-
]
|
|
14
|
-
],
|
|
15
|
-
"nodes": {
|
|
16
|
-
"0": {
|
|
17
|
-
"id": 0,
|
|
18
|
-
"name": "id:0",
|
|
19
|
-
"isHalt": true,
|
|
20
|
-
"isHaltMarker": false,
|
|
21
|
-
"isWrapper": false,
|
|
22
|
-
"bareStateId": null,
|
|
23
|
-
"frameId": null,
|
|
24
|
-
"transitions": [],
|
|
25
|
-
"overriddenHaltStateId": null,
|
|
26
|
-
"tags": []
|
|
27
|
-
},
|
|
28
|
-
"2": {
|
|
29
|
-
"id": 2,
|
|
30
|
-
"name": "id:2",
|
|
31
|
-
"isHalt": false,
|
|
32
|
-
"isHaltMarker": false,
|
|
33
|
-
"isWrapper": false,
|
|
34
|
-
"bareStateId": null,
|
|
35
|
-
"frameId": null,
|
|
36
|
-
"transitions": [
|
|
37
|
-
{
|
|
38
|
-
"pattern": "'a',*",
|
|
39
|
-
"command": [
|
|
40
|
-
{
|
|
41
|
-
"symbol": "'a'",
|
|
42
|
-
"movement": "R"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"symbol": "'a'",
|
|
46
|
-
"movement": "R"
|
|
47
|
-
}
|
|
48
|
-
],
|
|
49
|
-
"nextStateId": 2,
|
|
50
|
-
"id": "2.0"
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
"pattern": "'b',*",
|
|
54
|
-
"command": [
|
|
55
|
-
{
|
|
56
|
-
"symbol": "'b'",
|
|
57
|
-
"movement": "R"
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"symbol": "'b'",
|
|
61
|
-
"movement": "R"
|
|
62
|
-
}
|
|
63
|
-
],
|
|
64
|
-
"nextStateId": 2,
|
|
65
|
-
"id": "2.1"
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
"pattern": "B,*",
|
|
69
|
-
"command": [
|
|
70
|
-
{
|
|
71
|
-
"symbol": "K",
|
|
72
|
-
"movement": "S"
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"symbol": "K",
|
|
76
|
-
"movement": "S"
|
|
77
|
-
}
|
|
78
|
-
],
|
|
79
|
-
"nextStateId": 0,
|
|
80
|
-
"id": "2.2"
|
|
81
|
-
}
|
|
82
|
-
],
|
|
83
|
-
"overriddenHaltStateId": null,
|
|
84
|
-
"tags": []
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"initialId": 1,
|
|
3
|
-
"alphabets": [
|
|
4
|
-
[
|
|
5
|
-
"␣",
|
|
6
|
-
"a",
|
|
7
|
-
"b",
|
|
8
|
-
"c",
|
|
9
|
-
"*"
|
|
10
|
-
]
|
|
11
|
-
],
|
|
12
|
-
"nodes": {
|
|
13
|
-
"0": {
|
|
14
|
-
"id": 0,
|
|
15
|
-
"name": "id:0",
|
|
16
|
-
"isHalt": true,
|
|
17
|
-
"isHaltMarker": false,
|
|
18
|
-
"isWrapper": false,
|
|
19
|
-
"bareStateId": null,
|
|
20
|
-
"frameId": null,
|
|
21
|
-
"transitions": [],
|
|
22
|
-
"overriddenHaltStateId": null,
|
|
23
|
-
"tags": []
|
|
24
|
-
},
|
|
25
|
-
"1": {
|
|
26
|
-
"id": 1,
|
|
27
|
-
"name": "id:1",
|
|
28
|
-
"isHalt": false,
|
|
29
|
-
"isHaltMarker": false,
|
|
30
|
-
"isWrapper": false,
|
|
31
|
-
"bareStateId": null,
|
|
32
|
-
"frameId": null,
|
|
33
|
-
"transitions": [
|
|
34
|
-
{
|
|
35
|
-
"pattern": "'b'",
|
|
36
|
-
"command": [
|
|
37
|
-
{
|
|
38
|
-
"symbol": "'*'",
|
|
39
|
-
"movement": "R"
|
|
40
|
-
}
|
|
41
|
-
],
|
|
42
|
-
"nextStateId": 1,
|
|
43
|
-
"id": "1.0"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"pattern": "B",
|
|
47
|
-
"command": [
|
|
48
|
-
{
|
|
49
|
-
"symbol": "K",
|
|
50
|
-
"movement": "L"
|
|
51
|
-
}
|
|
52
|
-
],
|
|
53
|
-
"nextStateId": 0,
|
|
54
|
-
"id": "1.1"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"pattern": "*",
|
|
58
|
-
"command": [
|
|
59
|
-
{
|
|
60
|
-
"symbol": "K",
|
|
61
|
-
"movement": "R"
|
|
62
|
-
}
|
|
63
|
-
],
|
|
64
|
-
"nextStateId": 1,
|
|
65
|
-
"id": "1.2"
|
|
66
|
-
}
|
|
67
|
-
],
|
|
68
|
-
"overriddenHaltStateId": null,
|
|
69
|
-
"tags": []
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
package/src/format.spec.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
Alphabet,
|
|
4
|
-
Tape,
|
|
5
|
-
TapeBlock,
|
|
6
|
-
TapeCommand,
|
|
7
|
-
TuringMachine,
|
|
8
|
-
State,
|
|
9
|
-
haltState,
|
|
10
|
-
movements,
|
|
11
|
-
symbolCommands,
|
|
12
|
-
} from '@turing-machine-js/machine';
|
|
13
|
-
import { formatCommand, formatStep } from './format';
|
|
14
|
-
|
|
15
|
-
describe('formatCommand', () => {
|
|
16
|
-
it('formats a literal symbol write + right move', () => {
|
|
17
|
-
const tc = new TapeCommand({ symbol: 'X', movement: movements.right });
|
|
18
|
-
expect(formatCommand(tc)).toBe("'X'/R");
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('formats keep + stay as K/S', () => {
|
|
22
|
-
const tc = new TapeCommand({ movement: movements.stay });
|
|
23
|
-
// default symbol is symbolCommands.keep
|
|
24
|
-
expect(formatCommand(tc)).toBe('K/S');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('formats erase + left as E/L', () => {
|
|
28
|
-
const tc = new TapeCommand({ symbol: symbolCommands.erase, movement: movements.left });
|
|
29
|
-
expect(formatCommand(tc)).toBe('E/L');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('formats keep + right as K/R', () => {
|
|
33
|
-
const tc = new TapeCommand({ symbol: symbolCommands.keep, movement: movements.right });
|
|
34
|
-
expect(formatCommand(tc)).toBe('K/R');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('formats a literal symbol write + left move', () => {
|
|
38
|
-
const tc = new TapeCommand({ symbol: 'a', movement: movements.left });
|
|
39
|
-
expect(formatCommand(tc)).toBe("'a'/L");
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('formatStep', () => {
|
|
44
|
-
it("formats a single-tape iter: 'a' → 'b'/R", () => {
|
|
45
|
-
const alphabet = new Alphabet([' ', 'a', 'b']);
|
|
46
|
-
const tape = new Tape({ alphabet, symbols: ['a'] });
|
|
47
|
-
const tapeBlock = TapeBlock.fromTapes([tape]);
|
|
48
|
-
const machine = new TuringMachine({ tapeBlock });
|
|
49
|
-
const initialState = new State({
|
|
50
|
-
[tapeBlock.symbol(['a'])]: {
|
|
51
|
-
command: [{ symbol: 'b', movement: movements.right }],
|
|
52
|
-
nextState: haltState,
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
const gen = machine.runStepByStep({ initialState });
|
|
56
|
-
const m = gen.next().value!;
|
|
57
|
-
|
|
58
|
-
expect(formatStep(m)).toBe("['a'] → ['b']/[R]");
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('encodes keep as K when nextSymbol equals currentSymbol', () => {
|
|
62
|
-
const alphabet = new Alphabet([' ', 'a', 'b']);
|
|
63
|
-
const tape = new Tape({ alphabet, symbols: ['a'] });
|
|
64
|
-
const tapeBlock = TapeBlock.fromTapes([tape]);
|
|
65
|
-
const machine = new TuringMachine({ tapeBlock });
|
|
66
|
-
const initialState = new State({
|
|
67
|
-
[tapeBlock.symbol(['a'])]: {
|
|
68
|
-
command: [{ symbol: symbolCommands.keep, movement: movements.stay }],
|
|
69
|
-
nextState: haltState,
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
const gen = machine.runStepByStep({ initialState });
|
|
73
|
-
const m = gen.next().value!;
|
|
74
|
-
|
|
75
|
-
// keep → nextSymbols[0] === currentSymbols[0], so write cell is K
|
|
76
|
-
expect(formatStep(m)).toBe("['a'] → [K]/[S]");
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('formats a 2-tape iter with comma-separated entries', () => {
|
|
80
|
-
const alphabetA = new Alphabet([' ', 'a', 'b']);
|
|
81
|
-
const alphabetB = new Alphabet([' ', 'x', 'y']);
|
|
82
|
-
const tape1 = new Tape({ alphabet: alphabetA, symbols: ['a'] });
|
|
83
|
-
const tape2 = new Tape({ alphabet: alphabetB, symbols: ['x'] });
|
|
84
|
-
const tapeBlock = TapeBlock.fromTapes([tape1, tape2]);
|
|
85
|
-
const machine = new TuringMachine({ tapeBlock });
|
|
86
|
-
const initialState = new State({
|
|
87
|
-
[tapeBlock.symbol(['a', 'x'])]: {
|
|
88
|
-
command: [
|
|
89
|
-
{ symbol: 'b', movement: movements.right },
|
|
90
|
-
{ symbol: symbolCommands.keep, movement: movements.left },
|
|
91
|
-
],
|
|
92
|
-
nextState: haltState,
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
const gen = machine.runStepByStep({ initialState });
|
|
96
|
-
const m = gen.next().value!;
|
|
97
|
-
|
|
98
|
-
expect(formatStep(m)).toBe("['a','x'] → ['b',K]/[R,L]");
|
|
99
|
-
});
|
|
100
|
-
});
|