@tabnas/transduce 0.2.9 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capability.d.ts +4 -0
- package/dist/capability.js +29 -1
- package/dist/capability.js.map +1 -1
- package/dist/parser-event-source.d.ts +11 -0
- package/dist/parser-event-source.js +101 -18
- package/dist/parser-event-source.js.map +1 -1
- package/dist/routers.js +5 -0
- package/dist/routers.js.map +1 -1
- package/dist/transduce-tree.d.ts +5 -0
- package/dist/transduce-tree.js +131 -40
- package/dist/transduce-tree.js.map +1 -1
- package/dist/transduce.d.ts +3 -3
- package/dist/transduce.js +5 -2
- package/dist/transduce.js.map +1 -1
- package/package.json +1 -1
- package/src/capability.ts +29 -0
- package/src/parser-event-source.ts +107 -18
- package/src/routers.ts +6 -0
- package/src/transduce-tree.ts +124 -38
- package/src/transduce.ts +3 -3
package/dist/capability.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export declare const INCREMENTAL: readonly string[];
|
|
2
2
|
export declare function isIncremental(grammar: string): boolean;
|
|
3
|
+
export declare const PRUNABLE: readonly string[];
|
|
4
|
+
export declare function isPrunable(grammar: string): boolean;
|
|
3
5
|
export declare const capability: Readonly<{
|
|
4
6
|
INCREMENTAL: readonly string[];
|
|
5
7
|
incremental: typeof isIncremental;
|
|
8
|
+
PRUNABLE: readonly string[];
|
|
9
|
+
prunable: typeof isPrunable;
|
|
6
10
|
}>;
|
package/dist/capability.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/* Copyright (c) 2026 tabnas, MIT License */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.capability = exports.INCREMENTAL = void 0;
|
|
4
|
+
exports.capability = exports.PRUNABLE = exports.INCREMENTAL = void 0;
|
|
5
5
|
exports.isIncremental = isIncremental;
|
|
6
|
+
exports.isPrunable = isPrunable;
|
|
6
7
|
// Which grammars the incremental source is verified for, in THIS runtime.
|
|
7
8
|
//
|
|
8
9
|
// Incremental streaming is a per-grammar VERIFIED capability, never an
|
|
@@ -57,8 +58,35 @@ exports.INCREMENTAL = Object.freeze([
|
|
|
57
58
|
function isIncremental(grammar) {
|
|
58
59
|
return exports.INCREMENTAL.includes(grammar);
|
|
59
60
|
}
|
|
61
|
+
// The grammars whose passes a `ParserEventSource` relays alike whether it
|
|
62
|
+
// prunes or not, on every document `test/prune.test.ts` reads them, by the
|
|
63
|
+
// name of the format each reads, as `INCREMENTAL` names them. The suite
|
|
64
|
+
// asserts the list in both directions; `toml` is not on it, since its
|
|
65
|
+
// message for a key defined twice quotes the value, which pruning empties.
|
|
66
|
+
exports.PRUNABLE = Object.freeze([
|
|
67
|
+
'csv',
|
|
68
|
+
'feed',
|
|
69
|
+
'ini',
|
|
70
|
+
'json',
|
|
71
|
+
'json5',
|
|
72
|
+
'jsonc',
|
|
73
|
+
'jsonic',
|
|
74
|
+
'jsonl',
|
|
75
|
+
'markdown',
|
|
76
|
+
'pgn',
|
|
77
|
+
'xml',
|
|
78
|
+
'yaml',
|
|
79
|
+
'zon',
|
|
80
|
+
]);
|
|
81
|
+
// Whether a `ParserEventSource` may empty `grammar`'s arrays behind its
|
|
82
|
+
// stream (`ParserEventSource.prune`).
|
|
83
|
+
function isPrunable(grammar) {
|
|
84
|
+
return exports.PRUNABLE.includes(grammar);
|
|
85
|
+
}
|
|
60
86
|
exports.capability = Object.freeze({
|
|
61
87
|
INCREMENTAL: exports.INCREMENTAL,
|
|
62
88
|
incremental: isIncremental,
|
|
89
|
+
PRUNABLE: exports.PRUNABLE,
|
|
90
|
+
prunable: isPrunable,
|
|
63
91
|
});
|
|
64
92
|
//# sourceMappingURL=capability.js.map
|
package/dist/capability.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability.js","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":";AAAA,4CAA4C
|
|
1
|
+
{"version":3,"file":"capability.js","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":";AAAA,4CAA4C;;;;;AAE5C,0EAA0E;AAC1E,EAAE;AACF,uEAAuE;AACvE,0EAA0E;AAC1E,4EAA4E;AAC5E,6DAA6D;AAC7D,wEAAwE;AACxE,qEAAqE;AACrE,0EAA0E;AAC1E,uEAAuE;AACvE,sEAAsE;AACtE,2EAA2E;AAC3E,wEAAwE;AACxE,yEAAyE;AACzE,mCAAmC;AACnC,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,0EAA0E;AAC1E,sEAAsE;AACtE,0EAA0E;AAC1E,UAAU;AAEV,yEAAyE;AACzE,yEAAyE;AACzE,0EAA0E;AAC1E,uEAAuE;AACvE,2EAA2E;AAC3E,4EAA4E;AAC5E,wEAAwE;AAExE,yEAAyE;AACzE,2EAA2E;AAC3E,0EAA0E;AAC1E,uEAAuE;AACvE,qEAAqE;AACrE,yEAAyE;AACzE,sEAAsE;AACtE,0EAA0E;AAC1E,6EAA6E;AAChE,QAAA,WAAW,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC1D,MAAM;IACN,OAAO;IACP,OAAO;IACP,QAAQ;IACR,OAAO;IACP,UAAU;IACV,KAAK;IACL,KAAK;IACL,MAAM;IACN,KAAK;CACN,CAAC,CAAA;AAEF,oDAAoD;AACpD,uBAA8B,OAAe;IAC3C,OAAO,QAAA,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;AACtC,CAAC;AAED,0EAA0E;AAC1E,2EAA2E;AAC3E,wEAAwE;AACxE,sEAAsE;AACtE,2EAA2E;AAC9D,QAAA,QAAQ,GAAsB,MAAM,CAAC,MAAM,CAAC;IACvD,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,OAAO;IACP,OAAO;IACP,QAAQ;IACR,OAAO;IACP,UAAU;IACV,KAAK;IACL,KAAK;IACL,MAAM;IACN,KAAK;CACN,CAAC,CAAA;AAEF,wEAAwE;AACxE,sCAAsC;AACtC,oBAA2B,OAAe;IACxC,OAAO,QAAA,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;AACnC,CAAC;AAEY,QAAA,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;IACtC,WAAW,EAAX,QAAA,WAAW;IACX,WAAW,EAAE,aAAa;IAC1B,QAAQ,EAAR,QAAA,QAAQ;IACR,QAAQ,EAAE,UAAU;CACrB,CAAC,CAAA"}
|
|
@@ -7,11 +7,22 @@ export declare class ParserEventSource {
|
|
|
7
7
|
private sourceLimits;
|
|
8
8
|
private abortFlag;
|
|
9
9
|
private sourceMetrics;
|
|
10
|
+
private grammarName;
|
|
11
|
+
private pruning;
|
|
12
|
+
private isUnverified;
|
|
10
13
|
private used;
|
|
11
14
|
constructor(parser: Tabnas, text: string);
|
|
15
|
+
grammar(name: string): this;
|
|
16
|
+
prune(): this;
|
|
17
|
+
unverified(): this;
|
|
18
|
+
private gate;
|
|
12
19
|
limits(limits: Limits): this;
|
|
13
20
|
abort(abort: AbortFlag): this;
|
|
14
21
|
metrics(metrics: Metrics): this;
|
|
15
22
|
run(sink: ParserSink): Flow;
|
|
23
|
+
runWithValue(sink: ParserSink): {
|
|
24
|
+
flow: Flow;
|
|
25
|
+
value: unknown;
|
|
26
|
+
};
|
|
16
27
|
}
|
|
17
28
|
export declare function scalarOf(value: unknown, lexeme: string | undefined): Datum;
|
|
@@ -16,22 +16,21 @@ exports.scalarOf = scalarOf;
|
|
|
16
16
|
// What each pass carries is alchemy's `RulePass`:
|
|
17
17
|
//
|
|
18
18
|
// - the rule's `name`, its depth `d` and instance `i` (the engine's, which
|
|
19
|
-
//
|
|
20
|
-
// alternate that matched
|
|
21
|
-
// sorted, since they are tags
|
|
22
|
-
//
|
|
23
|
-
// `n`, sorted by name;
|
|
19
|
+
// numbers rules from 1, the start rule's, in the order it makes them),
|
|
20
|
+
// the alternate that matched
|
|
21
|
+
// (`alt`: `p`, `r`, `b`, and its groups `g`, sorted, since they are tags
|
|
22
|
+
// whose order the runtimes' engines keep differently), whether recovery
|
|
23
|
+
// forced the close, and its named counters `n`, sorted by name;
|
|
24
24
|
// - the `tokens` the pass consumed: those its alternate matched (the
|
|
25
25
|
// engine's `o[:oN]` or `c[:cN]`), less the `b` it gave back from their
|
|
26
26
|
// end, which are the next rule's to consume, and none when `b` is more
|
|
27
27
|
// than it recorded, since a close alternate can back up a token it did
|
|
28
28
|
// not record. A token is its name, its value, its source text and its
|
|
29
29
|
// 1-based row and column; a token with no value (the end of the source,
|
|
30
|
-
// a fixed token such as `[
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// stands;
|
|
30
|
+
// or a fixed token such as `[`) has `null`, and a number carries
|
|
31
|
+
// its source text as its lexeme when that text is a JSON number spelling
|
|
32
|
+
// the value (RFC 8259), the rule the incremental source keeps lexemes by,
|
|
33
|
+
// so a renderer can write it as it stands;
|
|
35
34
|
// - on a close pass, the rule's node when that is a scalar (`value`), a
|
|
36
35
|
// number with the lexeme of the rule's first open token as above;
|
|
37
36
|
// nothing on an open pass, nor for a container.
|
|
@@ -55,21 +54,81 @@ exports.scalarOf = scalarOf;
|
|
|
55
54
|
// `AbortFlag` is `ABORTED`; any other engine error is `INPUT_INVALID` with
|
|
56
55
|
// the engine's code and position. A source runs once, as `ParserSource`
|
|
57
56
|
// does: its parser carries the run's subscriber and budget.
|
|
57
|
+
//
|
|
58
|
+
// The engine builds its value as it parses, so a long document's tree
|
|
59
|
+
// holds every record, though nothing reads the value the parse returns.
|
|
60
|
+
// `prune()` empties each array behind the stream: once the sink has seen a
|
|
61
|
+
// close pass whose rule's node is an array, the source empties that array
|
|
62
|
+
// in place, so the tree holds the elements of the passes since the last
|
|
63
|
+
// close on it. That alters the value the engine returns, which this source
|
|
64
|
+
// never reads, and it is sound for a grammar whose passes do not depend on
|
|
65
|
+
// what its arrays hold, so it is a verified capability, as the incremental
|
|
66
|
+
// source is: `test/prune.test.ts` runs every document of every grammar in
|
|
67
|
+
// the devDependencies pruned and not, and `capability.prunable` lists the
|
|
68
|
+
// grammars whose passes and outcomes never differed
|
|
69
|
+
// (rs/src/source/parser_events.rs has the contract's one difference, a
|
|
70
|
+
// token whose value is a container of the tree). A run that prunes is
|
|
71
|
+
// gated on the list, by the grammar's name (`grammar`), and without a
|
|
72
|
+
// listed name it fails with `STREAMABILITY_UNKNOWN` before the parse,
|
|
73
|
+
// having sent nothing; `unverified()` lifts the gate for the suite.
|
|
58
74
|
const shared_1 = require("@tabnas/alchemy/shared");
|
|
75
|
+
const capability_1 = require("./capability");
|
|
59
76
|
const rule_events_1 = require("./rule-events");
|
|
60
77
|
const source_1 = require("./source");
|
|
61
78
|
// A tabnas parser applied to one text, as a source of its rule passes.
|
|
62
79
|
class ParserEventSource {
|
|
63
|
-
// A source with default limits, its own abort flag and fresh metrics
|
|
64
|
-
// builder methods change each.
|
|
80
|
+
// A source with default limits, its own abort flag and fresh metrics,
|
|
81
|
+
// that prunes nothing; the builder methods change each.
|
|
65
82
|
constructor(parser, text) {
|
|
66
83
|
this.sourceLimits = shared_1.Limits.default();
|
|
67
84
|
this.abortFlag = new shared_1.AbortFlag();
|
|
68
85
|
this.sourceMetrics = new shared_1.Metrics();
|
|
86
|
+
this.grammarName = null;
|
|
87
|
+
this.pruning = false;
|
|
88
|
+
this.isUnverified = false;
|
|
69
89
|
this.used = false;
|
|
70
90
|
this.parser = parser;
|
|
71
91
|
this.text = text;
|
|
72
92
|
}
|
|
93
|
+
// The grammar the parser implements, by the name of the format it reads,
|
|
94
|
+
// as `ParserSource.grammar` takes it. A run that prunes needs a name
|
|
95
|
+
// `capability.prunable` lists; one that does not prune needs no name.
|
|
96
|
+
grammar(name) {
|
|
97
|
+
this.grammarName = name;
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
// Empty each array in the engine's tree once the sink has seen a close
|
|
101
|
+
// pass on it, so the tree does not hold the whole document. The passes
|
|
102
|
+
// are unchanged for a grammar `capability.prunable` lists, and `run`
|
|
103
|
+
// refuses to prune any other, with `STREAMABILITY_UNKNOWN` before the
|
|
104
|
+
// parse.
|
|
105
|
+
prune() {
|
|
106
|
+
this.pruning = true;
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
// Prune whatever the verified list says. This exists for the
|
|
110
|
+
// differential suite that maintains the list and for nothing else: a
|
|
111
|
+
// grammar that reads its arrays back may make other passes once they are
|
|
112
|
+
// emptied, and the run still returns.
|
|
113
|
+
unverified() {
|
|
114
|
+
this.isUnverified = true;
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
// Why the run may not prune, when it may not.
|
|
118
|
+
gate() {
|
|
119
|
+
if (!this.pruning || this.isUnverified)
|
|
120
|
+
return null;
|
|
121
|
+
const name = this.grammarName;
|
|
122
|
+
if (null !== name && (0, capability_1.isPrunable)(name))
|
|
123
|
+
return null;
|
|
124
|
+
if (null !== name) {
|
|
125
|
+
return new shared_1.Fail('STREAMABILITY_UNKNOWN', `grammar ${JSON.stringify(name)} is not in capability.prunable: the differential suite ` +
|
|
126
|
+
'has not verified that its passes are the same when its arrays are emptied behind ' +
|
|
127
|
+
'the stream; run it without ParserEventSource.prune');
|
|
128
|
+
}
|
|
129
|
+
return new shared_1.Fail('STREAMABILITY_UNKNOWN', "ParserEventSource.prune needs the grammar's name (ParserEventSource.grammar) to check " +
|
|
130
|
+
'capability.prunable; without one, run it without pruning');
|
|
131
|
+
}
|
|
73
132
|
limits(limits) {
|
|
74
133
|
this.sourceLimits = limits;
|
|
75
134
|
return this;
|
|
@@ -84,19 +143,28 @@ class ParserEventSource {
|
|
|
84
143
|
}
|
|
85
144
|
// Parse, relaying each pass into `sink`, then `end`.
|
|
86
145
|
run(sink) {
|
|
146
|
+
return this.runWithValue(sink).flow;
|
|
147
|
+
}
|
|
148
|
+
// `run`, also handing back the value the engine returned: the engine's
|
|
149
|
+
// tree after pruning, when the run prunes. It is not the run's result
|
|
150
|
+
// (the passes are): it exists so a test can measure what pruning left in
|
|
151
|
+
// the tree, and nothing else should read it.
|
|
152
|
+
runWithValue(sink) {
|
|
87
153
|
if (this.used) {
|
|
88
154
|
throw new shared_1.Fail('STREAM_REUSED', "a ParserEventSource runs once: its parser carries the run's subscriber and budget; " +
|
|
89
155
|
'make a new source, with a new parser, for another run');
|
|
90
156
|
}
|
|
91
157
|
this.used = true;
|
|
158
|
+
const refused = this.gate();
|
|
159
|
+
if (null !== refused)
|
|
160
|
+
throw refused;
|
|
92
161
|
const abort = this.abortFlag;
|
|
93
162
|
const stop = new shared_1.AbortFlag();
|
|
94
|
-
const relay = new Relay(sink, this.sourceLimits.max_scalar_bytes, abort, stop);
|
|
163
|
+
const relay = new Relay(sink, this.sourceLimits.max_scalar_bytes, abort, stop, this.pruning);
|
|
95
164
|
(0, rule_events_1.prepare)(this.parser, () => !abort.isAborted() && !stop.isAborted(), (rule, done) => relay.onDone(rule, done));
|
|
96
165
|
let parsed;
|
|
97
166
|
try {
|
|
98
|
-
this.parser.parse(this.text);
|
|
99
|
-
parsed = { ok: true };
|
|
167
|
+
parsed = { ok: true, value: this.parser.parse(this.text) };
|
|
100
168
|
}
|
|
101
169
|
catch (error) {
|
|
102
170
|
parsed = { ok: false, error };
|
|
@@ -106,10 +174,10 @@ class ParserEventSource {
|
|
|
106
174
|
if ('failed' === status.type)
|
|
107
175
|
throw status.error;
|
|
108
176
|
if ('stopped' === status.type)
|
|
109
|
-
return 'stop';
|
|
177
|
+
return { flow: 'stop', value: undefined };
|
|
110
178
|
if (!parsed.ok)
|
|
111
179
|
throw (0, source_1.engineFailure)(parsed.error, abort);
|
|
112
|
-
return relay.send({ type: 'end' });
|
|
180
|
+
return { flow: relay.send({ type: 'end' }), value: parsed.value };
|
|
113
181
|
}
|
|
114
182
|
finally {
|
|
115
183
|
this.sourceMetrics.events += relay.events;
|
|
@@ -121,11 +189,14 @@ exports.ParserEventSource = ParserEventSource;
|
|
|
121
189
|
class Relay {
|
|
122
190
|
constructor(sink, maxScalarBytes, abort,
|
|
123
191
|
// Set to make the parse guard cancel the parse.
|
|
124
|
-
stop
|
|
192
|
+
stop,
|
|
193
|
+
// Empty the array a close pass's rule builds, once the sink has seen it.
|
|
194
|
+
prune) {
|
|
125
195
|
this.sink = sink;
|
|
126
196
|
this.maxScalarBytes = maxScalarBytes;
|
|
127
197
|
this.abort = abort;
|
|
128
198
|
this.stop = stop;
|
|
199
|
+
this.prune = prune;
|
|
129
200
|
this.status = { type: 'running' };
|
|
130
201
|
this.events = 0;
|
|
131
202
|
}
|
|
@@ -160,12 +231,24 @@ class Relay {
|
|
|
160
231
|
this.status = { type: 'stopped' };
|
|
161
232
|
this.stop.abort();
|
|
162
233
|
}
|
|
234
|
+
else if (this.prune && !open) {
|
|
235
|
+
emptyArray(rule);
|
|
236
|
+
}
|
|
163
237
|
}
|
|
164
238
|
catch (error) {
|
|
165
239
|
this.fail(error);
|
|
166
240
|
}
|
|
167
241
|
}
|
|
168
242
|
}
|
|
243
|
+
// Empty the array `rule`'s node is, in place, so every holder of it sees
|
|
244
|
+
// it emptied. Every element in it was built by passes the sink has seen,
|
|
245
|
+
// the last of them the close pass just sent, and what those passes carry
|
|
246
|
+
// is the sink's, so nothing sent so far changes.
|
|
247
|
+
function emptyArray(rule) {
|
|
248
|
+
const node = rule.node;
|
|
249
|
+
if (Array.isArray(node) && 0 < node.length)
|
|
250
|
+
node.length = 0;
|
|
251
|
+
}
|
|
169
252
|
// The pass as alchemy's `RulePass`, its sizes held to `max_scalar_bytes`.
|
|
170
253
|
function rulePass(rule, done, open, max) {
|
|
171
254
|
const r = rule;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser-event-source.js","sourceRoot":"","sources":["../src/parser-event-source.ts"],"names":[],"mappings":";AAAA,4CAA4C;;;;AAE5C,yEAAyE;AACzE,sEAAsE;AACtE,EAAE;AACF,wEAAwE;AACxE,2EAA2E;AAC3E,4EAA4E;AAC5E,oEAAoE;AACpE,2EAA2E;AAC3E,gDAAgD;AAChD,EAAE;AACF,kDAAkD;AAClD,EAAE;AACF,2EAA2E;AAC3E,
|
|
1
|
+
{"version":3,"file":"parser-event-source.js","sourceRoot":"","sources":["../src/parser-event-source.ts"],"names":[],"mappings":"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"}
|
package/dist/routers.js
CHANGED
|
@@ -24,5 +24,10 @@ exports.routers = Object.freeze({
|
|
|
24
24
|
transduceTree(spec, downstream) {
|
|
25
25
|
return new transduce_tree_1.TransduceTree(spec, downstream);
|
|
26
26
|
},
|
|
27
|
+
// TransduceTree reads a spec's `:member` and `:text`, and holds a text to
|
|
28
|
+
// the spec's `maxTextBytes` as it grows.
|
|
29
|
+
transduceTreeReadsMemberAndText() {
|
|
30
|
+
return true;
|
|
31
|
+
},
|
|
27
32
|
});
|
|
28
33
|
//# sourceMappingURL=routers.js.map
|
package/dist/routers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routers.js","sourceRoot":"","sources":["../src/routers.ts"],"names":[],"mappings":";AAAA,4CAA4C;;;AA2B5C,mCAAiC;AACjC,mCAAgC;AAChC,iCAAiC;AACjC,uDAAiD;AACjD,qDAAgD;AAEhD,sEAAsE;AACzD,QAAA,OAAO,GAAY,MAAM,CAAC,MAAM,CAAC;IAC5C,MAAM,CACJ,KAA6B,EAC7B,MAAc,EACd,UAAsB,EACtB,OAAgB,EAChB,UAAqB;QAErB,OAAO,IAAI,cAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,CAAC,CAAA;IACnE,CAAC;IAED,aAAa,CACX,OAAqB,EACrB,MAAc,EACd,UAAsB,EACtB,OAAgB,EAChB,IAAe;QAEf,OAAO,IAAI,+BAAa,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;IACtE,CAAC;IAED,QAAQ,CACN,OAAU,EACV,IAA6C,EAC7C,MAAyB,EACzB,GAAwB;QAExB,OAAO,IAAI,eAAQ,CAAU,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IAC1D,CAAC;IAED,OAAO,CAAC,KAAW,EAAE,MAAc,EAAE,KAAgB,EAAE,OAAgB;QACrE,OAAO,IAAI,eAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IACnD,CAAC;IAED,aAAa,CAAC,IAAc,EAAE,UAAgB;QAC5C,OAAO,IAAI,8BAAa,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;IAC5C,CAAC;CACF,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"routers.js","sourceRoot":"","sources":["../src/routers.ts"],"names":[],"mappings":";AAAA,4CAA4C;;;AA2B5C,mCAAiC;AACjC,mCAAgC;AAChC,iCAAiC;AACjC,uDAAiD;AACjD,qDAAgD;AAEhD,sEAAsE;AACzD,QAAA,OAAO,GAAY,MAAM,CAAC,MAAM,CAAC;IAC5C,MAAM,CACJ,KAA6B,EAC7B,MAAc,EACd,UAAsB,EACtB,OAAgB,EAChB,UAAqB;QAErB,OAAO,IAAI,cAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,CAAC,CAAA;IACnE,CAAC;IAED,aAAa,CACX,OAAqB,EACrB,MAAc,EACd,UAAsB,EACtB,OAAgB,EAChB,IAAe;QAEf,OAAO,IAAI,+BAAa,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;IACtE,CAAC;IAED,QAAQ,CACN,OAAU,EACV,IAA6C,EAC7C,MAAyB,EACzB,GAAwB;QAExB,OAAO,IAAI,eAAQ,CAAU,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IAC1D,CAAC;IAED,OAAO,CAAC,KAAW,EAAE,MAAc,EAAE,KAAgB,EAAE,OAAgB;QACrE,OAAO,IAAI,eAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IACnD,CAAC;IAED,aAAa,CAAC,IAAc,EAAE,UAAgB;QAC5C,OAAO,IAAI,8BAAa,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;IAC5C,CAAC;IAED,0EAA0E;IAC1E,yCAAyC;IACzC,+BAA+B;QAC7B,OAAO,IAAI,CAAA;IACb,CAAC;CACF,CAAC,CAAA"}
|
package/dist/transduce-tree.d.ts
CHANGED
|
@@ -4,8 +4,13 @@ export declare class TransduceTree implements ParserSink {
|
|
|
4
4
|
readonly down: Sink;
|
|
5
5
|
open: number;
|
|
6
6
|
carry: boolean;
|
|
7
|
+
readonly texts: string[];
|
|
8
|
+
readonly bytes: number[];
|
|
7
9
|
constructor(spec: TreeSpec, down: Sink);
|
|
8
10
|
event(ev: ParserEvent): Flow;
|
|
11
|
+
private get max();
|
|
12
|
+
private spell;
|
|
13
|
+
private write;
|
|
9
14
|
private openPass;
|
|
10
15
|
private closePass;
|
|
11
16
|
}
|
package/dist/transduce-tree.js
CHANGED
|
@@ -10,18 +10,32 @@ exports.TransduceTree = void 0;
|
|
|
10
10
|
// alchemy's `TreeSpec` documents what a native must do: each pass is looked
|
|
11
11
|
// up by its rule's name, one lookup per pass whatever the spec's size; an
|
|
12
12
|
// open pass announces its key, then begins its container or yields its
|
|
13
|
-
// scalar from one of its tokens; a close pass
|
|
14
|
-
// yields its
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// the
|
|
20
|
-
//
|
|
13
|
+
// scalar from one of its tokens, then announces its member; a close pass
|
|
14
|
+
// yields its text, when its rule is a `:text` rule, then ends its
|
|
15
|
+
// container, or yields its own value; and a container whose rule is
|
|
16
|
+
// replaced by a rule the spec names a container is one container, so that
|
|
17
|
+
// close ends nothing and the replacing open begins nothing. The state is
|
|
18
|
+
// the count of open containers, whether a close handed its container on,
|
|
19
|
+
// and the text each open `:text` rule has gathered, to which every pass
|
|
20
|
+
// writes its tokens' source text, so the native retains the document's
|
|
21
|
+
// nesting and an open rule's text, and nothing else of its width or
|
|
22
|
+
// length, as the library's step does. alchemy-cli's differential test
|
|
23
|
+
// holds the two to the same bytes.
|
|
24
|
+
//
|
|
25
|
+
// A text is held to the spec's `maxTextBytes` as it grows, at the joins
|
|
26
|
+
// the library's `string-join` makes and in their order: a pass's tokens
|
|
27
|
+
// together, then the innermost text with them, and a closed text with the
|
|
28
|
+
// text around it, each refused past the bound with alchemy's
|
|
29
|
+
// `treeTextRefusal`. The routers say this native reads `:member` and
|
|
30
|
+
// `:text` (routers.ts), which is what lets the lowering hand it a spec
|
|
31
|
+
// that uses either.
|
|
21
32
|
//
|
|
22
33
|
// A pass's events are worked out before the first of them is written, as
|
|
23
34
|
// the library's step returns them together: a pass that fails writes none.
|
|
24
|
-
// The failures are the library's, with its codes and texts
|
|
35
|
+
// The failures are the library's, with its codes and texts, and in its
|
|
36
|
+
// order: an open pass's key, then what it begins or yields, then its
|
|
37
|
+
// tokens' text, then its member; a close pass's text, then what it ends or
|
|
38
|
+
// yields.
|
|
25
39
|
const shared_1 = require("@tabnas/alchemy/shared");
|
|
26
40
|
// The library's `transduce-tree`, natively, writing to `down`.
|
|
27
41
|
class TransduceTree {
|
|
@@ -32,6 +46,10 @@ class TransduceTree {
|
|
|
32
46
|
this.open = 0;
|
|
33
47
|
// A close handed its container on to the rule replacing it.
|
|
34
48
|
this.carry = false;
|
|
49
|
+
// The text each open `:text` rule has gathered, the innermost last, and
|
|
50
|
+
// its length in UTF-8 bytes, which the bound is held to.
|
|
51
|
+
this.texts = [];
|
|
52
|
+
this.bytes = [];
|
|
35
53
|
}
|
|
36
54
|
event(ev) {
|
|
37
55
|
switch (ev.type) {
|
|
@@ -46,30 +64,49 @@ class TransduceTree {
|
|
|
46
64
|
return this.down.event(shared_1.Ev.end);
|
|
47
65
|
}
|
|
48
66
|
}
|
|
67
|
+
// The most bytes a text may hold.
|
|
68
|
+
get max() {
|
|
69
|
+
return this.spec.maxTextBytes ?? Infinity;
|
|
70
|
+
}
|
|
71
|
+
// The pass's tokens written to the innermost open text, if one is and the
|
|
72
|
+
// pass consumed any, as the library's `transduce-spell` writes them: the
|
|
73
|
+
// tokens' text joined, then written to the innermost, each join held to
|
|
74
|
+
// the bound.
|
|
75
|
+
spell(pass) {
|
|
76
|
+
if (0 === this.texts.length || 0 === pass.tokens.length)
|
|
77
|
+
return;
|
|
78
|
+
let more = '';
|
|
79
|
+
let n = 0;
|
|
80
|
+
for (const token of pass.tokens) {
|
|
81
|
+
more += token.src;
|
|
82
|
+
n += (0, shared_1.utf8Bytes)(token.src);
|
|
83
|
+
}
|
|
84
|
+
if (n > this.max)
|
|
85
|
+
throw (0, shared_1.treeTextRefusal)(n, this.max);
|
|
86
|
+
this.write(more, n);
|
|
87
|
+
}
|
|
88
|
+
// `more`, of `n` bytes, written at the end of the innermost open text, as
|
|
89
|
+
// the library's `transduce-write` joins the two, held to the bound.
|
|
90
|
+
write(more, n) {
|
|
91
|
+
const last = this.texts.length - 1;
|
|
92
|
+
const len = this.bytes[last] + n;
|
|
93
|
+
if (len > this.max)
|
|
94
|
+
throw (0, shared_1.treeTextRefusal)(len, this.max);
|
|
95
|
+
this.texts[last] += more;
|
|
96
|
+
this.bytes[last] = len;
|
|
97
|
+
}
|
|
49
98
|
openPass(pass) {
|
|
50
99
|
const rule = this.spec.rules.get(pass.name);
|
|
51
|
-
if (undefined === rule)
|
|
100
|
+
if (undefined === rule) {
|
|
101
|
+
this.spell(pass);
|
|
52
102
|
return 'continue';
|
|
53
|
-
let key;
|
|
54
|
-
if (undefined !== rule.key) {
|
|
55
|
-
if ('name' in rule.key)
|
|
56
|
-
key = rule.key.name;
|
|
57
|
-
else {
|
|
58
|
-
const token = pass.tokens[rule.key.token];
|
|
59
|
-
if (undefined !== token) {
|
|
60
|
-
if ('string' !== token.val.type) {
|
|
61
|
-
throw typeError(`key: the name must be a string, not ${kind(token.val)}`);
|
|
62
|
-
}
|
|
63
|
-
key = token.val.value;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
103
|
}
|
|
104
|
+
const key = named(rule.key, pass);
|
|
67
105
|
let then;
|
|
106
|
+
let begins = false;
|
|
68
107
|
if (undefined !== rule.container) {
|
|
69
|
-
if (this.carry)
|
|
70
|
-
|
|
71
|
-
else {
|
|
72
|
-
this.open++;
|
|
108
|
+
if (!this.carry) {
|
|
109
|
+
begins = true;
|
|
73
110
|
then = 'object' === rule.container ? shared_1.Ev.objectStart : shared_1.Ev.arrayStart;
|
|
74
111
|
}
|
|
75
112
|
}
|
|
@@ -78,31 +115,85 @@ class TransduceTree {
|
|
|
78
115
|
if (undefined !== token)
|
|
79
116
|
then = scalar(token.val);
|
|
80
117
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
118
|
+
this.spell(pass);
|
|
119
|
+
const member = named(rule.member, pass);
|
|
120
|
+
if (undefined !== rule.container) {
|
|
121
|
+
if (begins)
|
|
122
|
+
this.open++;
|
|
123
|
+
else
|
|
124
|
+
this.carry = false;
|
|
125
|
+
}
|
|
126
|
+
if (true === rule.text) {
|
|
127
|
+
this.texts.push('');
|
|
128
|
+
this.bytes.push(0);
|
|
129
|
+
}
|
|
130
|
+
for (const ev of [
|
|
131
|
+
undefined === key ? undefined : shared_1.Ev.key(key),
|
|
132
|
+
then,
|
|
133
|
+
undefined === member ? undefined : shared_1.Ev.key(member),
|
|
134
|
+
]) {
|
|
135
|
+
if (undefined !== ev && 'stop' === this.down.event(ev))
|
|
136
|
+
return 'stop';
|
|
137
|
+
}
|
|
138
|
+
return 'continue';
|
|
84
139
|
}
|
|
85
140
|
closePass(pass) {
|
|
86
141
|
const rule = this.spec.rules.get(pass.name);
|
|
87
|
-
if (undefined === rule)
|
|
142
|
+
if (undefined === rule) {
|
|
143
|
+
this.spell(pass);
|
|
88
144
|
return 'continue';
|
|
145
|
+
}
|
|
146
|
+
// A `:text` rule's text closes first, and joins the one around it
|
|
147
|
+
// before this pass's own tokens do; it is yielded before anything
|
|
148
|
+
// else, once the rest of the pass is known not to fail.
|
|
149
|
+
let text;
|
|
150
|
+
if (true === rule.text) {
|
|
151
|
+
text = this.texts.pop();
|
|
152
|
+
if (undefined === text)
|
|
153
|
+
throw typeError('top: the vector is empty; there is no last item');
|
|
154
|
+
const n = this.bytes.pop();
|
|
155
|
+
if (0 < this.texts.length)
|
|
156
|
+
this.write(text, n);
|
|
157
|
+
}
|
|
158
|
+
this.spell(pass);
|
|
159
|
+
let then;
|
|
89
160
|
if (undefined === rule.container) {
|
|
90
161
|
if ('value' === rule.scalar && undefined !== pass.value)
|
|
91
|
-
|
|
92
|
-
return 'continue';
|
|
162
|
+
then = scalar(pass.value);
|
|
93
163
|
}
|
|
94
|
-
if (undefined !== pass.alt && (0, shared_1.isTreeContainer)(this.spec, pass.alt.r)) {
|
|
164
|
+
else if (undefined !== pass.alt && (0, shared_1.isTreeContainer)(this.spec, pass.alt.r)) {
|
|
95
165
|
this.carry = true;
|
|
96
|
-
return 'continue';
|
|
97
166
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
167
|
+
else {
|
|
168
|
+
if (0 === this.open)
|
|
169
|
+
throw typeError('pop: the vector is empty; there is no last item to remove');
|
|
170
|
+
this.open--;
|
|
171
|
+
this.carry = false;
|
|
172
|
+
then = 'object' === rule.container ? shared_1.Ev.objectEnd : shared_1.Ev.arrayEnd;
|
|
173
|
+
}
|
|
174
|
+
if (undefined !== text && 'stop' === this.down.event(shared_1.Ev.string(text)))
|
|
175
|
+
return 'stop';
|
|
176
|
+
return undefined === then ? 'continue' : this.down.event(then);
|
|
103
177
|
}
|
|
104
178
|
}
|
|
105
179
|
exports.TransduceTree = TransduceTree;
|
|
180
|
+
// The member name a field of a rule's entry names for a pass: the string,
|
|
181
|
+
// or the `:val` of the pass's token at that index, nothing when the pass
|
|
182
|
+
// consumed no such token; the library's `key` refuses a name that is not a
|
|
183
|
+
// string.
|
|
184
|
+
function named(field, pass) {
|
|
185
|
+
if (undefined === field)
|
|
186
|
+
return undefined;
|
|
187
|
+
if ('name' in field)
|
|
188
|
+
return field.name;
|
|
189
|
+
const token = pass.tokens[field.token];
|
|
190
|
+
if (undefined === token)
|
|
191
|
+
return undefined;
|
|
192
|
+
if ('string' !== token.val.type) {
|
|
193
|
+
throw typeError(`key: the name must be a string, not ${kind(token.val)}`);
|
|
194
|
+
}
|
|
195
|
+
return token.val.value;
|
|
196
|
+
}
|
|
106
197
|
// A value as the library's `scalar` takes it, or its refusal.
|
|
107
198
|
function scalar(value) {
|
|
108
199
|
switch (value.type) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transduce-tree.js","sourceRoot":"","sources":["../src/transduce-tree.ts"],"names":[],"mappings":";AAAA,4CAA4C;;;AAE5C,yEAAyE;AACzE,0EAA0E;AAC1E,iEAAiE;AACjE,EAAE;AACF,wEAAwE;AACxE,4EAA4E;AAC5E,0EAA0E;AAC1E,uEAAuE;AACvE,
|
|
1
|
+
{"version":3,"file":"transduce-tree.js","sourceRoot":"","sources":["../src/transduce-tree.ts"],"names":[],"mappings":"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"}
|
package/dist/transduce.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const VERSION = "0.2.
|
|
1
|
+
export declare const VERSION = "0.2.11";
|
|
2
2
|
export { Datum, DatumBuilder, byteSize, fromJSON, fromTabnas, getPath, takePath, toJSON, toText, walkDatum, } from '@tabnas/alchemy/shared';
|
|
3
3
|
export type { Duplicates } from '@tabnas/alchemy/shared';
|
|
4
4
|
export { Code, Fail, isFail } from '@tabnas/alchemy/shared';
|
|
@@ -25,11 +25,11 @@ export { Guarded } from './guard';
|
|
|
25
25
|
export { ParserSource } from './parser-source';
|
|
26
26
|
export { ParserEventSource } from './parser-event-source';
|
|
27
27
|
export { TransduceTree } from './transduce-tree';
|
|
28
|
-
export { isTreeContainer, parserEventFromJSON, parserEventToJSON } from '@tabnas/alchemy/shared';
|
|
28
|
+
export { isTreeContainer, parserEventFromJSON, parserEventToJSON, treeTextRefusal } from '@tabnas/alchemy/shared';
|
|
29
29
|
export type { ParserEvent, ParserSink, PassAlt, PassToken, RulePass, TreeContainer, TreeKey, TreeRule, TreeScalar, TreeSpec, } from '@tabnas/alchemy/shared';
|
|
30
30
|
export { DEFAULT_CHUNK_BYTES, LineFormat, LinesSource } from './lines';
|
|
31
31
|
export type { LinesChunk, LinesInput, LinesWriter } from './lines';
|
|
32
|
-
export { INCREMENTAL, capability, isIncremental } from './capability';
|
|
32
|
+
export { INCREMENTAL, PRUNABLE, capability, isIncremental, isPrunable } from './capability';
|
|
33
33
|
export { Cell, Schema, Table, boundColumn, columnFromMeta } from '@tabnas/alchemy/shared';
|
|
34
34
|
export type { BoundColumn, ColumnMapper, MissingPolicy, PublicColumn, TableBinding, TableEvent, TableSink, } from '@tabnas/alchemy/shared';
|
|
35
35
|
export { TableFromJson } from './table-from-json';
|
package/dist/transduce.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/* Copyright (c) 2026 tabnas, MIT License */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.engineFailure = exports.ValueSource = exports.SourceMode = exports.Prune = exports.replay = exports.TreeContract = exports.FnSink = exports.EventRecorder = exports.CountSink = exports.pathText = exports.keyText = exports.Selector = exports.Path = exports.Transition = exports.ScanEmit = exports.CaptureSpec = exports.SelectedRecorder = exports.Router = exports.FnRoute = exports.Matcher = exports.utf8Bytes = exports.NODE_BYTES = exports.Metrics = exports.Limits = exports.LIMIT_NAMES = exports.AbortFlag = exports.numberText = exports.jsonString = exports.jsonNumber = exports.isJsonNumber = exports.isStart = exports.isScalar = exports.isEnd = exports.eventText = exports.eventEquals = exports.Ev = exports.isFail = exports.Fail = exports.Code = exports.walkDatum = exports.toText = exports.toJSON = exports.takePath = exports.getPath = exports.fromTabnas = exports.fromJSON = exports.byteSize = exports.DatumBuilder = exports.Datum = exports.VERSION = void 0;
|
|
5
|
-
exports.routers = exports.TableFromJson = exports.columnFromMeta = exports.boundColumn = exports.Table = exports.Schema = exports.Cell = exports.isIncremental = exports.capability = exports.INCREMENTAL = exports.LinesSource = exports.LineFormat = exports.DEFAULT_CHUNK_BYTES = exports.parserEventToJSON = exports.parserEventFromJSON = exports.isTreeContainer = exports.TransduceTree = exports.ParserEventSource = exports.ParserSource = exports.Guarded = exports.walkValue = void 0;
|
|
5
|
+
exports.routers = exports.TableFromJson = exports.columnFromMeta = exports.boundColumn = exports.Table = exports.Schema = exports.Cell = exports.isPrunable = exports.isIncremental = exports.capability = exports.PRUNABLE = exports.INCREMENTAL = exports.LinesSource = exports.LineFormat = exports.DEFAULT_CHUNK_BYTES = exports.treeTextRefusal = exports.parserEventToJSON = exports.parserEventFromJSON = exports.isTreeContainer = exports.TransduceTree = exports.ParserEventSource = exports.ParserSource = exports.Guarded = exports.walkValue = void 0;
|
|
6
6
|
// Streaming transducers over any tabnas parser.
|
|
7
7
|
//
|
|
8
8
|
// A transducer decides what a source contributes to an output model; a
|
|
@@ -34,7 +34,7 @@ exports.routers = exports.TableFromJson = exports.columnFromMeta = exports.bound
|
|
|
34
34
|
// under the names it always exported them by. `routers` is this package's
|
|
35
35
|
// stages as alchemy's `Routers`, for a host to pass to alchemy's `compile`.
|
|
36
36
|
// This package's version, as package.json declares it.
|
|
37
|
-
exports.VERSION = '0.2.
|
|
37
|
+
exports.VERSION = '0.2.11';
|
|
38
38
|
var shared_1 = require("@tabnas/alchemy/shared");
|
|
39
39
|
Object.defineProperty(exports, "Datum", { enumerable: true, get: function () { return shared_1.Datum; } });
|
|
40
40
|
Object.defineProperty(exports, "DatumBuilder", { enumerable: true, get: function () { return shared_1.DatumBuilder; } });
|
|
@@ -110,14 +110,17 @@ var shared_10 = require("@tabnas/alchemy/shared");
|
|
|
110
110
|
Object.defineProperty(exports, "isTreeContainer", { enumerable: true, get: function () { return shared_10.isTreeContainer; } });
|
|
111
111
|
Object.defineProperty(exports, "parserEventFromJSON", { enumerable: true, get: function () { return shared_10.parserEventFromJSON; } });
|
|
112
112
|
Object.defineProperty(exports, "parserEventToJSON", { enumerable: true, get: function () { return shared_10.parserEventToJSON; } });
|
|
113
|
+
Object.defineProperty(exports, "treeTextRefusal", { enumerable: true, get: function () { return shared_10.treeTextRefusal; } });
|
|
113
114
|
var lines_1 = require("./lines");
|
|
114
115
|
Object.defineProperty(exports, "DEFAULT_CHUNK_BYTES", { enumerable: true, get: function () { return lines_1.DEFAULT_CHUNK_BYTES; } });
|
|
115
116
|
Object.defineProperty(exports, "LineFormat", { enumerable: true, get: function () { return lines_1.LineFormat; } });
|
|
116
117
|
Object.defineProperty(exports, "LinesSource", { enumerable: true, get: function () { return lines_1.LinesSource; } });
|
|
117
118
|
var capability_1 = require("./capability");
|
|
118
119
|
Object.defineProperty(exports, "INCREMENTAL", { enumerable: true, get: function () { return capability_1.INCREMENTAL; } });
|
|
120
|
+
Object.defineProperty(exports, "PRUNABLE", { enumerable: true, get: function () { return capability_1.PRUNABLE; } });
|
|
119
121
|
Object.defineProperty(exports, "capability", { enumerable: true, get: function () { return capability_1.capability; } });
|
|
120
122
|
Object.defineProperty(exports, "isIncremental", { enumerable: true, get: function () { return capability_1.isIncremental; } });
|
|
123
|
+
Object.defineProperty(exports, "isPrunable", { enumerable: true, get: function () { return capability_1.isPrunable; } });
|
|
121
124
|
var shared_11 = require("@tabnas/alchemy/shared");
|
|
122
125
|
Object.defineProperty(exports, "Cell", { enumerable: true, get: function () { return shared_11.Cell; } });
|
|
123
126
|
Object.defineProperty(exports, "Schema", { enumerable: true, get: function () { return shared_11.Schema; } });
|
package/dist/transduce.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transduce.js","sourceRoot":"","sources":["../src/transduce.ts"],"names":[],"mappings":";AAAA,4CAA4C;;;;AAE5C,gDAAgD;AAChD,EAAE;AACF,uEAAuE;AACvE,oEAAoE;AACpE,mBAAmB;AACnB,EAAE;AACF,2EAA2E;AAC3E,YAAY;AACZ,0EAA0E;AAC1E,wEAAwE;AACxE,4EAA4E;AAC5E,qDAAqD;AACrD,wEAAwE;AACxE,YAAY;AACZ,0EAA0E;AAC1E,yDAAyD;AACzD,qEAAqE;AACrE,qCAAqC;AACrC,0EAA0E;AAC1E,mBAAmB;AACnB,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,gEAAgE;AAChE,EAAE;AACF,wEAAwE;AACxE,6DAA6D;AAC7D,wEAAwE;AACxE,0EAA0E;AAC1E,4EAA4E;AAE5E,uDAAuD;AAC1C,QAAA,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"transduce.js","sourceRoot":"","sources":["../src/transduce.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/capability.ts
CHANGED
|
@@ -58,7 +58,36 @@ export function isIncremental(grammar: string): boolean {
|
|
|
58
58
|
return INCREMENTAL.includes(grammar)
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// The grammars whose passes a `ParserEventSource` relays alike whether it
|
|
62
|
+
// prunes or not, on every document `test/prune.test.ts` reads them, by the
|
|
63
|
+
// name of the format each reads, as `INCREMENTAL` names them. The suite
|
|
64
|
+
// asserts the list in both directions; `toml` is not on it, since its
|
|
65
|
+
// message for a key defined twice quotes the value, which pruning empties.
|
|
66
|
+
export const PRUNABLE: readonly string[] = Object.freeze([
|
|
67
|
+
'csv',
|
|
68
|
+
'feed',
|
|
69
|
+
'ini',
|
|
70
|
+
'json',
|
|
71
|
+
'json5',
|
|
72
|
+
'jsonc',
|
|
73
|
+
'jsonic',
|
|
74
|
+
'jsonl',
|
|
75
|
+
'markdown',
|
|
76
|
+
'pgn',
|
|
77
|
+
'xml',
|
|
78
|
+
'yaml',
|
|
79
|
+
'zon',
|
|
80
|
+
])
|
|
81
|
+
|
|
82
|
+
// Whether a `ParserEventSource` may empty `grammar`'s arrays behind its
|
|
83
|
+
// stream (`ParserEventSource.prune`).
|
|
84
|
+
export function isPrunable(grammar: string): boolean {
|
|
85
|
+
return PRUNABLE.includes(grammar)
|
|
86
|
+
}
|
|
87
|
+
|
|
61
88
|
export const capability = Object.freeze({
|
|
62
89
|
INCREMENTAL,
|
|
63
90
|
incremental: isIncremental,
|
|
91
|
+
PRUNABLE,
|
|
92
|
+
prunable: isPrunable,
|
|
64
93
|
})
|
|
@@ -13,22 +13,21 @@
|
|
|
13
13
|
// What each pass carries is alchemy's `RulePass`:
|
|
14
14
|
//
|
|
15
15
|
// - the rule's `name`, its depth `d` and instance `i` (the engine's, which
|
|
16
|
-
//
|
|
17
|
-
// alternate that matched
|
|
18
|
-
// sorted, since they are tags
|
|
19
|
-
//
|
|
20
|
-
// `n`, sorted by name;
|
|
16
|
+
// numbers rules from 1, the start rule's, in the order it makes them),
|
|
17
|
+
// the alternate that matched
|
|
18
|
+
// (`alt`: `p`, `r`, `b`, and its groups `g`, sorted, since they are tags
|
|
19
|
+
// whose order the runtimes' engines keep differently), whether recovery
|
|
20
|
+
// forced the close, and its named counters `n`, sorted by name;
|
|
21
21
|
// - the `tokens` the pass consumed: those its alternate matched (the
|
|
22
22
|
// engine's `o[:oN]` or `c[:cN]`), less the `b` it gave back from their
|
|
23
23
|
// end, which are the next rule's to consume, and none when `b` is more
|
|
24
24
|
// than it recorded, since a close alternate can back up a token it did
|
|
25
25
|
// not record. A token is its name, its value, its source text and its
|
|
26
26
|
// 1-based row and column; a token with no value (the end of the source,
|
|
27
|
-
// a fixed token such as `[
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
// stands;
|
|
27
|
+
// or a fixed token such as `[`) has `null`, and a number carries
|
|
28
|
+
// its source text as its lexeme when that text is a JSON number spelling
|
|
29
|
+
// the value (RFC 8259), the rule the incremental source keeps lexemes by,
|
|
30
|
+
// so a renderer can write it as it stands;
|
|
32
31
|
// - on a close pass, the rule's node when that is a scalar (`value`), a
|
|
33
32
|
// number with the lexeme of the rule's first open token as above;
|
|
34
33
|
// nothing on an open pass, nor for a container.
|
|
@@ -52,6 +51,23 @@
|
|
|
52
51
|
// `AbortFlag` is `ABORTED`; any other engine error is `INPUT_INVALID` with
|
|
53
52
|
// the engine's code and position. A source runs once, as `ParserSource`
|
|
54
53
|
// does: its parser carries the run's subscriber and budget.
|
|
54
|
+
//
|
|
55
|
+
// The engine builds its value as it parses, so a long document's tree
|
|
56
|
+
// holds every record, though nothing reads the value the parse returns.
|
|
57
|
+
// `prune()` empties each array behind the stream: once the sink has seen a
|
|
58
|
+
// close pass whose rule's node is an array, the source empties that array
|
|
59
|
+
// in place, so the tree holds the elements of the passes since the last
|
|
60
|
+
// close on it. That alters the value the engine returns, which this source
|
|
61
|
+
// never reads, and it is sound for a grammar whose passes do not depend on
|
|
62
|
+
// what its arrays hold, so it is a verified capability, as the incremental
|
|
63
|
+
// source is: `test/prune.test.ts` runs every document of every grammar in
|
|
64
|
+
// the devDependencies pruned and not, and `capability.prunable` lists the
|
|
65
|
+
// grammars whose passes and outcomes never differed
|
|
66
|
+
// (rs/src/source/parser_events.rs has the contract's one difference, a
|
|
67
|
+
// token whose value is a container of the tree). A run that prunes is
|
|
68
|
+
// gated on the list, by the grammar's name (`grammar`), and without a
|
|
69
|
+
// listed name it fails with `STREAMABILITY_UNKNOWN` before the parse,
|
|
70
|
+
// having sent nothing; `unverified()` lifts the gate for the suite.
|
|
55
71
|
|
|
56
72
|
import {
|
|
57
73
|
AbortFlag,
|
|
@@ -67,6 +83,7 @@ import {
|
|
|
67
83
|
import type { Flow, ParserEvent, ParserSink, PassAlt, PassToken, RulePass } from '@tabnas/alchemy/shared'
|
|
68
84
|
import type { Rule, RuleDone, Tabnas } from '@tabnas/parser'
|
|
69
85
|
|
|
86
|
+
import { isPrunable } from './capability'
|
|
70
87
|
import { prepare } from './rule-events'
|
|
71
88
|
import { engineFailure } from './source'
|
|
72
89
|
|
|
@@ -77,15 +94,65 @@ export class ParserEventSource {
|
|
|
77
94
|
private sourceLimits: Limits = Limits.default()
|
|
78
95
|
private abortFlag: AbortFlag = new AbortFlag()
|
|
79
96
|
private sourceMetrics: Metrics = new Metrics()
|
|
97
|
+
private grammarName: string | null = null
|
|
98
|
+
private pruning = false
|
|
99
|
+
private isUnverified = false
|
|
80
100
|
private used = false
|
|
81
101
|
|
|
82
|
-
// A source with default limits, its own abort flag and fresh metrics
|
|
83
|
-
// builder methods change each.
|
|
102
|
+
// A source with default limits, its own abort flag and fresh metrics,
|
|
103
|
+
// that prunes nothing; the builder methods change each.
|
|
84
104
|
constructor(parser: Tabnas, text: string) {
|
|
85
105
|
this.parser = parser
|
|
86
106
|
this.text = text
|
|
87
107
|
}
|
|
88
108
|
|
|
109
|
+
// The grammar the parser implements, by the name of the format it reads,
|
|
110
|
+
// as `ParserSource.grammar` takes it. A run that prunes needs a name
|
|
111
|
+
// `capability.prunable` lists; one that does not prune needs no name.
|
|
112
|
+
grammar(name: string): this {
|
|
113
|
+
this.grammarName = name
|
|
114
|
+
return this
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Empty each array in the engine's tree once the sink has seen a close
|
|
118
|
+
// pass on it, so the tree does not hold the whole document. The passes
|
|
119
|
+
// are unchanged for a grammar `capability.prunable` lists, and `run`
|
|
120
|
+
// refuses to prune any other, with `STREAMABILITY_UNKNOWN` before the
|
|
121
|
+
// parse.
|
|
122
|
+
prune(): this {
|
|
123
|
+
this.pruning = true
|
|
124
|
+
return this
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Prune whatever the verified list says. This exists for the
|
|
128
|
+
// differential suite that maintains the list and for nothing else: a
|
|
129
|
+
// grammar that reads its arrays back may make other passes once they are
|
|
130
|
+
// emptied, and the run still returns.
|
|
131
|
+
unverified(): this {
|
|
132
|
+
this.isUnverified = true
|
|
133
|
+
return this
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Why the run may not prune, when it may not.
|
|
137
|
+
private gate(): Fail | null {
|
|
138
|
+
if (!this.pruning || this.isUnverified) return null
|
|
139
|
+
const name = this.grammarName
|
|
140
|
+
if (null !== name && isPrunable(name)) return null
|
|
141
|
+
if (null !== name) {
|
|
142
|
+
return new Fail(
|
|
143
|
+
'STREAMABILITY_UNKNOWN',
|
|
144
|
+
`grammar ${JSON.stringify(name)} is not in capability.prunable: the differential suite ` +
|
|
145
|
+
'has not verified that its passes are the same when its arrays are emptied behind ' +
|
|
146
|
+
'the stream; run it without ParserEventSource.prune',
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
return new Fail(
|
|
150
|
+
'STREAMABILITY_UNKNOWN',
|
|
151
|
+
"ParserEventSource.prune needs the grammar's name (ParserEventSource.grammar) to check " +
|
|
152
|
+
'capability.prunable; without one, run it without pruning',
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
89
156
|
limits(limits: Limits): this {
|
|
90
157
|
this.sourceLimits = limits
|
|
91
158
|
return this
|
|
@@ -103,6 +170,14 @@ export class ParserEventSource {
|
|
|
103
170
|
|
|
104
171
|
// Parse, relaying each pass into `sink`, then `end`.
|
|
105
172
|
run(sink: ParserSink): Flow {
|
|
173
|
+
return this.runWithValue(sink).flow
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// `run`, also handing back the value the engine returned: the engine's
|
|
177
|
+
// tree after pruning, when the run prunes. It is not the run's result
|
|
178
|
+
// (the passes are): it exists so a test can measure what pruning left in
|
|
179
|
+
// the tree, and nothing else should read it.
|
|
180
|
+
runWithValue(sink: ParserSink): { flow: Flow; value: unknown } {
|
|
106
181
|
if (this.used) {
|
|
107
182
|
throw new Fail(
|
|
108
183
|
'STREAM_REUSED',
|
|
@@ -111,27 +186,28 @@ export class ParserEventSource {
|
|
|
111
186
|
)
|
|
112
187
|
}
|
|
113
188
|
this.used = true
|
|
189
|
+
const refused = this.gate()
|
|
190
|
+
if (null !== refused) throw refused
|
|
114
191
|
const abort = this.abortFlag
|
|
115
192
|
const stop = new AbortFlag()
|
|
116
|
-
const relay = new Relay(sink, this.sourceLimits.max_scalar_bytes, abort, stop)
|
|
193
|
+
const relay = new Relay(sink, this.sourceLimits.max_scalar_bytes, abort, stop, this.pruning)
|
|
117
194
|
prepare(
|
|
118
195
|
this.parser,
|
|
119
196
|
() => !abort.isAborted() && !stop.isAborted(),
|
|
120
197
|
(rule, done) => relay.onDone(rule, done),
|
|
121
198
|
)
|
|
122
|
-
let parsed: { ok: true } | { ok: false; error: unknown }
|
|
199
|
+
let parsed: { ok: true; value: unknown } | { ok: false; error: unknown }
|
|
123
200
|
try {
|
|
124
|
-
this.parser.parse(this.text)
|
|
125
|
-
parsed = { ok: true }
|
|
201
|
+
parsed = { ok: true, value: this.parser.parse(this.text) }
|
|
126
202
|
} catch (error) {
|
|
127
203
|
parsed = { ok: false, error }
|
|
128
204
|
}
|
|
129
205
|
try {
|
|
130
206
|
const status = relay.status
|
|
131
207
|
if ('failed' === status.type) throw status.error
|
|
132
|
-
if ('stopped' === status.type) return 'stop'
|
|
208
|
+
if ('stopped' === status.type) return { flow: 'stop', value: undefined }
|
|
133
209
|
if (!parsed.ok) throw engineFailure(parsed.error, abort)
|
|
134
|
-
return relay.send({ type: 'end' })
|
|
210
|
+
return { flow: relay.send({ type: 'end' }), value: parsed.value }
|
|
135
211
|
} finally {
|
|
136
212
|
this.sourceMetrics.events += relay.events
|
|
137
213
|
}
|
|
@@ -151,6 +227,8 @@ class Relay {
|
|
|
151
227
|
readonly abort: AbortFlag,
|
|
152
228
|
// Set to make the parse guard cancel the parse.
|
|
153
229
|
readonly stop: AbortFlag,
|
|
230
|
+
// Empty the array a close pass's rule builds, once the sink has seen it.
|
|
231
|
+
readonly prune: boolean,
|
|
154
232
|
) {}
|
|
155
233
|
|
|
156
234
|
send(ev: ParserEvent): Flow {
|
|
@@ -183,6 +261,8 @@ class Relay {
|
|
|
183
261
|
if ('stop' === this.send(ev)) {
|
|
184
262
|
this.status = { type: 'stopped' }
|
|
185
263
|
this.stop.abort()
|
|
264
|
+
} else if (this.prune && !open) {
|
|
265
|
+
emptyArray(rule)
|
|
186
266
|
}
|
|
187
267
|
} catch (error) {
|
|
188
268
|
this.fail(error)
|
|
@@ -190,6 +270,15 @@ class Relay {
|
|
|
190
270
|
}
|
|
191
271
|
}
|
|
192
272
|
|
|
273
|
+
// Empty the array `rule`'s node is, in place, so every holder of it sees
|
|
274
|
+
// it emptied. Every element in it was built by passes the sink has seen,
|
|
275
|
+
// the last of them the close pass just sent, and what those passes carry
|
|
276
|
+
// is the sink's, so nothing sent so far changes.
|
|
277
|
+
function emptyArray(rule: Rule): void {
|
|
278
|
+
const node: unknown = (rule as any).node
|
|
279
|
+
if (Array.isArray(node) && 0 < node.length) node.length = 0
|
|
280
|
+
}
|
|
281
|
+
|
|
193
282
|
// The pass as alchemy's `RulePass`, its sizes held to `max_scalar_bytes`.
|
|
194
283
|
function rulePass(rule: Rule, done: RuleDone, open: boolean, max: number): RulePass {
|
|
195
284
|
const r: any = rule
|
package/src/routers.ts
CHANGED
|
@@ -69,4 +69,10 @@ export const routers: Routers = Object.freeze({
|
|
|
69
69
|
transduceTree(spec: TreeSpec, downstream: Sink): ParserSink {
|
|
70
70
|
return new TransduceTree(spec, downstream)
|
|
71
71
|
},
|
|
72
|
+
|
|
73
|
+
// TransduceTree reads a spec's `:member` and `:text`, and holds a text to
|
|
74
|
+
// the spec's `maxTextBytes` as it grows.
|
|
75
|
+
transduceTreeReadsMemberAndText(): boolean {
|
|
76
|
+
return true
|
|
77
|
+
},
|
|
72
78
|
})
|
package/src/transduce-tree.ts
CHANGED
|
@@ -8,21 +8,35 @@
|
|
|
8
8
|
// alchemy's `TreeSpec` documents what a native must do: each pass is looked
|
|
9
9
|
// up by its rule's name, one lookup per pass whatever the spec's size; an
|
|
10
10
|
// open pass announces its key, then begins its container or yields its
|
|
11
|
-
// scalar from one of its tokens; a close pass
|
|
12
|
-
// yields its
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// the
|
|
18
|
-
//
|
|
11
|
+
// scalar from one of its tokens, then announces its member; a close pass
|
|
12
|
+
// yields its text, when its rule is a `:text` rule, then ends its
|
|
13
|
+
// container, or yields its own value; and a container whose rule is
|
|
14
|
+
// replaced by a rule the spec names a container is one container, so that
|
|
15
|
+
// close ends nothing and the replacing open begins nothing. The state is
|
|
16
|
+
// the count of open containers, whether a close handed its container on,
|
|
17
|
+
// and the text each open `:text` rule has gathered, to which every pass
|
|
18
|
+
// writes its tokens' source text, so the native retains the document's
|
|
19
|
+
// nesting and an open rule's text, and nothing else of its width or
|
|
20
|
+
// length, as the library's step does. alchemy-cli's differential test
|
|
21
|
+
// holds the two to the same bytes.
|
|
22
|
+
//
|
|
23
|
+
// A text is held to the spec's `maxTextBytes` as it grows, at the joins
|
|
24
|
+
// the library's `string-join` makes and in their order: a pass's tokens
|
|
25
|
+
// together, then the innermost text with them, and a closed text with the
|
|
26
|
+
// text around it, each refused past the bound with alchemy's
|
|
27
|
+
// `treeTextRefusal`. The routers say this native reads `:member` and
|
|
28
|
+
// `:text` (routers.ts), which is what lets the lowering hand it a spec
|
|
29
|
+
// that uses either.
|
|
19
30
|
//
|
|
20
31
|
// A pass's events are worked out before the first of them is written, as
|
|
21
32
|
// the library's step returns them together: a pass that fails writes none.
|
|
22
|
-
// The failures are the library's, with its codes and texts
|
|
33
|
+
// The failures are the library's, with its codes and texts, and in its
|
|
34
|
+
// order: an open pass's key, then what it begins or yields, then its
|
|
35
|
+
// tokens' text, then its member; a close pass's text, then what it ends or
|
|
36
|
+
// yields.
|
|
23
37
|
|
|
24
|
-
import { Datum, Ev, Fail, isTreeContainer } from '@tabnas/alchemy/shared'
|
|
25
|
-
import type { Flow, JsonEvent, ParserEvent, ParserSink, RulePass, Sink, TreeSpec } from '@tabnas/alchemy/shared'
|
|
38
|
+
import { Datum, Ev, Fail, isTreeContainer, treeTextRefusal, utf8Bytes } from '@tabnas/alchemy/shared'
|
|
39
|
+
import type { Flow, JsonEvent, ParserEvent, ParserSink, RulePass, Sink, TreeKey, TreeSpec } from '@tabnas/alchemy/shared'
|
|
26
40
|
|
|
27
41
|
// The library's `transduce-tree`, natively, writing to `down`.
|
|
28
42
|
export class TransduceTree implements ParserSink {
|
|
@@ -30,6 +44,10 @@ export class TransduceTree implements ParserSink {
|
|
|
30
44
|
open = 0
|
|
31
45
|
// A close handed its container on to the rule replacing it.
|
|
32
46
|
carry = false
|
|
47
|
+
// The text each open `:text` rule has gathered, the innermost last, and
|
|
48
|
+
// its length in UTF-8 bytes, which the bound is held to.
|
|
49
|
+
readonly texts: string[] = []
|
|
50
|
+
readonly bytes: number[] = []
|
|
33
51
|
|
|
34
52
|
constructor(
|
|
35
53
|
readonly spec: TreeSpec,
|
|
@@ -50,53 +68,121 @@ export class TransduceTree implements ParserSink {
|
|
|
50
68
|
}
|
|
51
69
|
}
|
|
52
70
|
|
|
71
|
+
// The most bytes a text may hold.
|
|
72
|
+
private get max(): number {
|
|
73
|
+
return this.spec.maxTextBytes ?? Infinity
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// The pass's tokens written to the innermost open text, if one is and the
|
|
77
|
+
// pass consumed any, as the library's `transduce-spell` writes them: the
|
|
78
|
+
// tokens' text joined, then written to the innermost, each join held to
|
|
79
|
+
// the bound.
|
|
80
|
+
private spell(pass: RulePass): void {
|
|
81
|
+
if (0 === this.texts.length || 0 === pass.tokens.length) return
|
|
82
|
+
let more = ''
|
|
83
|
+
let n = 0
|
|
84
|
+
for (const token of pass.tokens) {
|
|
85
|
+
more += token.src
|
|
86
|
+
n += utf8Bytes(token.src)
|
|
87
|
+
}
|
|
88
|
+
if (n > this.max) throw treeTextRefusal(n, this.max)
|
|
89
|
+
this.write(more, n)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// `more`, of `n` bytes, written at the end of the innermost open text, as
|
|
93
|
+
// the library's `transduce-write` joins the two, held to the bound.
|
|
94
|
+
private write(more: string, n: number): void {
|
|
95
|
+
const last = this.texts.length - 1
|
|
96
|
+
const len = this.bytes[last] + n
|
|
97
|
+
if (len > this.max) throw treeTextRefusal(len, this.max)
|
|
98
|
+
this.texts[last] += more
|
|
99
|
+
this.bytes[last] = len
|
|
100
|
+
}
|
|
101
|
+
|
|
53
102
|
private openPass(pass: RulePass): Flow {
|
|
54
103
|
const rule = this.spec.rules.get(pass.name)
|
|
55
|
-
if (undefined === rule)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if ('name' in rule.key) key = rule.key.name
|
|
59
|
-
else {
|
|
60
|
-
const token = pass.tokens[rule.key.token]
|
|
61
|
-
if (undefined !== token) {
|
|
62
|
-
if ('string' !== token.val.type) {
|
|
63
|
-
throw typeError(`key: the name must be a string, not ${kind(token.val)}`)
|
|
64
|
-
}
|
|
65
|
-
key = token.val.value
|
|
66
|
-
}
|
|
67
|
-
}
|
|
104
|
+
if (undefined === rule) {
|
|
105
|
+
this.spell(pass)
|
|
106
|
+
return 'continue'
|
|
68
107
|
}
|
|
108
|
+
const key = named(rule.key, pass)
|
|
69
109
|
let then: JsonEvent | undefined
|
|
110
|
+
let begins = false
|
|
70
111
|
if (undefined !== rule.container) {
|
|
71
|
-
if (this.carry)
|
|
72
|
-
|
|
73
|
-
this.open++
|
|
112
|
+
if (!this.carry) {
|
|
113
|
+
begins = true
|
|
74
114
|
then = 'object' === rule.container ? Ev.objectStart : Ev.arrayStart
|
|
75
115
|
}
|
|
76
116
|
} else if (undefined !== rule.scalar && 'value' !== rule.scalar) {
|
|
77
117
|
const token = pass.tokens[rule.scalar.token]
|
|
78
118
|
if (undefined !== token) then = scalar(token.val)
|
|
79
119
|
}
|
|
80
|
-
|
|
81
|
-
|
|
120
|
+
this.spell(pass)
|
|
121
|
+
const member = named(rule.member, pass)
|
|
122
|
+
if (undefined !== rule.container) {
|
|
123
|
+
if (begins) this.open++
|
|
124
|
+
else this.carry = false
|
|
125
|
+
}
|
|
126
|
+
if (true === rule.text) {
|
|
127
|
+
this.texts.push('')
|
|
128
|
+
this.bytes.push(0)
|
|
129
|
+
}
|
|
130
|
+
for (const ev of [
|
|
131
|
+
undefined === key ? undefined : Ev.key(key),
|
|
132
|
+
then,
|
|
133
|
+
undefined === member ? undefined : Ev.key(member),
|
|
134
|
+
]) {
|
|
135
|
+
if (undefined !== ev && 'stop' === this.down.event(ev)) return 'stop'
|
|
136
|
+
}
|
|
137
|
+
return 'continue'
|
|
82
138
|
}
|
|
83
139
|
|
|
84
140
|
private closePass(pass: RulePass): Flow {
|
|
85
141
|
const rule = this.spec.rules.get(pass.name)
|
|
86
|
-
if (undefined === rule)
|
|
87
|
-
|
|
88
|
-
if ('value' === rule.scalar && undefined !== pass.value) return this.down.event(scalar(pass.value))
|
|
142
|
+
if (undefined === rule) {
|
|
143
|
+
this.spell(pass)
|
|
89
144
|
return 'continue'
|
|
90
145
|
}
|
|
91
|
-
|
|
146
|
+
// A `:text` rule's text closes first, and joins the one around it
|
|
147
|
+
// before this pass's own tokens do; it is yielded before anything
|
|
148
|
+
// else, once the rest of the pass is known not to fail.
|
|
149
|
+
let text: string | undefined
|
|
150
|
+
if (true === rule.text) {
|
|
151
|
+
text = this.texts.pop()
|
|
152
|
+
if (undefined === text) throw typeError('top: the vector is empty; there is no last item')
|
|
153
|
+
const n = this.bytes.pop() as number
|
|
154
|
+
if (0 < this.texts.length) this.write(text, n)
|
|
155
|
+
}
|
|
156
|
+
this.spell(pass)
|
|
157
|
+
let then: JsonEvent | undefined
|
|
158
|
+
if (undefined === rule.container) {
|
|
159
|
+
if ('value' === rule.scalar && undefined !== pass.value) then = scalar(pass.value)
|
|
160
|
+
} else if (undefined !== pass.alt && isTreeContainer(this.spec, pass.alt.r)) {
|
|
92
161
|
this.carry = true
|
|
93
|
-
|
|
162
|
+
} else {
|
|
163
|
+
if (0 === this.open) throw typeError('pop: the vector is empty; there is no last item to remove')
|
|
164
|
+
this.open--
|
|
165
|
+
this.carry = false
|
|
166
|
+
then = 'object' === rule.container ? Ev.objectEnd : Ev.arrayEnd
|
|
94
167
|
}
|
|
95
|
-
if (
|
|
96
|
-
this.
|
|
97
|
-
|
|
98
|
-
|
|
168
|
+
if (undefined !== text && 'stop' === this.down.event(Ev.string(text))) return 'stop'
|
|
169
|
+
return undefined === then ? 'continue' : this.down.event(then)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// The member name a field of a rule's entry names for a pass: the string,
|
|
174
|
+
// or the `:val` of the pass's token at that index, nothing when the pass
|
|
175
|
+
// consumed no such token; the library's `key` refuses a name that is not a
|
|
176
|
+
// string.
|
|
177
|
+
function named(field: TreeKey | undefined, pass: RulePass): string | undefined {
|
|
178
|
+
if (undefined === field) return undefined
|
|
179
|
+
if ('name' in field) return field.name
|
|
180
|
+
const token = pass.tokens[field.token]
|
|
181
|
+
if (undefined === token) return undefined
|
|
182
|
+
if ('string' !== token.val.type) {
|
|
183
|
+
throw typeError(`key: the name must be a string, not ${kind(token.val)}`)
|
|
99
184
|
}
|
|
185
|
+
return token.val.value
|
|
100
186
|
}
|
|
101
187
|
|
|
102
188
|
// A value as the library's `scalar` takes it, or its refusal.
|
package/src/transduce.ts
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
// stages as alchemy's `Routers`, for a host to pass to alchemy's `compile`.
|
|
33
33
|
|
|
34
34
|
// This package's version, as package.json declares it.
|
|
35
|
-
export const VERSION = '0.2.
|
|
35
|
+
export const VERSION = '0.2.11'
|
|
36
36
|
|
|
37
37
|
export {
|
|
38
38
|
Datum,
|
|
@@ -78,7 +78,7 @@ export { Guarded } from './guard'
|
|
|
78
78
|
export { ParserSource } from './parser-source'
|
|
79
79
|
export { ParserEventSource } from './parser-event-source'
|
|
80
80
|
export { TransduceTree } from './transduce-tree'
|
|
81
|
-
export { isTreeContainer, parserEventFromJSON, parserEventToJSON } from '@tabnas/alchemy/shared'
|
|
81
|
+
export { isTreeContainer, parserEventFromJSON, parserEventToJSON, treeTextRefusal } from '@tabnas/alchemy/shared'
|
|
82
82
|
export type {
|
|
83
83
|
ParserEvent,
|
|
84
84
|
ParserSink,
|
|
@@ -93,7 +93,7 @@ export type {
|
|
|
93
93
|
} from '@tabnas/alchemy/shared'
|
|
94
94
|
export { DEFAULT_CHUNK_BYTES, LineFormat, LinesSource } from './lines'
|
|
95
95
|
export type { LinesChunk, LinesInput, LinesWriter } from './lines'
|
|
96
|
-
export { INCREMENTAL, capability, isIncremental } from './capability'
|
|
96
|
+
export { INCREMENTAL, PRUNABLE, capability, isIncremental, isPrunable } from './capability'
|
|
97
97
|
export { Cell, Schema, Table, boundColumn, columnFromMeta } from '@tabnas/alchemy/shared'
|
|
98
98
|
export type {
|
|
99
99
|
BoundColumn,
|