@tabnas/transduce 0.2.9 → 0.2.10
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/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 +2 -2
- package/dist/transduce.js +3 -2
- package/dist/transduce.js.map +1 -1
- package/package.json +1 -1
- package/src/routers.ts +6 -0
- package/src/transduce-tree.ts +124 -38
- package/src/transduce.ts +2 -2
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.10";
|
|
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,7 +25,7 @@ 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';
|
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.isIncremental = exports.capability = 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.10';
|
|
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,6 +110,7 @@ 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; } });
|
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/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.10'
|
|
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,
|