@bablr/agast-helpers 0.7.1 → 0.9.0
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 +110 -49
- package/lib/object.js +86 -0
- package/lib/path-facade.js +44 -0
- package/lib/path.js +780 -556
- package/lib/print.js +75 -97
- package/lib/shorthand.js +26 -1
- package/lib/stream.js +55 -163
- package/lib/symbols.js +6 -2
- package/lib/tags.js +236 -0
- package/lib/template.js +28 -37
- package/lib/tree.js +391 -213
- package/package.json +11 -6
- package/lib/sumtree.js +0 -62
package/lib/tree.js
CHANGED
|
@@ -7,6 +7,16 @@ import {
|
|
|
7
7
|
buildLiteralTag,
|
|
8
8
|
buildCloseNodeTag,
|
|
9
9
|
tokenFlags,
|
|
10
|
+
buildInitializerTag,
|
|
11
|
+
buildBindingTag,
|
|
12
|
+
buildProperty,
|
|
13
|
+
buildChild,
|
|
14
|
+
buildShiftTag,
|
|
15
|
+
buildGapTag,
|
|
16
|
+
buildBinding,
|
|
17
|
+
buildReference,
|
|
18
|
+
multiFragmentFlags,
|
|
19
|
+
buildPropertyWrapper,
|
|
10
20
|
} from './builders.js';
|
|
11
21
|
import {
|
|
12
22
|
printPrettyCSTML as printPrettyCSTMLFromStream,
|
|
@@ -23,15 +33,21 @@ import {
|
|
|
23
33
|
NullTag,
|
|
24
34
|
InitializerTag,
|
|
25
35
|
LiteralTag,
|
|
26
|
-
|
|
36
|
+
AttributeDefinition,
|
|
37
|
+
BindingTag,
|
|
38
|
+
Property,
|
|
39
|
+
ShiftTag,
|
|
40
|
+
PropertyWrapper,
|
|
27
41
|
} from './symbols.js';
|
|
28
|
-
import * as
|
|
29
|
-
import * as
|
|
42
|
+
import * as BTree from './btree.js';
|
|
43
|
+
import * as Tags from './tags.js';
|
|
30
44
|
export * from './builders.js';
|
|
31
45
|
export * from './print.js';
|
|
32
46
|
import {
|
|
33
|
-
add,
|
|
34
47
|
get,
|
|
48
|
+
getOr,
|
|
49
|
+
has,
|
|
50
|
+
list,
|
|
35
51
|
TagPath,
|
|
36
52
|
Path,
|
|
37
53
|
isFragmentNode,
|
|
@@ -39,14 +55,30 @@ import {
|
|
|
39
55
|
isGapNode,
|
|
40
56
|
getOpenTag,
|
|
41
57
|
getCloseTag,
|
|
42
|
-
|
|
58
|
+
getRoot,
|
|
59
|
+
getRootArray,
|
|
60
|
+
isStubTag,
|
|
61
|
+
wrapperIsFull,
|
|
43
62
|
} from './path.js';
|
|
63
|
+
import { isPlainObject } from './object.js';
|
|
44
64
|
|
|
45
|
-
export {
|
|
65
|
+
export {
|
|
66
|
+
get,
|
|
67
|
+
getOr,
|
|
68
|
+
has,
|
|
69
|
+
list,
|
|
70
|
+
isFragmentNode,
|
|
71
|
+
isNullNode,
|
|
72
|
+
isGapNode,
|
|
73
|
+
getOpenTag,
|
|
74
|
+
getCloseTag,
|
|
75
|
+
getRoot,
|
|
76
|
+
getRootArray,
|
|
77
|
+
};
|
|
46
78
|
|
|
47
|
-
export const buildToken = (
|
|
79
|
+
export const buildToken = (type, value, attributes = {}) => {
|
|
48
80
|
return treeFromStreamSync([
|
|
49
|
-
buildOpenNodeTag(tokenFlags,
|
|
81
|
+
buildOpenNodeTag(tokenFlags, type, attributes),
|
|
50
82
|
buildLiteralTag(value),
|
|
51
83
|
buildCloseNodeTag(),
|
|
52
84
|
]);
|
|
@@ -55,72 +87,36 @@ export const buildToken = (language, type, value, attributes = {}) => {
|
|
|
55
87
|
const isString = (str) => typeof str === 'string';
|
|
56
88
|
|
|
57
89
|
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
|
-
};
|
|
73
|
-
|
|
74
|
-
index = index ? parseInt(index, 10) : null;
|
|
75
|
-
isArray = !!isArray;
|
|
76
|
-
name = name || null;
|
|
77
|
-
|
|
78
|
-
return buildReferenceTag(name, isArray, flags, index);
|
|
79
|
-
};
|
|
90
|
+
const { freeze, hasOwn } = Object;
|
|
80
91
|
|
|
81
92
|
export const mergeReferences = (outer, inner) => {
|
|
82
|
-
let {
|
|
83
|
-
name,
|
|
84
|
-
isArray,
|
|
85
|
-
index,
|
|
86
|
-
flags: { expression, hasGap },
|
|
87
|
-
} = outer.value;
|
|
93
|
+
let { type, name, isArray, index, flags: { expression, hasGap } = {} } = outer;
|
|
88
94
|
|
|
89
95
|
if (
|
|
90
|
-
name != null &&
|
|
91
|
-
|
|
92
|
-
inner.value.name != null &&
|
|
93
|
-
inner.value.name !== '.' &&
|
|
94
|
-
name !== inner.value.name
|
|
96
|
+
(name != null && inner.name != null && name !== inner.name) ||
|
|
97
|
+
(type != null && inner.type != null && type !== inner.type && inner.type !== '.')
|
|
95
98
|
) {
|
|
96
99
|
return inner;
|
|
97
100
|
}
|
|
98
101
|
|
|
99
|
-
isArray = isArray || inner.
|
|
100
|
-
expression = !!(expression || inner.
|
|
101
|
-
hasGap = !!(hasGap || inner.
|
|
102
|
-
name =
|
|
103
|
-
|
|
104
|
-
return buildReferenceTag(name, isArray, { expression, hasGap }, index);
|
|
105
|
-
};
|
|
102
|
+
isArray = isArray || inner.isArray;
|
|
103
|
+
expression = !!(expression || inner.flags.expression);
|
|
104
|
+
hasGap = !!(hasGap || inner.flags.hasGap);
|
|
105
|
+
name = type === '.' ? inner.name : name;
|
|
106
|
+
type = type === '.' ? inner.type : type;
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
let { name, isArray, flags } = ref.value;
|
|
109
|
-
return name === '.' && !isArray && !(flags.expression || flags.hasGap);
|
|
108
|
+
return buildReferenceTag(type, name, isArray, { expression, hasGap }, index).value;
|
|
110
109
|
};
|
|
111
110
|
|
|
112
|
-
export const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (value != null) {
|
|
116
|
-
const { flags, language, type } = openTag.value;
|
|
117
|
-
const attributes = { ...openTag.value.attributes, [key]: value };
|
|
118
|
-
const newOpenTag = buildOpenNodeTag(flags, language, type, attributes);
|
|
111
|
+
export const mergeReferenceTags = (outer, inner) => {
|
|
112
|
+
let value = mergeReferences(outer.value, inner.value);
|
|
119
113
|
|
|
120
|
-
|
|
114
|
+
return buildChild(ReferenceTag, value);
|
|
115
|
+
};
|
|
121
116
|
|
|
122
|
-
|
|
123
|
-
}
|
|
117
|
+
export const isEmptyReference = (ref) => {
|
|
118
|
+
let { type, isArray, flags } = ref.value;
|
|
119
|
+
return type === '.' && !isArray && !(flags.expression || flags.hasGap);
|
|
124
120
|
};
|
|
125
121
|
|
|
126
122
|
function* __treeFromStream(tags, options) {
|
|
@@ -130,7 +126,8 @@ function* __treeFromStream(tags, options) {
|
|
|
130
126
|
let doctype = null;
|
|
131
127
|
const co = new Coroutine(getStreamIterator(tags));
|
|
132
128
|
const expressionsCo = new Coroutine(getStreamIterator(options.expressions || []));
|
|
133
|
-
let
|
|
129
|
+
let referenceTag = null;
|
|
130
|
+
let bindingTag = null;
|
|
134
131
|
|
|
135
132
|
for (;;) {
|
|
136
133
|
co.advance();
|
|
@@ -160,20 +157,30 @@ function* __treeFromStream(tags, options) {
|
|
|
160
157
|
|
|
161
158
|
switch (tag.type) {
|
|
162
159
|
case LiteralTag:
|
|
160
|
+
break;
|
|
161
|
+
|
|
163
162
|
case CloseNodeTag: {
|
|
163
|
+
let { node } = path;
|
|
164
|
+
if (!node.tags[1]) throw new Error();
|
|
164
165
|
break;
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
case ReferenceTag: {
|
|
168
|
-
|
|
169
|
+
referenceTag = tag;
|
|
170
|
+
suppressTag = true;
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
case BindingTag: {
|
|
175
|
+
bindingTag = tag;
|
|
169
176
|
suppressTag = true;
|
|
170
177
|
break;
|
|
171
178
|
}
|
|
172
179
|
|
|
173
180
|
case InitializerTag: {
|
|
174
|
-
add(path.node,
|
|
181
|
+
add(path.node, referenceTag, []);
|
|
175
182
|
suppressTag = true;
|
|
176
|
-
|
|
183
|
+
referenceTag = null;
|
|
177
184
|
break;
|
|
178
185
|
}
|
|
179
186
|
|
|
@@ -185,19 +192,20 @@ function* __treeFromStream(tags, options) {
|
|
|
185
192
|
|
|
186
193
|
const isGap = tag.type === GapTag;
|
|
187
194
|
|
|
188
|
-
if (path.parent &&
|
|
195
|
+
if (path.parent && referenceTag.type !== ReferenceTag) throw new Error();
|
|
189
196
|
|
|
190
197
|
let node = createNode(tag);
|
|
191
198
|
|
|
199
|
+
suppressTag = true;
|
|
200
|
+
|
|
192
201
|
if (isGap) {
|
|
193
202
|
if (held) {
|
|
194
203
|
node = held;
|
|
195
|
-
add(path.node,
|
|
196
|
-
suppressTag = true;
|
|
204
|
+
add(path.node, referenceTag, node, bindingTag);
|
|
197
205
|
} else if (!expressionsCo.done) {
|
|
198
206
|
expressionsCo.advance();
|
|
199
207
|
|
|
200
|
-
let outerReference =
|
|
208
|
+
let outerReference = referenceTag;
|
|
201
209
|
|
|
202
210
|
if (!expressionsCo.done) {
|
|
203
211
|
node =
|
|
@@ -206,20 +214,21 @@ function* __treeFromStream(tags, options) {
|
|
|
206
214
|
: expressionsCo.value == null
|
|
207
215
|
? buildStubNode(buildNullTag())
|
|
208
216
|
: expressionsCo.value;
|
|
209
|
-
suppressTag = true;
|
|
210
217
|
|
|
211
218
|
if (isFragmentNode(node)) {
|
|
212
219
|
const parentNode = path.node;
|
|
213
220
|
|
|
214
|
-
let
|
|
221
|
+
let referenceTag;
|
|
222
|
+
let bindingTag;
|
|
215
223
|
|
|
216
|
-
for (const tag of
|
|
224
|
+
for (const tag of Tags.traverse(node.tags)) {
|
|
217
225
|
switch (tag.type) {
|
|
218
226
|
case DoctypeTag: {
|
|
219
227
|
break;
|
|
220
228
|
}
|
|
221
|
-
|
|
222
|
-
case
|
|
229
|
+
|
|
230
|
+
case OpenNodeTag: {
|
|
231
|
+
referenceTag = bindingTag = null;
|
|
223
232
|
if (!tag.value.type) {
|
|
224
233
|
break;
|
|
225
234
|
} else {
|
|
@@ -228,29 +237,28 @@ function* __treeFromStream(tags, options) {
|
|
|
228
237
|
}
|
|
229
238
|
|
|
230
239
|
case ReferenceTag:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
reference = tag;
|
|
240
|
+
referenceTag = tag;
|
|
234
241
|
break;
|
|
235
242
|
|
|
236
243
|
case InitializerTag: {
|
|
237
|
-
add(parentNode,
|
|
244
|
+
add(parentNode, mergeReferenceTags(outerReference, referenceTag), []);
|
|
238
245
|
break;
|
|
239
246
|
}
|
|
240
247
|
|
|
241
|
-
case
|
|
242
|
-
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
case GapTag: {
|
|
247
|
-
const resolvedNode = getShifted(shift, reference, node);
|
|
248
|
-
add(parentNode, mergeReferences(outerReference, reference), resolvedNode);
|
|
248
|
+
case BindingTag: {
|
|
249
|
+
bindingTag = tag;
|
|
249
250
|
break;
|
|
250
251
|
}
|
|
251
252
|
|
|
253
|
+
case GapTag:
|
|
252
254
|
case NullTag: {
|
|
253
|
-
add(
|
|
255
|
+
add(
|
|
256
|
+
parentNode,
|
|
257
|
+
mergeReferenceTags(outerReference, referenceTag),
|
|
258
|
+
buildStubNode(tag),
|
|
259
|
+
bindingTag,
|
|
260
|
+
);
|
|
261
|
+
referenceTag = bindingTag = null;
|
|
254
262
|
break;
|
|
255
263
|
}
|
|
256
264
|
|
|
@@ -262,28 +270,28 @@ function* __treeFromStream(tags, options) {
|
|
|
262
270
|
if (path.node.flags.token) {
|
|
263
271
|
throw new Error('not implemented');
|
|
264
272
|
}
|
|
265
|
-
add(path.node,
|
|
273
|
+
add(path.node, referenceTag, node, bindingTag ?? buildBindingTag());
|
|
266
274
|
}
|
|
267
275
|
} else {
|
|
268
276
|
if (!path.node.flags.token) {
|
|
269
|
-
add(path.node,
|
|
277
|
+
add(path.node, referenceTag, node, bindingTag ?? buildBindingTag());
|
|
270
278
|
}
|
|
271
279
|
}
|
|
272
280
|
}
|
|
273
281
|
}
|
|
274
282
|
|
|
275
|
-
|
|
283
|
+
referenceTag = null;
|
|
276
284
|
held = isGap ? null : held;
|
|
277
285
|
|
|
278
286
|
if (!path.node.flags.token) {
|
|
279
|
-
path = { parent: path, node, depth: (path.depth
|
|
287
|
+
path = { parent: path, node, depth: (path.depth ?? -1) + 1, arrays: new Set() };
|
|
280
288
|
}
|
|
281
289
|
|
|
282
290
|
break;
|
|
283
291
|
}
|
|
284
292
|
|
|
285
293
|
// case ShiftTag: {
|
|
286
|
-
// const {
|
|
294
|
+
// const { tags, properties } = path.node;
|
|
287
295
|
|
|
288
296
|
// let property = properties[ref.value.name];
|
|
289
297
|
// let node;
|
|
@@ -301,35 +309,21 @@ function* __treeFromStream(tags, options) {
|
|
|
301
309
|
// }
|
|
302
310
|
|
|
303
311
|
case OpenNodeTag: {
|
|
312
|
+
const node = createNode(tag);
|
|
304
313
|
if (path) {
|
|
305
|
-
const node = createNode(tag);
|
|
306
|
-
|
|
307
314
|
if (path) {
|
|
308
|
-
add(path.node,
|
|
309
|
-
|
|
315
|
+
add(path.node, referenceTag, node, bindingTag ?? buildBindingTag());
|
|
316
|
+
referenceTag = null;
|
|
310
317
|
}
|
|
311
318
|
|
|
312
319
|
path = { parent: path, node, depth: (path ? path.depth : -1) + 1, arrays: new Set() };
|
|
313
320
|
} else {
|
|
314
|
-
const { language, type, flags, attributes } = tag.value;
|
|
315
|
-
|
|
316
|
-
const attributes_ = doctype?.value.attributes ?? attributes;
|
|
317
|
-
const language_ = attributes?.['bablrLanguage'] ?? language;
|
|
318
|
-
|
|
319
|
-
const node = {
|
|
320
|
-
flags,
|
|
321
|
-
language: language_,
|
|
322
|
-
type,
|
|
323
|
-
children: [],
|
|
324
|
-
properties: {},
|
|
325
|
-
attributes: attributes_,
|
|
326
|
-
};
|
|
327
|
-
|
|
328
321
|
path = { parent: null, node, depth: 0, arrays: new Set() };
|
|
329
322
|
|
|
330
323
|
rootPath = path;
|
|
331
324
|
}
|
|
332
325
|
|
|
326
|
+
suppressTag = true;
|
|
333
327
|
break;
|
|
334
328
|
}
|
|
335
329
|
|
|
@@ -339,7 +333,7 @@ function* __treeFromStream(tags, options) {
|
|
|
339
333
|
}
|
|
340
334
|
|
|
341
335
|
if (!suppressTag) {
|
|
342
|
-
path.node.
|
|
336
|
+
path.node.tags = Tags.push(path.node.tags, tag);
|
|
343
337
|
}
|
|
344
338
|
|
|
345
339
|
switch (tag.type) {
|
|
@@ -403,41 +397,22 @@ export const evaluateReturnAsync = async (generator) => {
|
|
|
403
397
|
return co.value;
|
|
404
398
|
};
|
|
405
399
|
|
|
406
|
-
export const streamFromTree = (rootNode, options = {}) => __streamFromTree(rootNode, options);
|
|
407
|
-
|
|
408
400
|
export const isEmpty = (node) => {
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
let ref = null;
|
|
412
|
-
|
|
413
|
-
for (const tag of sumtree.traverse(node.children)) {
|
|
401
|
+
for (const tag of Tags.traverseInner(node.tags)) {
|
|
414
402
|
switch (tag.type) {
|
|
415
|
-
case
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
if (properties[name]) {
|
|
421
|
-
const property = properties[name];
|
|
403
|
+
case Property: {
|
|
404
|
+
if (tag.value.reference.type === '@') {
|
|
405
|
+
return false;
|
|
406
|
+
} else {
|
|
407
|
+
const property = tag.value;
|
|
422
408
|
|
|
423
|
-
if (
|
|
424
|
-
property != null ||
|
|
425
|
-
(isArray(property) && property.length) ||
|
|
426
|
-
!isNullNode(property.node)
|
|
427
|
-
) {
|
|
409
|
+
if (!isNullNode(property.node)) {
|
|
428
410
|
return false;
|
|
429
411
|
}
|
|
430
412
|
}
|
|
431
413
|
break;
|
|
432
414
|
}
|
|
433
415
|
|
|
434
|
-
case EmbeddedNode: {
|
|
435
|
-
if (ref.value.name === '@') {
|
|
436
|
-
return false;
|
|
437
|
-
}
|
|
438
|
-
break;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
416
|
case LiteralTag:
|
|
442
417
|
case GapTag:
|
|
443
418
|
return false;
|
|
@@ -446,67 +421,193 @@ export const isEmpty = (node) => {
|
|
|
446
421
|
return true;
|
|
447
422
|
};
|
|
448
423
|
|
|
424
|
+
let buildGapProperty = () =>
|
|
425
|
+
buildProperty(buildReference('.'), buildBinding(), buildStubNode(buildGapTag()));
|
|
426
|
+
|
|
427
|
+
export const buildBounds = (
|
|
428
|
+
openBoundary = BTree.fromValues([buildGapProperty()]),
|
|
429
|
+
closeBoundary = openBoundary,
|
|
430
|
+
) => {
|
|
431
|
+
return freeze([openBoundary, closeBoundary]);
|
|
432
|
+
};
|
|
433
|
+
|
|
449
434
|
export const buildStubNode = (tag) => {
|
|
435
|
+
if (!isStubTag(tag)) throw new Error();
|
|
450
436
|
return freeze({
|
|
451
437
|
flags: nodeFlags,
|
|
452
|
-
language: null,
|
|
453
438
|
type: null,
|
|
454
|
-
|
|
455
|
-
|
|
439
|
+
bounds: buildBounds(null, null),
|
|
440
|
+
tags: Tags.from(tag),
|
|
456
441
|
attributes: freeze({}),
|
|
457
442
|
});
|
|
458
443
|
};
|
|
459
444
|
|
|
460
|
-
|
|
445
|
+
export const streamFromTree = (tree, options = {}) => {
|
|
446
|
+
let rootNode = isPlainObject(tree) ? tree : tree.node;
|
|
447
|
+
|
|
448
|
+
return __streamFromTree(null, rootNode, options);
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
function* __streamFromTree(doctypeTag, rootNode, options) {
|
|
461
452
|
const { unshift = false } = options;
|
|
462
|
-
if (!rootNode || !
|
|
453
|
+
if (!rootNode || !Tags.getSize(rootNode.tags)) return;
|
|
463
454
|
|
|
464
455
|
let tagPath = TagPath.fromNode(rootNode, 0);
|
|
465
456
|
|
|
466
457
|
let count = 0;
|
|
467
458
|
|
|
459
|
+
if (doctypeTag) {
|
|
460
|
+
yield doctypeTag;
|
|
461
|
+
}
|
|
462
|
+
|
|
468
463
|
do {
|
|
469
|
-
if (tagPath.tag.type === OpenNodeTag) count++;
|
|
464
|
+
if (tagPath.tag.type === OpenNodeTag && !tagPath.tag.value.literalValue) count++;
|
|
470
465
|
if (tagPath.tag.type === CloseNodeTag) count--;
|
|
471
466
|
|
|
472
|
-
|
|
467
|
+
if (
|
|
468
|
+
!(
|
|
469
|
+
tagPath.tag.type === AttributeDefinition ||
|
|
470
|
+
(tagPath.tag.type === BindingTag && !tagPath.tag.value.languagePath?.length)
|
|
471
|
+
)
|
|
472
|
+
) {
|
|
473
|
+
yield tagPath.tag;
|
|
474
|
+
}
|
|
473
475
|
} while ((tagPath = unshift ? tagPath.nextUnshifted : tagPath.next));
|
|
474
476
|
|
|
475
477
|
if (count !== 0) throw new Error();
|
|
476
478
|
}
|
|
477
479
|
|
|
480
|
+
export const vcsStreamFromTree = (rootNode) => {
|
|
481
|
+
let rootNode_ = isPlainObject(rootNode) ? rootNode : rootNode.node;
|
|
482
|
+
|
|
483
|
+
return __vcsStreamFromTree(rootNode_);
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
function* __vcsStreamFromTree(rootNode) {
|
|
487
|
+
let stack = null;
|
|
488
|
+
let agastNode = rootNode;
|
|
489
|
+
let depth = 0;
|
|
490
|
+
let nodeShifted = false;
|
|
491
|
+
let i = 0;
|
|
492
|
+
let seenFirstProperty = false;
|
|
493
|
+
let node = Tags.getValues(rootNode.tags);
|
|
494
|
+
|
|
495
|
+
outer: while (node) {
|
|
496
|
+
while (i >= node.length) {
|
|
497
|
+
if (stack) {
|
|
498
|
+
let oldAgastNode = agastNode;
|
|
499
|
+
let hadSeenFirst = seenFirstProperty;
|
|
500
|
+
({ stack, agastNode, node, depth, i, nodeShifted, seenFirstProperty } = stack);
|
|
501
|
+
if (oldAgastNode === agastNode) {
|
|
502
|
+
seenFirstProperty = hadSeenFirst;
|
|
503
|
+
yield buildCloseNodeTag();
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
i++;
|
|
507
|
+
} else {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
let child = node[i];
|
|
513
|
+
|
|
514
|
+
if (isArray(child)) {
|
|
515
|
+
stack = { stack, agastNode, node, depth, i, nodeShifted, seenFirstProperty };
|
|
516
|
+
|
|
517
|
+
yield buildOpenNodeTag(multiFragmentFlags);
|
|
518
|
+
|
|
519
|
+
node = Tags.getValues(child);
|
|
520
|
+
depth++;
|
|
521
|
+
i = 0;
|
|
522
|
+
} else {
|
|
523
|
+
let wrappedTag = child;
|
|
524
|
+
|
|
525
|
+
if (wrappedTag.type === PropertyWrapper) {
|
|
526
|
+
for (let tag of wrappedTag.value.tags) {
|
|
527
|
+
switch (tag.type) {
|
|
528
|
+
case Property: {
|
|
529
|
+
let replaceWithGap = nodeShifted && !seenFirstProperty;
|
|
530
|
+
|
|
531
|
+
if (replaceWithGap) {
|
|
532
|
+
yield buildGapTag();
|
|
533
|
+
|
|
534
|
+
seenFirstProperty = true;
|
|
535
|
+
|
|
536
|
+
break;
|
|
537
|
+
} else {
|
|
538
|
+
stack = { stack, agastNode, node, depth, i, nodeShifted, seenFirstProperty: true };
|
|
539
|
+
node = Tags.getValues(tag.value.node.tags);
|
|
540
|
+
|
|
541
|
+
depth++;
|
|
542
|
+
i = 0;
|
|
543
|
+
agastNode = tag.value.node;
|
|
544
|
+
nodeShifted = wrappedTag.value.tags[0].type === ShiftTag;
|
|
545
|
+
seenFirstProperty = false;
|
|
546
|
+
continue outer;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
default: {
|
|
551
|
+
yield tag;
|
|
552
|
+
|
|
553
|
+
if (tag.type === OpenNodeTag && tag.value.literalValue) {
|
|
554
|
+
({ stack, agastNode, node, depth, i, nodeShifted } = stack);
|
|
555
|
+
seenFirstProperty = true;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
yield wrappedTag;
|
|
564
|
+
|
|
565
|
+
if (wrappedTag.type === OpenNodeTag && wrappedTag.value.literalValue) {
|
|
566
|
+
({ stack, agastNode, node, depth, i, nodeShifted } = stack);
|
|
567
|
+
seenFirstProperty = true;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
i++;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
478
575
|
export const getCooked = (cookable) => {
|
|
479
576
|
if (!cookable || isGapNode(cookable.type)) {
|
|
480
577
|
return '';
|
|
481
578
|
}
|
|
482
579
|
|
|
483
|
-
const
|
|
580
|
+
const tags = cookable.tags || cookable;
|
|
484
581
|
|
|
485
582
|
let cooked = '';
|
|
486
583
|
|
|
487
584
|
// const openTag = getOpenTag(cookable);
|
|
488
585
|
// const closeTag = getCloseTag(cookable);
|
|
489
586
|
|
|
490
|
-
let
|
|
587
|
+
let referenceTag = null;
|
|
491
588
|
|
|
492
|
-
for (
|
|
589
|
+
for (let tag of Tags.traverse(tags)) {
|
|
493
590
|
switch (tag.type) {
|
|
494
591
|
case ReferenceTag: {
|
|
495
|
-
|
|
592
|
+
let { type } = tag.value;
|
|
496
593
|
|
|
497
|
-
if (!(
|
|
594
|
+
if (!(type === '#' || type === '@')) {
|
|
498
595
|
throw new Error('cookable nodes must not contain other nodes');
|
|
499
596
|
}
|
|
500
597
|
|
|
501
|
-
|
|
598
|
+
referenceTag = tag;
|
|
502
599
|
break;
|
|
503
600
|
}
|
|
504
601
|
|
|
505
|
-
case
|
|
506
|
-
|
|
602
|
+
case BindingTag:
|
|
603
|
+
break;
|
|
604
|
+
|
|
605
|
+
case PropertyWrapper: {
|
|
606
|
+
let { node, reference } = tag.value.property;
|
|
607
|
+
let { attributes } = node;
|
|
507
608
|
|
|
508
|
-
if (reference.
|
|
509
|
-
|
|
609
|
+
if (reference.type === '@') {
|
|
610
|
+
let { cooked: cookedValue } = attributes;
|
|
510
611
|
|
|
511
612
|
if (!isString(cookedValue))
|
|
512
613
|
throw new Error('cannot cook string: it contains uncooked escapes');
|
|
@@ -539,45 +640,44 @@ export const getCooked = (cookable) => {
|
|
|
539
640
|
return cooked;
|
|
540
641
|
};
|
|
541
642
|
|
|
542
|
-
export const printCSTML = (
|
|
543
|
-
return printCSTMLFromStream(streamFromTree(
|
|
643
|
+
export const printCSTML = (tree) => {
|
|
644
|
+
return printCSTMLFromStream(streamFromTree(tree));
|
|
544
645
|
};
|
|
545
646
|
|
|
546
|
-
export const printPrettyCSTML = (
|
|
547
|
-
return printPrettyCSTMLFromStream(streamFromTree(
|
|
647
|
+
export const printPrettyCSTML = (tree, options = {}) => {
|
|
648
|
+
return printPrettyCSTMLFromStream(streamFromTree(tree), options);
|
|
548
649
|
};
|
|
549
650
|
|
|
550
|
-
export const printSource = (
|
|
551
|
-
return printSourceFromStream(streamFromTree(
|
|
651
|
+
export const printSource = (tree) => {
|
|
652
|
+
return printSourceFromStream(streamFromTree(tree, { unshift: true }));
|
|
552
653
|
};
|
|
553
654
|
|
|
554
655
|
export const sourceTextFor = printSource;
|
|
555
656
|
|
|
556
657
|
export const getRange = (node) => {
|
|
557
|
-
const {
|
|
658
|
+
const { tags } = node;
|
|
558
659
|
let path = Path.from(node);
|
|
559
|
-
return
|
|
660
|
+
return Tags.getSize(tags) ? [TagPath.from(path, 0), TagPath.from(path, -1)] : null;
|
|
560
661
|
};
|
|
561
662
|
|
|
562
663
|
export const createNode = (openTag) => {
|
|
664
|
+
let bounds = buildBounds();
|
|
665
|
+
let literalTags = [];
|
|
666
|
+
let tags = openTag ? Tags.fromValues([openTag, ...literalTags]) : Tags.fromValues(literalTags);
|
|
667
|
+
let flags, type, attributes;
|
|
668
|
+
|
|
563
669
|
if (!openTag || openTag.type === GapTag || openTag.type === NullTag) {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
type: openTag && ([NullTag, GapTag].includes(openTag.type) ? null : openTag.type),
|
|
568
|
-
children: [],
|
|
569
|
-
properties: {},
|
|
570
|
-
attributes: openTag?.attributes || {},
|
|
571
|
-
};
|
|
670
|
+
flags = nodeFlags;
|
|
671
|
+
type = openTag && ([NullTag, GapTag].includes(openTag.type) ? null : openTag.type);
|
|
672
|
+
attributes = openTag?.attributes || {};
|
|
572
673
|
} else {
|
|
573
|
-
|
|
574
|
-
return { flags, language, type, children: [], properties: {}, attributes };
|
|
674
|
+
({ flags, type, attributes = {} } = openTag.value || {});
|
|
575
675
|
}
|
|
676
|
+
return { flags, type, bounds, tags, attributes };
|
|
576
677
|
};
|
|
577
678
|
|
|
578
679
|
export const finalizeNode = (node) => {
|
|
579
680
|
freeze(node);
|
|
580
|
-
freeze(node.properties);
|
|
581
681
|
freeze(node.attributes);
|
|
582
682
|
return node;
|
|
583
683
|
};
|
|
@@ -590,54 +690,132 @@ export const isNull = (node) => {
|
|
|
590
690
|
return node == null || isNullNode(node);
|
|
591
691
|
};
|
|
592
692
|
|
|
593
|
-
export const
|
|
594
|
-
|
|
693
|
+
export const addProperty = (node, property) => {
|
|
694
|
+
if (!node || !property) throw new Error();
|
|
695
|
+
if (!Object.isFrozen(property)) throw new Error();
|
|
595
696
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
copy[key] = btree.treeFromValues(value);
|
|
599
|
-
}
|
|
697
|
+
if (property.node === null) {
|
|
698
|
+
throw new Error();
|
|
600
699
|
}
|
|
601
700
|
|
|
602
|
-
|
|
603
|
-
|
|
701
|
+
let { node: value, binding, reference } = property;
|
|
702
|
+
|
|
703
|
+
let isArrayInitializer = reference.type !== '_' && isArray(value);
|
|
704
|
+
let isInitializer = value === undefined || isArrayInitializer;
|
|
705
|
+
|
|
706
|
+
if (isArrayInitializer && value.length) throw new Error();
|
|
707
|
+
|
|
708
|
+
if (!isInitializer && property.node.type && !property.binding) {
|
|
709
|
+
throw new Error();
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (reference.type === '_') {
|
|
713
|
+
node.tags = Tags.push(node.tags, value);
|
|
714
|
+
return node;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
let referenceTag = buildChild(ReferenceTag, reference);
|
|
718
|
+
let propertyWrapper;
|
|
719
|
+
|
|
720
|
+
if (isInitializer) {
|
|
721
|
+
let tags = freeze([referenceTag, buildInitializerTag(isArrayInitializer)]);
|
|
722
|
+
|
|
723
|
+
propertyWrapper = buildPropertyWrapper(
|
|
724
|
+
tags,
|
|
725
|
+
buildProperty(reference, null, isArrayInitializer ? [] : null),
|
|
726
|
+
);
|
|
727
|
+
} else {
|
|
728
|
+
let tags = freeze([
|
|
729
|
+
referenceTag,
|
|
730
|
+
buildChild(BindingTag, binding),
|
|
731
|
+
buildChild(Property, property),
|
|
732
|
+
]);
|
|
733
|
+
|
|
734
|
+
propertyWrapper = buildPropertyWrapper(tags, property);
|
|
735
|
+
}
|
|
604
736
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
737
|
+
let lastTag = Tags.getAt(-1, node.tags);
|
|
738
|
+
let unboundProperty = lastTag.type === PropertyWrapper && lastTag.value.tags.length < 3;
|
|
739
|
+
|
|
740
|
+
if (unboundProperty) {
|
|
741
|
+
node.tags = Tags.replaceAt(-1, node.tags, buildChild(PropertyWrapper, propertyWrapper));
|
|
742
|
+
} else {
|
|
743
|
+
node.tags = Tags.push(node.tags, buildChild(PropertyWrapper, propertyWrapper));
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return node;
|
|
615
747
|
};
|
|
616
748
|
|
|
617
|
-
export const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
749
|
+
export const shiftProperty = (node, property) => {
|
|
750
|
+
if (!node || !property) throw new Error();
|
|
751
|
+
if (!property.reference) throw new Error();
|
|
752
|
+
if (!Object.isFrozen(property)) throw new Error();
|
|
753
|
+
|
|
754
|
+
if (property.node === null) {
|
|
755
|
+
property.node = buildNullNode();
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
let { node: value } = property;
|
|
759
|
+
|
|
760
|
+
let isArrayInitializer = isArray(value);
|
|
761
|
+
let isInitializer = value === undefined || isArrayInitializer;
|
|
762
|
+
|
|
763
|
+
if (isInitializer || !property.binding) {
|
|
764
|
+
throw new Error();
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
let existingProperty = Tags.getAt(-1, node.tags);
|
|
768
|
+
|
|
769
|
+
if (!wrapperIsFull(existingProperty)) {
|
|
770
|
+
existingProperty = Tags.getAt(-2, node.tags);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
let existingRef = existingProperty?.value.tags[0];
|
|
774
|
+
|
|
775
|
+
let bindingTag = buildChild(BindingTag, property.binding);
|
|
776
|
+
|
|
777
|
+
let shiftStack = existingProperty?.value.property.node.bounds[0] || BTree.fromValues([]);
|
|
778
|
+
let stackSize = shiftStack && property.reference.flags.expression ? BTree.getSize(shiftStack) : 0;
|
|
779
|
+
|
|
780
|
+
let index = existingRef.type === ReferenceTag ? 1 : existingRef.value.index + 1;
|
|
781
|
+
let height = BTree.getSize(shiftStack) + 1;
|
|
782
|
+
|
|
783
|
+
let shiftTag = stackSize ? buildShiftTag(index, height) : existingRef;
|
|
784
|
+
|
|
785
|
+
let tags = freeze([shiftTag, bindingTag, buildChild(Property, property)]);
|
|
786
|
+
|
|
787
|
+
let lastTag = Tags.getAt(-1, node.tags);
|
|
788
|
+
let unboundProperty = lastTag.type === PropertyWrapper && lastTag.value.tags.length < 3;
|
|
789
|
+
|
|
790
|
+
let propertyWrapper = buildPropertyWrapper(tags, property);
|
|
791
|
+
|
|
792
|
+
if (unboundProperty) {
|
|
793
|
+
node.tags = Tags.replaceAt(-1, node.tags, buildChild(PropertyWrapper, propertyWrapper));
|
|
794
|
+
} else {
|
|
795
|
+
node.tags = Tags.push(node.tags, buildChild(PropertyWrapper, propertyWrapper));
|
|
796
|
+
}
|
|
797
|
+
|
|
622
798
|
return node;
|
|
623
799
|
};
|
|
624
800
|
|
|
625
|
-
export const
|
|
626
|
-
|
|
801
|
+
export const add = (node, referenceTag, value, bindingTag) => {
|
|
802
|
+
let lastChild = Tags.getAt(-1, node.tags);
|
|
803
|
+
let reference = referenceTag.value;
|
|
804
|
+
let binding = bindingTag
|
|
805
|
+
? bindingTag.value
|
|
806
|
+
: lastChild?.type === BindingTag
|
|
807
|
+
? lastChild.value
|
|
808
|
+
: buildBinding();
|
|
809
|
+
return addProperty(node, buildProperty(reference, binding, value));
|
|
627
810
|
};
|
|
628
811
|
|
|
629
|
-
export
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
} else {
|
|
640
|
-
yield value.node;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
}
|
|
812
|
+
export const shift = (node, referenceTag, value, bindingTag) => {
|
|
813
|
+
let lastChild = Tags.getAt(-1, node.tags);
|
|
814
|
+
let reference = referenceTag.value;
|
|
815
|
+
let binding = bindingTag
|
|
816
|
+
? bindingTag.value
|
|
817
|
+
: lastChild.type === BindingTag
|
|
818
|
+
? lastChild.value
|
|
819
|
+
: buildBinding();
|
|
820
|
+
return shiftProperty(node, buildProperty(reference, binding, value));
|
|
821
|
+
};
|