@bablr/agast-helpers 0.5.3 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/builders.js +69 -215
- package/lib/path.js +921 -14
- package/lib/print.js +91 -70
- package/lib/shorthand.js +11 -31
- package/lib/stream.js +201 -62
- package/lib/symbols.js +4 -11
- package/lib/template.js +83 -69
- package/lib/tree.js +390 -297
- package/package.json +6 -1
package/lib/tree.js
CHANGED
|
@@ -1,74 +1,121 @@
|
|
|
1
1
|
import { Coroutine } from '@bablr/coroutine';
|
|
2
2
|
import emptyStack from '@iter-tools/imm-stack';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
nodeFlags,
|
|
5
|
+
buildReferenceTag,
|
|
6
|
+
buildNullTag,
|
|
7
|
+
buildOpenNodeTag,
|
|
8
|
+
buildLiteralTag,
|
|
9
|
+
buildCloseNodeTag,
|
|
10
|
+
tokenFlags,
|
|
11
|
+
} from './builders.js';
|
|
4
12
|
import {
|
|
5
13
|
printPrettyCSTML as printPrettyCSTMLFromStream,
|
|
6
14
|
printCSTML as printCSTMLFromStream,
|
|
15
|
+
printSource as printSourceFromStream,
|
|
7
16
|
getStreamIterator,
|
|
8
|
-
StreamIterable,
|
|
9
17
|
} from './stream.js';
|
|
10
18
|
import {
|
|
11
19
|
DoctypeTag,
|
|
12
20
|
OpenNodeTag,
|
|
13
21
|
CloseNodeTag,
|
|
14
|
-
OpenFragmentTag,
|
|
15
|
-
CloseFragmentTag,
|
|
16
22
|
ReferenceTag,
|
|
17
23
|
ShiftTag,
|
|
18
24
|
GapTag,
|
|
19
25
|
NullTag,
|
|
20
|
-
|
|
26
|
+
ArrayInitializerTag,
|
|
21
27
|
LiteralTag,
|
|
22
28
|
EmbeddedNode,
|
|
23
29
|
} from './symbols.js';
|
|
24
30
|
import * as btree from './btree.js';
|
|
25
|
-
import * as sym from './symbols.js';
|
|
26
31
|
export * from './builders.js';
|
|
27
32
|
export * from './print.js';
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
import {
|
|
34
|
+
add,
|
|
35
|
+
get,
|
|
36
|
+
TagPath,
|
|
37
|
+
Path,
|
|
38
|
+
isFragmentNode,
|
|
39
|
+
isNullNode,
|
|
40
|
+
isGapNode,
|
|
41
|
+
getOpenTag,
|
|
42
|
+
getCloseTag,
|
|
43
|
+
} from './path.js';
|
|
44
|
+
|
|
45
|
+
export { add, get, isFragmentNode, isNullNode, isGapNode, getOpenTag, getCloseTag };
|
|
46
|
+
|
|
47
|
+
export const buildToken = (language, type, value, attributes = {}) => {
|
|
48
|
+
return treeFromStreamSync([
|
|
49
|
+
buildOpenNodeTag(tokenFlags, language, type, attributes),
|
|
50
|
+
buildLiteralTag(value),
|
|
51
|
+
buildCloseNodeTag(),
|
|
52
|
+
]);
|
|
53
|
+
};
|
|
30
54
|
|
|
31
55
|
const isString = (str) => typeof str === 'string';
|
|
32
56
|
|
|
33
57
|
const { isArray } = Array;
|
|
58
|
+
const { freeze } = Object;
|
|
59
|
+
|
|
60
|
+
export const parseReference = (str) => {
|
|
61
|
+
let {
|
|
62
|
+
1: name,
|
|
63
|
+
2: isArray,
|
|
64
|
+
3: index,
|
|
65
|
+
4: expressionToken,
|
|
66
|
+
5: hasGapToken,
|
|
67
|
+
} = /^\s*([.#@]|[a-zA-Z]+)\s*(\[\s*(\d+\s*)?\])?\s*(\+)?(\$)?\s*$/.exec(str);
|
|
68
|
+
|
|
69
|
+
let flags = {
|
|
70
|
+
expression: !!expressionToken,
|
|
71
|
+
hasGap: !!hasGapToken,
|
|
72
|
+
};
|
|
34
73
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const { properties } = node;
|
|
39
|
-
const { 1: name, 2: index } = /^([^\.]+|\.)(?:\.(\d+))?/.exec(path) || [];
|
|
40
|
-
|
|
41
|
-
if (!hasOwn(properties, name)) {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
74
|
+
index = index ? parseInt(index, 10) : null;
|
|
75
|
+
isArray = !!isArray;
|
|
76
|
+
name = name || null;
|
|
44
77
|
|
|
45
|
-
|
|
46
|
-
return btree.getAt(parseInt(index, 10), properties[name]);
|
|
47
|
-
} else {
|
|
48
|
-
return properties[name];
|
|
49
|
-
}
|
|
78
|
+
return buildReferenceTag(name, isArray, flags, index);
|
|
50
79
|
};
|
|
51
80
|
|
|
52
|
-
export const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
81
|
+
export const mergeReferences = (outer, inner) => {
|
|
82
|
+
let {
|
|
83
|
+
name,
|
|
84
|
+
isArray,
|
|
85
|
+
index,
|
|
86
|
+
flags: { expression, hasGap },
|
|
87
|
+
} = outer.value;
|
|
88
|
+
|
|
89
|
+
if (
|
|
90
|
+
name != null &&
|
|
91
|
+
name !== '.' &&
|
|
92
|
+
inner.value.name != null &&
|
|
93
|
+
inner.value.name !== '.' &&
|
|
94
|
+
name !== inner.value.name
|
|
95
|
+
)
|
|
96
|
+
throw new Error();
|
|
97
|
+
|
|
98
|
+
isArray = isArray || inner.value.isArray;
|
|
99
|
+
expression = !!(expression || inner.value.flags.expression);
|
|
100
|
+
hasGap = !!(hasGap || inner.value.flags.hasGap);
|
|
101
|
+
name = name === '.' ? inner.value.name : name;
|
|
102
|
+
|
|
103
|
+
return buildReferenceTag(name, isArray, { expression, hasGap }, index);
|
|
104
|
+
};
|
|
59
105
|
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
}
|
|
106
|
+
export const isEmptyReference = (ref) => {
|
|
107
|
+
let { name, isArray, flags } = ref.value;
|
|
108
|
+
return name === '.' && !isArray && !(flags.expression || flags.hasGap);
|
|
64
109
|
};
|
|
65
110
|
|
|
66
|
-
function* __treeFromStream(
|
|
111
|
+
function* __treeFromStream(tags, options) {
|
|
67
112
|
let path = null;
|
|
68
113
|
let rootPath = null;
|
|
69
114
|
let held = null;
|
|
70
115
|
let doctype = null;
|
|
71
|
-
const co = new Coroutine(getStreamIterator(
|
|
116
|
+
const co = new Coroutine(getStreamIterator(tags));
|
|
117
|
+
const expressionsCo = new Coroutine(getStreamIterator(options.expressions || []));
|
|
118
|
+
let reference = null;
|
|
72
119
|
|
|
73
120
|
for (;;) {
|
|
74
121
|
co.advance();
|
|
@@ -94,95 +141,179 @@ function* __treeFromStream(tokens) {
|
|
|
94
141
|
throw new Error('cannot eat this type of tag while holding');
|
|
95
142
|
}
|
|
96
143
|
|
|
144
|
+
let suppressTag = false;
|
|
145
|
+
|
|
97
146
|
switch (tag.type) {
|
|
98
147
|
case LiteralTag:
|
|
99
|
-
case
|
|
100
|
-
|
|
101
|
-
|
|
148
|
+
case CloseNodeTag: {
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
case ReferenceTag: {
|
|
153
|
+
reference = tag;
|
|
154
|
+
suppressTag = true;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
case ArrayInitializerTag: {
|
|
159
|
+
add(path.node, reference, []);
|
|
160
|
+
suppressTag = true;
|
|
161
|
+
reference = null;
|
|
102
162
|
break;
|
|
103
163
|
}
|
|
104
164
|
|
|
105
165
|
case NullTag:
|
|
106
166
|
case GapTag: {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
167
|
+
if (!path) {
|
|
168
|
+
return buildStubNode(tag);
|
|
169
|
+
}
|
|
110
170
|
|
|
111
|
-
|
|
171
|
+
const isGap = tag.type === GapTag;
|
|
112
172
|
|
|
113
|
-
|
|
173
|
+
if (path.parent && reference.type !== ReferenceTag) throw new Error();
|
|
174
|
+
|
|
175
|
+
let node = createNode(tag);
|
|
176
|
+
|
|
177
|
+
if (isGap) {
|
|
178
|
+
if (held) {
|
|
179
|
+
node = held;
|
|
180
|
+
add(path.node, reference, node);
|
|
181
|
+
suppressTag = true;
|
|
182
|
+
} else if (!expressionsCo.done) {
|
|
183
|
+
expressionsCo.advance();
|
|
184
|
+
|
|
185
|
+
let outerReference = reference;
|
|
186
|
+
|
|
187
|
+
if (!expressionsCo.done) {
|
|
188
|
+
node =
|
|
189
|
+
node == null
|
|
190
|
+
? buildStubNode(buildNullTag())
|
|
191
|
+
: expressionsCo.value == null
|
|
192
|
+
? buildStubNode(buildNullTag())
|
|
193
|
+
: expressionsCo.value;
|
|
194
|
+
suppressTag = true;
|
|
195
|
+
|
|
196
|
+
if (isFragmentNode(node)) {
|
|
197
|
+
const parentNode = path.node;
|
|
198
|
+
|
|
199
|
+
let reference;
|
|
200
|
+
|
|
201
|
+
for (const tag of btree.traverse(node.children)) {
|
|
202
|
+
switch (tag.type) {
|
|
203
|
+
case DoctypeTag: {
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
case OpenNodeTag:
|
|
207
|
+
case CloseNodeTag: {
|
|
208
|
+
if (!tag.value.type) {
|
|
209
|
+
break;
|
|
210
|
+
} else {
|
|
211
|
+
throw new Error();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
case ReferenceTag:
|
|
216
|
+
// combine tags for .
|
|
217
|
+
|
|
218
|
+
reference = tag;
|
|
219
|
+
break;
|
|
220
|
+
|
|
221
|
+
case ArrayInitializerTag: {
|
|
222
|
+
add(parentNode, mergeReferences(outerReference, reference), []);
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
case EmbeddedNode: {
|
|
227
|
+
add(parentNode, mergeReferences(outerReference, reference), tag.value);
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
case GapTag: {
|
|
232
|
+
const resolvedNode = get(reference, node);
|
|
233
|
+
add(parentNode, mergeReferences(outerReference, reference), resolvedNode);
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
case NullTag: {
|
|
238
|
+
add(parentNode, mergeReferences(outerReference, reference), null);
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
default:
|
|
243
|
+
throw new Error();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
if (path.node.flags.token) {
|
|
248
|
+
throw new Error('not implemented');
|
|
249
|
+
}
|
|
250
|
+
add(path.node, reference, node);
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
if (!path.node.flags.token) {
|
|
254
|
+
add(path.node, reference, node);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
114
259
|
|
|
260
|
+
reference = null;
|
|
115
261
|
held = isGap ? null : held;
|
|
116
|
-
path = { parent: path, node, depth: (path.depth || -1) + 1 };
|
|
117
|
-
|
|
118
|
-
add(parentNode, ref, node);
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
case ShiftTag: {
|
|
123
|
-
const { children, properties } = path.node;
|
|
124
|
-
|
|
125
|
-
const ref = arrayLast(children);
|
|
126
|
-
let node = properties[ref.value.name];
|
|
127
262
|
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
properties[ref.value.name].pop();
|
|
131
|
-
} else {
|
|
132
|
-
properties[ref.value.name] = null;
|
|
263
|
+
if (!path.node.flags.token) {
|
|
264
|
+
path = { parent: path, node, depth: (path.depth || -1) + 1, arrays: new Set() };
|
|
133
265
|
}
|
|
134
266
|
|
|
135
|
-
held = node;
|
|
136
267
|
break;
|
|
137
268
|
}
|
|
138
269
|
|
|
139
|
-
case
|
|
140
|
-
|
|
270
|
+
// case ShiftTag: {
|
|
271
|
+
// const { children, properties } = path.node;
|
|
141
272
|
|
|
142
|
-
|
|
273
|
+
// let property = properties[ref.value.name];
|
|
274
|
+
// let node;
|
|
143
275
|
|
|
144
|
-
|
|
276
|
+
// if (ref.value.isArray) {
|
|
277
|
+
// ({ node } = btree.getAt(-1, property));
|
|
278
|
+
// properties[ref.value.name].pop();
|
|
279
|
+
// } else {
|
|
280
|
+
// ({ node } = property);
|
|
281
|
+
// properties[ref.value.name] = null;
|
|
282
|
+
// }
|
|
145
283
|
|
|
146
|
-
|
|
284
|
+
// held = node;
|
|
285
|
+
// break;
|
|
286
|
+
// }
|
|
147
287
|
|
|
148
|
-
|
|
288
|
+
case OpenNodeTag: {
|
|
289
|
+
if (path) {
|
|
290
|
+
const node = createNode(tag);
|
|
149
291
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
throw new Error('Nodes must follow references');
|
|
292
|
+
if (path) {
|
|
293
|
+
add(path.node, reference, node);
|
|
294
|
+
reference = null;
|
|
154
295
|
}
|
|
155
296
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
add(parentNode, ref, node);
|
|
297
|
+
path = { parent: path, node, depth: (path ? path.depth : -1) + 1, arrays: new Set() };
|
|
159
298
|
} else {
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
case OpenFragmentTag: {
|
|
167
|
-
const { flags } = tag.value;
|
|
168
|
-
|
|
169
|
-
const { attributes } = doctype.value;
|
|
170
|
-
const language = attributes['bablr-language'];
|
|
299
|
+
const { language, type, flags, attributes } = tag.value;
|
|
171
300
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
language,
|
|
175
|
-
type: null,
|
|
176
|
-
children: [],
|
|
177
|
-
properties: {},
|
|
178
|
-
attributes,
|
|
179
|
-
});
|
|
301
|
+
const attributes_ = doctype?.value.attributes ?? attributes;
|
|
302
|
+
const language_ = attributes?.['bablrLanguage'] ?? language;
|
|
180
303
|
|
|
181
|
-
|
|
304
|
+
const node = {
|
|
305
|
+
flags,
|
|
306
|
+
language: language_,
|
|
307
|
+
type,
|
|
308
|
+
children: [],
|
|
309
|
+
properties: {},
|
|
310
|
+
attributes: attributes_,
|
|
311
|
+
};
|
|
182
312
|
|
|
183
|
-
|
|
313
|
+
path = { parent: null, node, depth: 0, arrays: new Set() };
|
|
184
314
|
|
|
185
|
-
|
|
315
|
+
rootPath = path;
|
|
316
|
+
}
|
|
186
317
|
|
|
187
318
|
break;
|
|
188
319
|
}
|
|
@@ -192,7 +323,9 @@ function* __treeFromStream(tokens) {
|
|
|
192
323
|
}
|
|
193
324
|
}
|
|
194
325
|
|
|
195
|
-
|
|
326
|
+
if (!suppressTag) {
|
|
327
|
+
path.node.children = btree.push(path.node.children, tag);
|
|
328
|
+
}
|
|
196
329
|
|
|
197
330
|
switch (tag.type) {
|
|
198
331
|
case NullTag:
|
|
@@ -200,10 +333,14 @@ function* __treeFromStream(tokens) {
|
|
|
200
333
|
case CloseNodeTag: {
|
|
201
334
|
const completedNode = path.node;
|
|
202
335
|
|
|
203
|
-
|
|
336
|
+
if (!(tag.type === GapTag && completedNode.flags.token)) {
|
|
337
|
+
finalizeNode(completedNode);
|
|
338
|
+
}
|
|
204
339
|
|
|
205
|
-
if (
|
|
206
|
-
|
|
340
|
+
if (tag.type === GapTag) {
|
|
341
|
+
if (path && completedNode.type === null && completedNode.flags.token) {
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
207
344
|
}
|
|
208
345
|
|
|
209
346
|
path = path.parent;
|
|
@@ -212,17 +349,25 @@ function* __treeFromStream(tokens) {
|
|
|
212
349
|
}
|
|
213
350
|
}
|
|
214
351
|
|
|
352
|
+
if (path && path.node.type) {
|
|
353
|
+
throw new Error('imbalanced tag stack');
|
|
354
|
+
}
|
|
355
|
+
|
|
215
356
|
return rootPath.node;
|
|
216
357
|
}
|
|
217
358
|
|
|
218
|
-
export const
|
|
359
|
+
export const buildNullNode = () => {
|
|
360
|
+
return treeFromStreamSync([buildNullTag()]);
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
export const treeFromStream = (tags, options = {}) => __treeFromStream(tags, options);
|
|
219
364
|
|
|
220
|
-
export const treeFromStreamSync = (tokens) => {
|
|
221
|
-
return evaluateReturnSync(treeFromStream(tokens));
|
|
365
|
+
export const treeFromStreamSync = (tokens, options = {}) => {
|
|
366
|
+
return evaluateReturnSync(treeFromStream(tokens, options));
|
|
222
367
|
};
|
|
223
368
|
|
|
224
|
-
export const treeFromStreamAsync = async (tokens) => {
|
|
225
|
-
return evaluateReturnAsync(treeFromStream(tokens));
|
|
369
|
+
export const treeFromStreamAsync = async (tokens, options = {}) => {
|
|
370
|
+
return evaluateReturnAsync(treeFromStream(tokens, options));
|
|
226
371
|
};
|
|
227
372
|
|
|
228
373
|
export const evaluateReturnSync = (generator) => {
|
|
@@ -243,20 +388,28 @@ export const evaluateReturnAsync = async (generator) => {
|
|
|
243
388
|
return co.value;
|
|
244
389
|
};
|
|
245
390
|
|
|
246
|
-
export const streamFromTree = (rootNode) => __streamFromTree(rootNode);
|
|
391
|
+
export const streamFromTree = (rootNode, options = {}) => __streamFromTree(rootNode, options);
|
|
247
392
|
|
|
248
393
|
export const isEmpty = (node) => {
|
|
249
394
|
const { properties } = node;
|
|
250
395
|
|
|
251
|
-
|
|
252
|
-
|
|
396
|
+
let ref = null;
|
|
397
|
+
|
|
398
|
+
for (const tag of btree.traverse(node.children)) {
|
|
399
|
+
switch (tag.type) {
|
|
253
400
|
case ReferenceTag: {
|
|
254
|
-
const { name } =
|
|
401
|
+
const { name } = tag.value;
|
|
402
|
+
|
|
403
|
+
ref = tag;
|
|
255
404
|
|
|
256
405
|
if (properties[name]) {
|
|
257
|
-
const
|
|
406
|
+
const property = properties[name];
|
|
258
407
|
|
|
259
|
-
if (
|
|
408
|
+
if (
|
|
409
|
+
property != null ||
|
|
410
|
+
(isArray(property) && property.length) ||
|
|
411
|
+
!isNullNode(property.node)
|
|
412
|
+
) {
|
|
260
413
|
return false;
|
|
261
414
|
}
|
|
262
415
|
}
|
|
@@ -264,7 +417,7 @@ export const isEmpty = (node) => {
|
|
|
264
417
|
}
|
|
265
418
|
|
|
266
419
|
case EmbeddedNode: {
|
|
267
|
-
if (
|
|
420
|
+
if (ref.value.name === '@') {
|
|
268
421
|
return false;
|
|
269
422
|
}
|
|
270
423
|
break;
|
|
@@ -278,93 +431,37 @@ export const isEmpty = (node) => {
|
|
|
278
431
|
return true;
|
|
279
432
|
};
|
|
280
433
|
|
|
281
|
-
const symbolTypeFor = (type) => {
|
|
282
|
-
// prettier-ignore
|
|
283
|
-
switch (type) {
|
|
284
|
-
case NullTag: return sym.null;
|
|
285
|
-
case GapTag: return sym.gap;
|
|
286
|
-
default: throw new Error();
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
|
|
290
434
|
export const buildStubNode = (tag) => {
|
|
291
435
|
return freeze({
|
|
292
436
|
flags: nodeFlags,
|
|
293
437
|
language: null,
|
|
294
|
-
type:
|
|
438
|
+
type: null,
|
|
295
439
|
children: freeze([tag]),
|
|
296
440
|
properties: freeze({}),
|
|
297
441
|
attributes: freeze({}),
|
|
298
442
|
});
|
|
299
443
|
};
|
|
300
444
|
|
|
301
|
-
function* __streamFromTree(rootNode) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
let stack = emptyStack.push(rootNode);
|
|
307
|
-
const resolver = new Resolver();
|
|
308
|
-
|
|
309
|
-
stack: while (stack.size) {
|
|
310
|
-
const node = stack.value;
|
|
311
|
-
const { children } = node;
|
|
312
|
-
|
|
313
|
-
while (true) {
|
|
314
|
-
const tag = btree.getAt(resolver.idx, children);
|
|
315
|
-
|
|
316
|
-
if (isArray(tag)) {
|
|
317
|
-
throw new Error();
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
switch (tag.type) {
|
|
321
|
-
case EmbeddedNode: {
|
|
322
|
-
stack = stack.push(tag.value);
|
|
323
|
-
|
|
324
|
-
resolver.advance(tag);
|
|
325
|
-
|
|
326
|
-
continue stack;
|
|
327
|
-
}
|
|
445
|
+
function* __streamFromTree(rootNode, options) {
|
|
446
|
+
const { unshift = false } = options;
|
|
447
|
+
if (!rootNode || !btree.getSum(rootNode.children)) return;
|
|
328
448
|
|
|
329
|
-
|
|
330
|
-
const resolvedPath = resolver.resolve(tag);
|
|
331
|
-
const resolved = get(stack.value, resolvedPath);
|
|
332
|
-
const { isArray: refIsArray } = tag.value;
|
|
449
|
+
let tagPath = TagPath.from(Path.from(rootNode), 0);
|
|
333
450
|
|
|
334
|
-
|
|
451
|
+
let count = 0;
|
|
335
452
|
|
|
336
|
-
|
|
453
|
+
do {
|
|
454
|
+
if (tagPath.tag.type === OpenNodeTag) count++;
|
|
455
|
+
if (tagPath.tag.type === CloseNodeTag) count--;
|
|
337
456
|
|
|
338
|
-
|
|
457
|
+
yield tagPath.tag;
|
|
458
|
+
} while ((tagPath = unshift ? tagPath.nextUnshifted : tagPath.next));
|
|
339
459
|
|
|
340
|
-
|
|
341
|
-
if (isArray(resolved)) throw new Error();
|
|
342
|
-
stack = stack.push(resolved);
|
|
343
|
-
}
|
|
344
|
-
continue stack;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
case GapTag:
|
|
348
|
-
case NullTag:
|
|
349
|
-
case CloseNodeTag:
|
|
350
|
-
case CloseFragmentTag: {
|
|
351
|
-
stack = stack.pop();
|
|
352
|
-
resolver.advance(tag);
|
|
353
|
-
yield tag;
|
|
354
|
-
continue stack;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
default:
|
|
358
|
-
resolver.advance(tag);
|
|
359
|
-
yield tag;
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
460
|
+
if (count !== 0) throw new Error();
|
|
364
461
|
}
|
|
365
462
|
|
|
366
463
|
export const getCooked = (cookable) => {
|
|
367
|
-
if (!cookable || cookable.type
|
|
464
|
+
if (!cookable || isGapNode(cookable.type)) {
|
|
368
465
|
return '';
|
|
369
466
|
}
|
|
370
467
|
|
|
@@ -372,26 +469,31 @@ export const getCooked = (cookable) => {
|
|
|
372
469
|
|
|
373
470
|
let cooked = '';
|
|
374
471
|
|
|
375
|
-
const openTag = getOpenTag(cookable);
|
|
376
|
-
const closeTag = getCloseTag(cookable);
|
|
472
|
+
// const openTag = getOpenTag(cookable);
|
|
473
|
+
// const closeTag = getCloseTag(cookable);
|
|
474
|
+
|
|
475
|
+
let reference = null;
|
|
377
476
|
|
|
378
477
|
for (const tag of btree.traverse(children)) {
|
|
379
478
|
switch (tag.type) {
|
|
380
479
|
case ReferenceTag: {
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
case EmbeddedNode: {
|
|
385
|
-
const { flags, attributes } = tag.value;
|
|
480
|
+
const { name } = tag.value;
|
|
386
481
|
|
|
387
|
-
if (!(
|
|
482
|
+
if (!(name === '#' || name === '@')) {
|
|
388
483
|
throw new Error('cookable nodes must not contain other nodes');
|
|
389
484
|
}
|
|
390
485
|
|
|
391
|
-
|
|
486
|
+
reference = tag;
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
case EmbeddedNode: {
|
|
491
|
+
const { attributes } = tag.value;
|
|
492
|
+
|
|
493
|
+
if (reference.value.name === '@') {
|
|
392
494
|
const { cooked: cookedValue } = attributes;
|
|
393
495
|
|
|
394
|
-
if (!
|
|
496
|
+
if (!isString(cookedValue))
|
|
395
497
|
throw new Error('cannot cook string: it contains uncooked escapes');
|
|
396
498
|
|
|
397
499
|
cooked += cookedValue;
|
|
@@ -430,74 +532,49 @@ export const printPrettyCSTML = (rootNode, options = {}) => {
|
|
|
430
532
|
return printPrettyCSTMLFromStream(streamFromTree(rootNode), options);
|
|
431
533
|
};
|
|
432
534
|
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (!rootNode) return '';
|
|
437
|
-
|
|
438
|
-
let node = rootNode;
|
|
439
|
-
|
|
440
|
-
if (node instanceof Promise) {
|
|
441
|
-
printed += '$Promise';
|
|
442
|
-
} else {
|
|
443
|
-
for (const child of btree.traverse(node.children)) {
|
|
444
|
-
if (child.type === LiteralTag) {
|
|
445
|
-
printed += child.value;
|
|
446
|
-
resolver.advance(child);
|
|
447
|
-
} else if (child.type === EmbeddedNode) {
|
|
448
|
-
resolver.advance(child);
|
|
449
|
-
printed += __printSource(child.value, resolver);
|
|
450
|
-
} else if (child.type === ReferenceTag) {
|
|
451
|
-
const resolvedPath = resolver.resolve(child);
|
|
452
|
-
const resolvedNode = get(node, resolvedPath);
|
|
453
|
-
|
|
454
|
-
resolver.advance(child);
|
|
455
|
-
if (resolvedNode) {
|
|
456
|
-
if (!isArray(resolvedNode)) {
|
|
457
|
-
printed += __printSource(resolvedNode, resolver);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
} else {
|
|
461
|
-
resolver.advance(child);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
return printed;
|
|
535
|
+
export const printSource = (rootNode) => {
|
|
536
|
+
return printSourceFromStream(streamFromTree(rootNode, { unshift: true }));
|
|
467
537
|
};
|
|
468
538
|
|
|
469
|
-
export const printSource = (rootNode) => __printSource(rootNode);
|
|
470
|
-
|
|
471
539
|
export const sourceTextFor = printSource;
|
|
472
540
|
|
|
473
|
-
export const getOpenTag = (node) => {
|
|
474
|
-
const tag = btree.getAt(node.type ? 0 : 1, node.children);
|
|
475
|
-
if (tag.type === NullTag) return null;
|
|
476
|
-
if (tag && tag.type !== OpenNodeTag && tag.type !== OpenFragmentTag) throw new Error();
|
|
477
|
-
return tag;
|
|
478
|
-
};
|
|
479
|
-
|
|
480
|
-
export const getCloseTag = (node) => {
|
|
481
|
-
const { children } = node;
|
|
482
|
-
const tag = btree.getAt(-1, children);
|
|
483
|
-
if (tag.type !== CloseNodeTag) return null;
|
|
484
|
-
return tag;
|
|
485
|
-
};
|
|
486
|
-
|
|
487
541
|
export const getRange = (node) => {
|
|
488
542
|
const { children } = node;
|
|
489
|
-
return children
|
|
543
|
+
return btree.getSum(children) ? [btree.getAt(0, children), btree.getAt(-1, children)] : null;
|
|
490
544
|
};
|
|
491
545
|
|
|
492
546
|
export const createNode = (openTag) => {
|
|
493
|
-
|
|
494
|
-
|
|
547
|
+
if (!openTag || openTag.type === GapTag || openTag.type === NullTag) {
|
|
548
|
+
return {
|
|
549
|
+
flags: nodeFlags,
|
|
550
|
+
language: openTag?.language,
|
|
551
|
+
type: openTag && ([NullTag, GapTag].includes(openTag.type) ? null : openTag.type),
|
|
552
|
+
children: [],
|
|
553
|
+
properties: {},
|
|
554
|
+
attributes: openTag?.attributes || {},
|
|
555
|
+
};
|
|
556
|
+
} else {
|
|
557
|
+
const { flags, language, type, attributes = {} } = openTag.value || {};
|
|
558
|
+
return { flags, language, type, children: [], properties: {}, attributes };
|
|
559
|
+
}
|
|
495
560
|
};
|
|
496
561
|
|
|
497
562
|
export const finalizeNode = (node) => {
|
|
498
|
-
for (const
|
|
499
|
-
if (isArray(
|
|
500
|
-
btree.freeze(
|
|
563
|
+
for (const property of Object.values(node.properties)) {
|
|
564
|
+
if (isArray(property)) {
|
|
565
|
+
btree.freeze(property);
|
|
566
|
+
for (const childProperty of btree.traverse(property)) {
|
|
567
|
+
freeze(childProperty);
|
|
568
|
+
if (childProperty.reference.value.flags.expression) {
|
|
569
|
+
btree.freeze(childProperty.node);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
} else {
|
|
573
|
+
freeze(property);
|
|
574
|
+
|
|
575
|
+
if (property.reference.value.flags.expression) {
|
|
576
|
+
btree.freeze(property.node);
|
|
577
|
+
}
|
|
501
578
|
}
|
|
502
579
|
}
|
|
503
580
|
|
|
@@ -509,11 +586,11 @@ export const finalizeNode = (node) => {
|
|
|
509
586
|
};
|
|
510
587
|
|
|
511
588
|
export const notNull = (node) => {
|
|
512
|
-
return node && node
|
|
589
|
+
return node != null && !isNullNode(node);
|
|
513
590
|
};
|
|
514
591
|
|
|
515
592
|
export const isNull = (node) => {
|
|
516
|
-
return
|
|
593
|
+
return node == null || isNullNode(node);
|
|
517
594
|
};
|
|
518
595
|
|
|
519
596
|
export const branchProperties = (properties) => {
|
|
@@ -521,7 +598,7 @@ export const branchProperties = (properties) => {
|
|
|
521
598
|
|
|
522
599
|
for (const { 0: key, 1: value } of Object.entries(copy)) {
|
|
523
600
|
if (isArray(value)) {
|
|
524
|
-
copy[key] =
|
|
601
|
+
copy[key] = btree.fromValues(value);
|
|
525
602
|
}
|
|
526
603
|
}
|
|
527
604
|
|
|
@@ -534,7 +611,8 @@ export const branchNode = (node) => {
|
|
|
534
611
|
flags,
|
|
535
612
|
language,
|
|
536
613
|
type,
|
|
537
|
-
|
|
614
|
+
// if we always use immutable trees we won't need to do this
|
|
615
|
+
children: btree.fromValues(btree.traverse(children)),
|
|
538
616
|
properties: branchProperties(properties),
|
|
539
617
|
attributes: { ...attributes },
|
|
540
618
|
};
|
|
@@ -549,15 +627,21 @@ export const acceptNode = (node, accepted) => {
|
|
|
549
627
|
};
|
|
550
628
|
|
|
551
629
|
export const getRoot = (node) => {
|
|
552
|
-
return node
|
|
630
|
+
return node == null ? node : isFragmentNode(node) ? node.properties['.'].node : node;
|
|
553
631
|
};
|
|
554
632
|
|
|
555
633
|
export function* traverseProperties(properties) {
|
|
556
634
|
for (const value of Object.values(properties)) {
|
|
557
635
|
if (isArray(value)) {
|
|
558
|
-
|
|
636
|
+
for (let item of btree.traverse(value)) {
|
|
637
|
+
if (isArray(item.node)) {
|
|
638
|
+
yield btree.getAt(-1, item.node);
|
|
639
|
+
} else {
|
|
640
|
+
yield item.node;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
559
643
|
} else {
|
|
560
|
-
yield value;
|
|
644
|
+
yield value.node;
|
|
561
645
|
}
|
|
562
646
|
}
|
|
563
647
|
}
|
|
@@ -591,6 +675,8 @@ export class Resolver {
|
|
|
591
675
|
|
|
592
676
|
this.popped = false;
|
|
593
677
|
|
|
678
|
+
let hadReference = this.reference;
|
|
679
|
+
|
|
594
680
|
switch (tag.type) {
|
|
595
681
|
case ReferenceTag: {
|
|
596
682
|
const { name, isArray } = tag.value;
|
|
@@ -600,62 +686,65 @@ export class Resolver {
|
|
|
600
686
|
|
|
601
687
|
this.reference = tag;
|
|
602
688
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if (isArray) {
|
|
606
|
-
if (state && !state.isArray) throw new Error();
|
|
689
|
+
if (name && name !== '#' && name !== '@') {
|
|
690
|
+
let state = properties.get(name);
|
|
607
691
|
|
|
608
|
-
|
|
692
|
+
if (isArray) {
|
|
693
|
+
if (state && !state.isArray) throw new Error();
|
|
609
694
|
|
|
610
|
-
|
|
611
|
-
} else if (state) {
|
|
612
|
-
throw new Error(`attempted to consume property {name: ${name}} twice`);
|
|
613
|
-
} else {
|
|
614
|
-
state = { count: 1, isArray: false };
|
|
615
|
-
}
|
|
695
|
+
const { count = -1 } = state || {};
|
|
616
696
|
|
|
617
|
-
|
|
697
|
+
state = { count: count + 1, isArray };
|
|
698
|
+
} else if (state) {
|
|
699
|
+
throw new Error(`attempted to consume property {name: ${name}} twice`);
|
|
700
|
+
} else {
|
|
701
|
+
state = { count: 1, isArray: false };
|
|
702
|
+
}
|
|
618
703
|
|
|
619
|
-
|
|
620
|
-
this.states = states.push({ properties: new Map(), idx: 0 });
|
|
704
|
+
properties.set(name, state);
|
|
621
705
|
}
|
|
622
706
|
|
|
623
707
|
break;
|
|
624
708
|
}
|
|
625
709
|
|
|
626
710
|
case EmbeddedNode: {
|
|
627
|
-
if (this.reference) throw new Error();
|
|
711
|
+
if (!this.reference || !['#', '@'].includes(this.reference.value.name)) throw new Error();
|
|
628
712
|
|
|
629
|
-
this.
|
|
630
|
-
|
|
631
|
-
this.states = states.push({ properties: new Map(), idx: 0 });
|
|
713
|
+
// this.states = states.push({ properties: new Map(), idx: 0 });
|
|
632
714
|
break;
|
|
633
715
|
}
|
|
634
716
|
|
|
635
|
-
case OpenNodeTag:
|
|
636
|
-
|
|
717
|
+
case OpenNodeTag: {
|
|
718
|
+
const { reference } = this;
|
|
637
719
|
const { flags } = tag.value;
|
|
638
720
|
const isRootNode = states.size === 1;
|
|
639
721
|
|
|
640
|
-
if (tag.type
|
|
722
|
+
if (tag.value.type) {
|
|
723
|
+
this.states = states.push({ properties: new Map(), idx: 0 });
|
|
724
|
+
}
|
|
641
725
|
|
|
642
|
-
if (
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
726
|
+
if (!tag.value.type && (!isRootNode || this.reference)) throw new Error();
|
|
727
|
+
|
|
728
|
+
if (
|
|
729
|
+
tag.type === OpenNodeTag &&
|
|
730
|
+
((!reference && !isRootNode) ||
|
|
731
|
+
(reference &&
|
|
732
|
+
reference.type !== ReferenceTag &&
|
|
733
|
+
reference.type !== ShiftTag &&
|
|
734
|
+
reference.type !== OpenNodeTag))
|
|
735
|
+
) {
|
|
736
|
+
throw new Error('Invalid location for OpenNodeTag');
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (!isRootNode && !reference) {
|
|
740
|
+
throw new Error();
|
|
652
741
|
}
|
|
653
742
|
|
|
654
743
|
this.reference = null;
|
|
655
744
|
break;
|
|
656
745
|
}
|
|
657
746
|
|
|
658
|
-
case
|
|
747
|
+
case ArrayInitializerTag: {
|
|
659
748
|
if (!this.reference) throw new Error();
|
|
660
749
|
|
|
661
750
|
const { name } = this.reference.value;
|
|
@@ -679,14 +768,15 @@ export class Resolver {
|
|
|
679
768
|
}
|
|
680
769
|
|
|
681
770
|
case NullTag: {
|
|
682
|
-
this.
|
|
771
|
+
if (!this.reference) throw new Error();
|
|
772
|
+
|
|
683
773
|
this.popped = true;
|
|
684
774
|
this.reference = null;
|
|
685
775
|
break;
|
|
686
776
|
}
|
|
687
777
|
|
|
688
778
|
case GapTag: {
|
|
689
|
-
this.
|
|
779
|
+
// if (!this.reference) throw new Error();
|
|
690
780
|
|
|
691
781
|
if (this.held) {
|
|
692
782
|
// this.states = this.states.push(this.held);
|
|
@@ -698,8 +788,9 @@ export class Resolver {
|
|
|
698
788
|
break;
|
|
699
789
|
}
|
|
700
790
|
|
|
701
|
-
case CloseNodeTag:
|
|
702
|
-
|
|
791
|
+
case CloseNodeTag: {
|
|
792
|
+
if (this.reference) throw new Error();
|
|
793
|
+
|
|
703
794
|
this.states = states.pop();
|
|
704
795
|
this.popped = true;
|
|
705
796
|
break;
|
|
@@ -708,6 +799,7 @@ export class Resolver {
|
|
|
708
799
|
case DoctypeTag:
|
|
709
800
|
this.doctype = tag;
|
|
710
801
|
break;
|
|
802
|
+
|
|
711
803
|
case LiteralTag:
|
|
712
804
|
break;
|
|
713
805
|
|
|
@@ -715,23 +807,24 @@ export class Resolver {
|
|
|
715
807
|
throw new Error();
|
|
716
808
|
}
|
|
717
809
|
|
|
810
|
+
if (hadReference && this.reference) throw new Error();
|
|
811
|
+
|
|
718
812
|
return this;
|
|
719
813
|
}
|
|
720
814
|
|
|
721
815
|
resolve(reference) {
|
|
722
|
-
let { name, isArray } = reference.value;
|
|
816
|
+
let { name, isArray, flags } = reference.value;
|
|
723
817
|
const { states } = this;
|
|
724
818
|
const state = states.value.properties.get(name);
|
|
725
|
-
let
|
|
819
|
+
let index = null;
|
|
726
820
|
|
|
727
|
-
if (
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
}
|
|
821
|
+
if (name === '@' || name === '#') return reference;
|
|
822
|
+
|
|
823
|
+
if (isArray && state) {
|
|
824
|
+
index = state?.count || 0;
|
|
732
825
|
}
|
|
733
826
|
|
|
734
|
-
return
|
|
827
|
+
return buildReferenceTag(name, isArray, flags, index);
|
|
735
828
|
}
|
|
736
829
|
|
|
737
830
|
branch() {
|