@bablr/agast-helpers 0.4.0 → 0.5.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 +115 -66
- package/lib/path.js +16 -6
- package/lib/print.js +27 -4
- package/lib/shorthand.js +6 -10
- package/lib/stream.js +84 -25
- package/lib/symbols.js +2 -0
- package/lib/template.js +46 -14
- package/lib/tree.js +139 -41
- package/package.json +2 -2
package/lib/builders.js
CHANGED
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
DoctypeTag,
|
|
5
5
|
OpenNodeTag,
|
|
6
6
|
CloseNodeTag,
|
|
7
|
+
OpenFragmentTag,
|
|
8
|
+
CloseFragmentTag,
|
|
7
9
|
ReferenceTag,
|
|
8
10
|
ShiftTag,
|
|
9
11
|
GapTag,
|
|
@@ -17,9 +19,26 @@ import {
|
|
|
17
19
|
} from './symbols.js';
|
|
18
20
|
|
|
19
21
|
const { freeze } = Object;
|
|
22
|
+
const { isArray } = Array;
|
|
20
23
|
|
|
21
24
|
const isObject = (val) => val !== null && typeof value !== 'object';
|
|
22
25
|
|
|
26
|
+
function* relatedNodes(properties) {
|
|
27
|
+
for (const value of Object.values(properties)) {
|
|
28
|
+
if (isArray(value)) {
|
|
29
|
+
yield* btree.traverse(value);
|
|
30
|
+
} else {
|
|
31
|
+
yield value;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const find = (predicate, iterable) => {
|
|
37
|
+
for (const value of iterable) {
|
|
38
|
+
if (predicate(value)) return value;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
23
42
|
export const buildEmbeddedExpression = (expr) => {
|
|
24
43
|
if (!isObject(expr)) return expr;
|
|
25
44
|
return freeze({ type: EmbeddedExpression, value: expr });
|
|
@@ -35,32 +54,28 @@ export const buildEffect = (value) => {
|
|
|
35
54
|
|
|
36
55
|
export const buildWriteEffect = (text, options = {}) => {
|
|
37
56
|
return buildEffect(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}),
|
|
45
|
-
),
|
|
57
|
+
freeze({
|
|
58
|
+
verb: 'write',
|
|
59
|
+
value: buildEmbeddedExpression(
|
|
60
|
+
freeze({ text, options: buildEmbeddedExpression(freeze(options)) }),
|
|
61
|
+
),
|
|
62
|
+
}),
|
|
46
63
|
);
|
|
47
64
|
};
|
|
48
65
|
|
|
49
66
|
export const buildAnsiPushEffect = (spans = '') => {
|
|
50
67
|
return buildEffect(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}),
|
|
58
|
-
),
|
|
68
|
+
freeze({
|
|
69
|
+
verb: 'ansi-push',
|
|
70
|
+
value: buildEmbeddedExpression(
|
|
71
|
+
freeze({ spans: spans === '' ? freeze([]) : freeze(spans.split(' ')) }),
|
|
72
|
+
),
|
|
73
|
+
}),
|
|
59
74
|
);
|
|
60
75
|
};
|
|
61
76
|
|
|
62
77
|
export const buildAnsiPopEffect = () => {
|
|
63
|
-
return buildEffect(
|
|
78
|
+
return buildEffect(freeze({ verb: 'ansi-pop', value: undefined }));
|
|
64
79
|
};
|
|
65
80
|
|
|
66
81
|
export const buildTokenGroup = (tokens) => {
|
|
@@ -75,8 +90,8 @@ export const buildBeginningOfStreamToken = () => {
|
|
|
75
90
|
return freeze({ type: Symbol.for('@bablr/beginning-of-stream'), value: undefined });
|
|
76
91
|
};
|
|
77
92
|
|
|
78
|
-
export const buildReferenceTag = (name, isArray = false) => {
|
|
79
|
-
return freeze({ type: ReferenceTag, value: freeze({ name, isArray }) });
|
|
93
|
+
export const buildReferenceTag = (name, isArray = false, hasGap = false) => {
|
|
94
|
+
return freeze({ type: ReferenceTag, value: freeze({ name, isArray, hasGap }) });
|
|
80
95
|
};
|
|
81
96
|
|
|
82
97
|
export const buildNullTag = () => {
|
|
@@ -99,7 +114,7 @@ export const buildEmbeddedNode = (node) => {
|
|
|
99
114
|
return freeze({ type: EmbeddedNode, value: node });
|
|
100
115
|
};
|
|
101
116
|
|
|
102
|
-
export const buildDoctypeTag = (attributes) => {
|
|
117
|
+
export const buildDoctypeTag = (attributes = {}) => {
|
|
103
118
|
return freeze({
|
|
104
119
|
type: DoctypeTag,
|
|
105
120
|
value: { doctype: 'cstml', version: 0, attributes: freeze(attributes) },
|
|
@@ -107,18 +122,10 @@ export const buildDoctypeTag = (attributes) => {
|
|
|
107
122
|
};
|
|
108
123
|
|
|
109
124
|
export const buildNodeOpenTag = (flags = {}, language = null, type = null, attributes = {}) => {
|
|
110
|
-
let { token, trivia, escape, expression, intrinsic } = flags;
|
|
111
|
-
|
|
112
|
-
token = !!token;
|
|
113
|
-
trivia = !!trivia;
|
|
114
|
-
escape = !!escape;
|
|
115
|
-
expression = !!expression;
|
|
116
|
-
intrinsic = !!intrinsic;
|
|
117
|
-
|
|
118
125
|
return freeze({
|
|
119
126
|
type: OpenNodeTag,
|
|
120
127
|
value: freeze({
|
|
121
|
-
flags: freeze(
|
|
128
|
+
flags: freeze(flags),
|
|
122
129
|
language,
|
|
123
130
|
type,
|
|
124
131
|
attributes,
|
|
@@ -130,6 +137,23 @@ export const buildNodeCloseTag = (type = null, language = null) => {
|
|
|
130
137
|
return freeze({ type: CloseNodeTag, value: freeze({ language, type }) });
|
|
131
138
|
};
|
|
132
139
|
|
|
140
|
+
export const buildFragmentOpenTag = (flags = {}) => {
|
|
141
|
+
return freeze({
|
|
142
|
+
type: OpenFragmentTag,
|
|
143
|
+
value: freeze({
|
|
144
|
+
flags: freeze(flags),
|
|
145
|
+
}),
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export const buildFragmentCloseTag = (type = null, language = null) => {
|
|
150
|
+
return freeze({ type: CloseFragmentTag, value: freeze({ language, type }) });
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export const wrapFragment = (node) => {
|
|
154
|
+
return buildFragment([buildReferenceTag('.')], { '.': node });
|
|
155
|
+
};
|
|
156
|
+
|
|
133
157
|
const isString = (val) => typeof val === 'string';
|
|
134
158
|
|
|
135
159
|
export const buildLiteralTag = (value) => {
|
|
@@ -145,7 +169,7 @@ export const buildNodeWithFlags = (
|
|
|
145
169
|
properties = {},
|
|
146
170
|
attributes = {},
|
|
147
171
|
) => {
|
|
148
|
-
const openTag = buildNodeOpenTag(
|
|
172
|
+
const openTag = buildNodeOpenTag(flags, language, type, attributes);
|
|
149
173
|
const closeTag = buildNodeCloseTag(type);
|
|
150
174
|
|
|
151
175
|
return freeze({
|
|
@@ -158,61 +182,84 @@ export const buildNodeWithFlags = (
|
|
|
158
182
|
});
|
|
159
183
|
};
|
|
160
184
|
|
|
185
|
+
const flagsWithGap = new WeakMap();
|
|
186
|
+
|
|
187
|
+
export const getFlagsWithGap = (flags) => flagsWithGap.get(flags);
|
|
188
|
+
|
|
161
189
|
export const nodeFlags = freeze({
|
|
162
190
|
token: false,
|
|
163
191
|
escape: false,
|
|
164
192
|
trivia: false,
|
|
165
|
-
intrinsic: false,
|
|
166
193
|
expression: false,
|
|
194
|
+
hasGap: false,
|
|
167
195
|
});
|
|
168
196
|
|
|
197
|
+
const hasGap = (flags, children, properties) => {
|
|
198
|
+
return find((node) => node.flags.hasGap, relatedNodes(properties));
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const getGapFlags = (flags) => {
|
|
202
|
+
let gapFlags = flagsWithGap.get(flags);
|
|
203
|
+
if (!gapFlags) {
|
|
204
|
+
gapFlags = { ...flags, hasGap: true };
|
|
205
|
+
flagsWithGap.set(flags, gapFlags);
|
|
206
|
+
}
|
|
207
|
+
return gapFlags;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const getFlags = (flags, children, properties) => {
|
|
211
|
+
if (!hasGap(flags, children, properties)) {
|
|
212
|
+
return flags;
|
|
213
|
+
} else {
|
|
214
|
+
return getGapFlags(flags);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
169
218
|
export const buildNode = (language, type, children = [], properties = {}, attributes = {}) => {
|
|
170
|
-
|
|
219
|
+
const flags = getFlags(nodeFlags, children, properties);
|
|
220
|
+
return buildNodeWithFlags(flags, language, type, children, properties, attributes);
|
|
171
221
|
};
|
|
172
222
|
|
|
173
|
-
export const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
intrinsic: false,
|
|
178
|
-
expression: false,
|
|
179
|
-
});
|
|
223
|
+
export const buildFragmentWithFlags = (flags, children = [], properties = {}, attributes = {}) => {
|
|
224
|
+
const doctypeTag = buildDoctypeTag(attributes);
|
|
225
|
+
const openTag = buildFragmentOpenTag(flags);
|
|
226
|
+
const closeTag = buildFragmentCloseTag();
|
|
180
227
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
228
|
+
return freeze({
|
|
229
|
+
flags,
|
|
230
|
+
children: btree.addAt(
|
|
231
|
+
0,
|
|
232
|
+
btree.addAt(0, btree.addAt(btree.getSum(children), children, closeTag), openTag),
|
|
233
|
+
doctypeTag,
|
|
234
|
+
),
|
|
235
|
+
properties: freeze(properties),
|
|
236
|
+
attributes: freeze(attributes),
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
export const buildFragment = (children = [], properties = {}, attributes = {}) => {
|
|
241
|
+
const flags = getFlags(nodeFlags, children, properties);
|
|
242
|
+
return buildFragmentWithFlags(flags, children, properties, attributes);
|
|
190
243
|
};
|
|
191
244
|
|
|
192
|
-
export const
|
|
245
|
+
export const syntacticFlags = freeze({
|
|
193
246
|
token: true,
|
|
194
247
|
escape: false,
|
|
195
248
|
trivia: false,
|
|
196
|
-
intrinsic: true,
|
|
197
249
|
expression: false,
|
|
250
|
+
hasGap: false,
|
|
198
251
|
});
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
language,
|
|
203
|
-
type,
|
|
204
|
-
[buildLiteralTag(value)],
|
|
205
|
-
{},
|
|
206
|
-
attributes,
|
|
207
|
-
);
|
|
252
|
+
|
|
253
|
+
export const buildSyntacticNode = (language, type, value) => {
|
|
254
|
+
return buildNodeWithFlags(syntacticFlags, language, type, [buildLiteralTag(value)]);
|
|
208
255
|
};
|
|
209
256
|
|
|
210
257
|
export const escapeFlags = freeze({
|
|
211
258
|
token: false,
|
|
212
259
|
escape: true,
|
|
213
260
|
trivia: false,
|
|
214
|
-
intrinsic: false,
|
|
215
261
|
expression: false,
|
|
262
|
+
hasGap: false,
|
|
216
263
|
});
|
|
217
264
|
|
|
218
265
|
export const buildEscapeNode = (
|
|
@@ -222,15 +269,16 @@ export const buildEscapeNode = (
|
|
|
222
269
|
properties = {},
|
|
223
270
|
attributes = {},
|
|
224
271
|
) => {
|
|
225
|
-
|
|
272
|
+
const flags = getFlags(escapeFlags, children, properties);
|
|
273
|
+
return buildNodeWithFlags(flags, language, type, children, properties, attributes);
|
|
226
274
|
};
|
|
227
275
|
|
|
228
276
|
export const syntacticEscapeFlags = freeze({
|
|
229
277
|
token: true,
|
|
230
278
|
escape: true,
|
|
231
279
|
trivia: false,
|
|
232
|
-
intrinsic: false,
|
|
233
280
|
expression: false,
|
|
281
|
+
hasGap: false,
|
|
234
282
|
});
|
|
235
283
|
|
|
236
284
|
export const buildSyntacticEscapeNode = (
|
|
@@ -247,8 +295,8 @@ export const syntacticTriviaFlags = freeze({
|
|
|
247
295
|
token: true,
|
|
248
296
|
escape: false,
|
|
249
297
|
trivia: true,
|
|
250
|
-
intrinsic: false,
|
|
251
298
|
expression: false,
|
|
299
|
+
hasGap: false,
|
|
252
300
|
});
|
|
253
301
|
|
|
254
302
|
export const buildSyntacticTriviaNode = (
|
|
@@ -265,8 +313,8 @@ export const triviaFlags = freeze({
|
|
|
265
313
|
token: false,
|
|
266
314
|
escape: false,
|
|
267
315
|
trivia: true,
|
|
268
|
-
intrinsic: false,
|
|
269
316
|
expression: false,
|
|
317
|
+
hasGap: false,
|
|
270
318
|
});
|
|
271
319
|
|
|
272
320
|
export const buildTriviaNode = (
|
|
@@ -276,7 +324,8 @@ export const buildTriviaNode = (
|
|
|
276
324
|
properties = {},
|
|
277
325
|
attributes = {},
|
|
278
326
|
) => {
|
|
279
|
-
|
|
327
|
+
const flags = getFlags(triviaFlags, children, properties);
|
|
328
|
+
return buildNodeWithFlags(flags, language, type, children, properties, attributes);
|
|
280
329
|
};
|
|
281
330
|
|
|
282
331
|
export const buildNullNode = (nullToken = buildNullTag()) => {
|
|
@@ -292,7 +341,7 @@ export const buildNullNode = (nullToken = buildNullTag()) => {
|
|
|
292
341
|
|
|
293
342
|
export const buildGapNode = (gapToken = buildGapTag()) => {
|
|
294
343
|
return freeze({
|
|
295
|
-
flags: nodeFlags,
|
|
344
|
+
flags: getGapFlags(nodeFlags),
|
|
296
345
|
language: null,
|
|
297
346
|
type: sym.gap,
|
|
298
347
|
children: btree.freeze([gapToken]),
|
package/lib/path.js
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
export const parsePath = (str) => {
|
|
2
|
-
|
|
3
|
-
const
|
|
2
|
+
let name = str;
|
|
3
|
+
const hasGap = name.endsWith('$');
|
|
4
4
|
|
|
5
|
-
if (
|
|
5
|
+
if (hasGap) name = name.slice(0, -1);
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const isArray = name.endsWith('[]');
|
|
8
|
+
|
|
9
|
+
if (isArray) name = name.slice(0, -2);
|
|
10
|
+
|
|
11
|
+
if (!/^(\.|[a-zA-Z]+)$/.test(name)) throw new Error();
|
|
12
|
+
|
|
13
|
+
const isRoot = name === '.';
|
|
14
|
+
|
|
15
|
+
return { name, hasGap, isArray, isRoot };
|
|
8
16
|
};
|
|
9
17
|
|
|
10
18
|
export const printPath = (path) => {
|
|
11
19
|
if (!path) return null;
|
|
12
20
|
|
|
13
|
-
const { isArray, name } = path;
|
|
21
|
+
const { isArray, isRoot, hasGap, name } = path;
|
|
22
|
+
|
|
23
|
+
if (isRoot && name !== '.') throw new Error();
|
|
14
24
|
|
|
15
|
-
return `${name}${isArray ? '[]' : ''}`;
|
|
25
|
+
return `${name}${isArray ? '[]' : ''}${hasGap ? '$' : ''}`;
|
|
16
26
|
};
|
package/lib/print.js
CHANGED
|
@@ -2,6 +2,8 @@ import {
|
|
|
2
2
|
DoctypeTag,
|
|
3
3
|
OpenNodeTag,
|
|
4
4
|
CloseNodeTag,
|
|
5
|
+
OpenFragmentTag,
|
|
6
|
+
CloseFragmentTag,
|
|
5
7
|
ReferenceTag,
|
|
6
8
|
ShiftTag,
|
|
7
9
|
GapTag,
|
|
@@ -151,10 +153,11 @@ export const printShiftTag = (tag) => {
|
|
|
151
153
|
export const printReferenceTag = (tag) => {
|
|
152
154
|
if (tag?.type !== ReferenceTag) throw new Error();
|
|
153
155
|
|
|
154
|
-
const { name, isArray } = tag.value;
|
|
156
|
+
const { name, isArray, hasGap } = tag.value;
|
|
155
157
|
const pathBraces = isArray ? '[]' : '';
|
|
158
|
+
const gapDollar = hasGap ? '$' : '';
|
|
156
159
|
|
|
157
|
-
return `${name}${pathBraces}:`;
|
|
160
|
+
return `${name}${pathBraces}${gapDollar}:`;
|
|
158
161
|
};
|
|
159
162
|
|
|
160
163
|
export const printNullTag = (tag) => {
|
|
@@ -192,14 +195,14 @@ export const printLiteralTag = (tag) => {
|
|
|
192
195
|
|
|
193
196
|
export const printFlags = (flags) => {
|
|
194
197
|
const hash = flags.trivia ? '#' : '';
|
|
195
|
-
const tilde = flags.intrinsic ? '~' : '';
|
|
196
198
|
const star = flags.token ? '*' : '';
|
|
197
199
|
const at = flags.escape ? '@' : '';
|
|
198
200
|
const plus = flags.expression ? '+' : '';
|
|
201
|
+
const dollar = flags.hasGap ? '$' : '';
|
|
199
202
|
|
|
200
203
|
if (flags.escape && flags.trivia) throw new Error('Node cannot be escape and trivia');
|
|
201
204
|
|
|
202
|
-
return `${hash}${
|
|
205
|
+
return `${hash}${star}${at}${plus}${dollar}`;
|
|
203
206
|
};
|
|
204
207
|
|
|
205
208
|
export const printOpenNodeTag = (tag) => {
|
|
@@ -213,6 +216,14 @@ export const printOpenNodeTag = (tag) => {
|
|
|
213
216
|
return `<${printFlags(flags)}${printTagPath(tagLanguage, type)}${attributesFrag}>`;
|
|
214
217
|
};
|
|
215
218
|
|
|
219
|
+
export const printOpenFragmentTag = (tag) => {
|
|
220
|
+
if (tag?.type !== OpenFragmentTag) throw new Error();
|
|
221
|
+
|
|
222
|
+
const { flags } = tag.value;
|
|
223
|
+
|
|
224
|
+
return `<${printFlags(flags)}>`;
|
|
225
|
+
};
|
|
226
|
+
|
|
216
227
|
export const printSelfClosingNodeTag = (tag, intrinsicValue) => {
|
|
217
228
|
if (tag?.type !== OpenNodeTag) throw new Error();
|
|
218
229
|
|
|
@@ -234,6 +245,12 @@ export const printCloseNodeTag = (tag) => {
|
|
|
234
245
|
return `</>`;
|
|
235
246
|
};
|
|
236
247
|
|
|
248
|
+
export const printCloseFragmentTag = (tag) => {
|
|
249
|
+
if (tag?.type !== CloseFragmentTag) throw new Error();
|
|
250
|
+
|
|
251
|
+
return `</>`;
|
|
252
|
+
};
|
|
253
|
+
|
|
237
254
|
export const printTag = (tag) => {
|
|
238
255
|
if (!isObject(tag)) throw new Error();
|
|
239
256
|
|
|
@@ -265,6 +282,12 @@ export const printTag = (tag) => {
|
|
|
265
282
|
case CloseNodeTag:
|
|
266
283
|
return printCloseNodeTag(tag);
|
|
267
284
|
|
|
285
|
+
case OpenFragmentTag:
|
|
286
|
+
return printOpenFragmentTag(tag);
|
|
287
|
+
|
|
288
|
+
case CloseFragmentTag:
|
|
289
|
+
return printCloseFragmentTag(tag);
|
|
290
|
+
|
|
268
291
|
default:
|
|
269
292
|
throw new Error();
|
|
270
293
|
}
|
package/lib/shorthand.js
CHANGED
|
@@ -8,14 +8,15 @@ import {
|
|
|
8
8
|
buildNullNode,
|
|
9
9
|
buildArrayTag,
|
|
10
10
|
buildNode,
|
|
11
|
+
buildFragment,
|
|
11
12
|
buildGapNode,
|
|
12
13
|
buildSyntacticNode,
|
|
13
|
-
buildSyntacticIntrinsicNode,
|
|
14
14
|
buildEscapeNode,
|
|
15
15
|
buildSyntacticEscapeNode,
|
|
16
16
|
buildSyntacticTriviaNode,
|
|
17
17
|
buildTriviaNode,
|
|
18
18
|
} from './builders.js';
|
|
19
|
+
import { parsePath } from './path.js';
|
|
19
20
|
|
|
20
21
|
export * from './builders.js';
|
|
21
22
|
|
|
@@ -33,14 +34,9 @@ const stripArray = (val) => {
|
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
export const ref = (path) => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return buildReferenceTag(name, pathIsArray);
|
|
40
|
-
} else {
|
|
41
|
-
const { name, isArray: pathIsArray } = path;
|
|
42
|
-
return buildReferenceTag(name, pathIsArray);
|
|
43
|
-
}
|
|
37
|
+
const { name, isArray: pathIsArray } = parsePath(isArray(path) ? path[0] : path);
|
|
38
|
+
|
|
39
|
+
return buildReferenceTag(name, pathIsArray);
|
|
44
40
|
};
|
|
45
41
|
|
|
46
42
|
export const lit = (str) => buildLiteralTag(stripArray(str));
|
|
@@ -51,9 +47,9 @@ export const embedded = buildEmbeddedNode;
|
|
|
51
47
|
export const nodeOpen = buildNodeOpenTag;
|
|
52
48
|
export const nodeClose = buildNodeCloseTag;
|
|
53
49
|
export const node = buildNode;
|
|
50
|
+
export const frag = buildFragment;
|
|
54
51
|
export const g_node = buildGapNode;
|
|
55
52
|
export const s_node = buildSyntacticNode;
|
|
56
|
-
export const s_i_node = buildSyntacticIntrinsicNode;
|
|
57
53
|
export const e_node = buildEscapeNode;
|
|
58
54
|
export const s_e_node = buildSyntacticEscapeNode;
|
|
59
55
|
export const s_t_node = buildSyntacticTriviaNode;
|
package/lib/stream.js
CHANGED
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
LiteralTag,
|
|
15
15
|
EmbeddedExpression,
|
|
16
16
|
TokenGroup,
|
|
17
|
+
OpenFragmentTag,
|
|
18
|
+
CloseFragmentTag,
|
|
17
19
|
} from './symbols.js';
|
|
18
20
|
|
|
19
21
|
export * from './print.js';
|
|
@@ -103,10 +105,6 @@ export class AsyncGenerator {
|
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
|
106
|
-
export const isIntrinsicToken = (tag) => {
|
|
107
|
-
return tag.type === OpenNodeTag && tag.value.flags.intrinsic && tag.value.flags.token;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
108
|
export class StreamGenerator {
|
|
111
109
|
constructor(embeddedGenerator) {
|
|
112
110
|
this.generator = embeddedGenerator;
|
|
@@ -161,6 +159,51 @@ export const maybeWait = (maybePromise, callback) => {
|
|
|
161
159
|
}
|
|
162
160
|
};
|
|
163
161
|
|
|
162
|
+
function* __isEmpty(tags) {
|
|
163
|
+
const co = new Coroutine(getStreamIterator(tags));
|
|
164
|
+
|
|
165
|
+
for (;;) {
|
|
166
|
+
co.advance();
|
|
167
|
+
|
|
168
|
+
if (co.current instanceof Promise) {
|
|
169
|
+
co.current = yield co.current;
|
|
170
|
+
}
|
|
171
|
+
if (co.done) break;
|
|
172
|
+
|
|
173
|
+
let depth = 0;
|
|
174
|
+
|
|
175
|
+
const tag = co.value;
|
|
176
|
+
|
|
177
|
+
switch (tag.type) {
|
|
178
|
+
case OpenFragmentTag:
|
|
179
|
+
case OpenNodeTag:
|
|
180
|
+
if (tag.value.flags.trivia) {
|
|
181
|
+
++depth;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (depth === 0 && tag.value.flags.escape) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
break;
|
|
189
|
+
|
|
190
|
+
case CloseFragmentTag:
|
|
191
|
+
case CloseNodeTag:
|
|
192
|
+
--depth;
|
|
193
|
+
break;
|
|
194
|
+
|
|
195
|
+
case LiteralTag:
|
|
196
|
+
case GapTag:
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const isEmpty = (tags) =>
|
|
205
|
+
new StreamIterable(__isEmpty(tags))[Symbol.iterator]().next().value;
|
|
206
|
+
|
|
164
207
|
function* __generateStandardOutput(tags) {
|
|
165
208
|
const co = new Coroutine(getStreamIterator(tags));
|
|
166
209
|
|
|
@@ -175,7 +218,7 @@ function* __generateStandardOutput(tags) {
|
|
|
175
218
|
const tag = co.value;
|
|
176
219
|
|
|
177
220
|
if (tag.type === 'Effect') {
|
|
178
|
-
const effect =
|
|
221
|
+
const effect = tag.value;
|
|
179
222
|
if (effect.verb === 'write') {
|
|
180
223
|
const writeEffect = getEmbeddedExpression(effect.value);
|
|
181
224
|
if (writeEffect.stream == null || writeEffect.stream === 1) {
|
|
@@ -204,7 +247,7 @@ function* __generateAllOutput(tags) {
|
|
|
204
247
|
const tag = co.value;
|
|
205
248
|
|
|
206
249
|
if (tag.type === 'Effect') {
|
|
207
|
-
const effect =
|
|
250
|
+
const effect = tag.value;
|
|
208
251
|
if (effect.verb === 'write') {
|
|
209
252
|
const writeEffect = getEmbeddedExpression(effect.value);
|
|
210
253
|
const prevStream = currentStream;
|
|
@@ -316,6 +359,10 @@ function* __generateCSTMLStrategy(tags, options) {
|
|
|
316
359
|
export const generateCSTMLStrategy = (tags, options = {}) =>
|
|
317
360
|
new StreamIterable(__generateCSTMLStrategy(tags, options));
|
|
318
361
|
|
|
362
|
+
const isToken = (tag) => {
|
|
363
|
+
return tag.value.flags.token;
|
|
364
|
+
};
|
|
365
|
+
|
|
319
366
|
export const prettyGroupTokens = (tags) => new StreamIterable(__prettyGroupTokens(tags));
|
|
320
367
|
|
|
321
368
|
function* __prettyGroupTokens(tags) {
|
|
@@ -334,15 +381,18 @@ function* __prettyGroupTokens(tags) {
|
|
|
334
381
|
}
|
|
335
382
|
|
|
336
383
|
const tag = co.value;
|
|
337
|
-
const isOpenClose =
|
|
384
|
+
const isOpenClose =
|
|
385
|
+
tag.type === CloseNodeTag ||
|
|
386
|
+
tag.type === OpenNodeTag ||
|
|
387
|
+
tag.type === CloseFragmentTag ||
|
|
388
|
+
tag.type === OpenFragmentTag;
|
|
338
389
|
|
|
339
390
|
if (
|
|
340
391
|
(tag.type === 'Effect' && tag.value.verb === 'write') ||
|
|
341
|
-
[ReferenceTag, DoctypeTag, GapTag, NullTag, ArrayTag, ShiftTag].includes(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
(tag.type === LiteralTag || (tag.type === OpenNodeTag && tag.value.flags.escape)))
|
|
392
|
+
[ReferenceTag, DoctypeTag, GapTag, NullTag, ArrayTag, ShiftTag, OpenFragmentTag].includes(
|
|
393
|
+
tag.type,
|
|
394
|
+
) ||
|
|
395
|
+
(tag.type === OpenNodeTag && tag.value.flags.escape)
|
|
346
396
|
) {
|
|
347
397
|
state.broken = true;
|
|
348
398
|
|
|
@@ -350,20 +400,16 @@ function* __prettyGroupTokens(tags) {
|
|
|
350
400
|
yield* state.holding;
|
|
351
401
|
state.holding = [];
|
|
352
402
|
}
|
|
353
|
-
|
|
354
|
-
if (!isOpenClose && tag.type !== 'Effect') {
|
|
355
|
-
yield tag;
|
|
356
|
-
}
|
|
357
|
-
} else if (!isOpenClose && tag.type !== 'Effect') {
|
|
403
|
+
} else if (tag.type === LiteralTag) {
|
|
358
404
|
state.holding.push(tag);
|
|
359
405
|
}
|
|
360
406
|
|
|
361
|
-
if (
|
|
407
|
+
if (!state.holding.length && !isOpenClose) {
|
|
362
408
|
yield tag;
|
|
363
409
|
}
|
|
364
410
|
|
|
365
|
-
if (tag.type === CloseNodeTag) {
|
|
366
|
-
if (!state.broken && (
|
|
411
|
+
if (tag.type === CloseNodeTag || tag.type === CloseFragmentTag) {
|
|
412
|
+
if (!state.broken && (isToken(state.open) || state.holding.length === 1)) {
|
|
367
413
|
state.holding.push(tag);
|
|
368
414
|
yield buildTokenGroup(state.holding);
|
|
369
415
|
} else {
|
|
@@ -377,8 +423,14 @@ function* __prettyGroupTokens(tags) {
|
|
|
377
423
|
state = states.value;
|
|
378
424
|
}
|
|
379
425
|
|
|
380
|
-
if (tag.type === OpenNodeTag
|
|
381
|
-
|
|
426
|
+
if (tag.type === OpenNodeTag || tag.type === OpenFragmentTag) {
|
|
427
|
+
if (tag.type === OpenFragmentTag) {
|
|
428
|
+
states = states.push({ holding: [], broken: false, open: tag });
|
|
429
|
+
yield tag;
|
|
430
|
+
} else {
|
|
431
|
+
states = states.push({ holding: [tag], broken: false, open: tag });
|
|
432
|
+
}
|
|
433
|
+
|
|
382
434
|
state = states.value;
|
|
383
435
|
}
|
|
384
436
|
}
|
|
@@ -396,6 +448,7 @@ function* __generatePrettyCSTMLStrategy(tags, options) {
|
|
|
396
448
|
let indentLevel = 0;
|
|
397
449
|
let first = true;
|
|
398
450
|
let inline = false;
|
|
451
|
+
let ref = null;
|
|
399
452
|
|
|
400
453
|
for (;;) {
|
|
401
454
|
co.advance();
|
|
@@ -409,7 +462,7 @@ function* __generatePrettyCSTMLStrategy(tags, options) {
|
|
|
409
462
|
const tag = co.value;
|
|
410
463
|
|
|
411
464
|
if (tag.type === 'Effect') {
|
|
412
|
-
const effect =
|
|
465
|
+
const effect = tag.value;
|
|
413
466
|
if (emitEffects && effect.verb === 'write') {
|
|
414
467
|
const writeEffect = getEmbeddedExpression(effect.value);
|
|
415
468
|
yield buildWriteEffect(
|
|
@@ -426,6 +479,7 @@ function* __generatePrettyCSTMLStrategy(tags, options) {
|
|
|
426
479
|
inline =
|
|
427
480
|
inlineOption &&
|
|
428
481
|
inline &&
|
|
482
|
+
ref &&
|
|
429
483
|
(tag.type === NullTag ||
|
|
430
484
|
tag.type === GapTag ||
|
|
431
485
|
tag.type === ArrayTag ||
|
|
@@ -435,7 +489,8 @@ function* __generatePrettyCSTMLStrategy(tags, options) {
|
|
|
435
489
|
yield buildWriteEffect('\n');
|
|
436
490
|
}
|
|
437
491
|
|
|
438
|
-
if (tag.type === CloseNodeTag) {
|
|
492
|
+
if (tag.type === CloseNodeTag || tag.type === CloseFragmentTag) {
|
|
493
|
+
ref = null;
|
|
439
494
|
if (indentLevel === 0) {
|
|
440
495
|
throw new Error('imbalanced tag stack');
|
|
441
496
|
}
|
|
@@ -450,6 +505,7 @@ function* __generatePrettyCSTMLStrategy(tags, options) {
|
|
|
450
505
|
}
|
|
451
506
|
|
|
452
507
|
if (tag.type === TokenGroup) {
|
|
508
|
+
ref = null;
|
|
453
509
|
const intrinsicValue = tag.value[0].value.flags.token ? getCooked(tag.value) : null;
|
|
454
510
|
yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
|
|
455
511
|
} else {
|
|
@@ -458,9 +514,10 @@ function* __generatePrettyCSTMLStrategy(tags, options) {
|
|
|
458
514
|
|
|
459
515
|
if (tag.type === ReferenceTag) {
|
|
460
516
|
inline = true;
|
|
517
|
+
ref = tag;
|
|
461
518
|
}
|
|
462
519
|
|
|
463
|
-
if (tag.type === OpenNodeTag) {
|
|
520
|
+
if (tag.type === OpenNodeTag || tag.type === OpenFragmentTag) {
|
|
464
521
|
indentLevel++;
|
|
465
522
|
}
|
|
466
523
|
|
|
@@ -496,6 +553,7 @@ export const getCooked = (tags) => {
|
|
|
496
553
|
break;
|
|
497
554
|
}
|
|
498
555
|
|
|
556
|
+
case OpenFragmentTag:
|
|
499
557
|
case OpenNodeTag: {
|
|
500
558
|
const { flags, attributes } = tag.value;
|
|
501
559
|
|
|
@@ -524,6 +582,7 @@ export const getCooked = (tags) => {
|
|
|
524
582
|
break;
|
|
525
583
|
}
|
|
526
584
|
|
|
585
|
+
case CloseFragmentTag:
|
|
527
586
|
case CloseNodeTag: {
|
|
528
587
|
if (depth === 1) {
|
|
529
588
|
foundLast = true;
|
package/lib/symbols.js
CHANGED
|
@@ -9,6 +9,8 @@ export const null_ = Symbol.for('@bablr/null');
|
|
|
9
9
|
export const DoctypeTag = Symbol.for('DoctypeTag');
|
|
10
10
|
export const OpenNodeTag = Symbol.for('OpenNodeTag');
|
|
11
11
|
export const CloseNodeTag = Symbol.for('CloseNodeTag');
|
|
12
|
+
export const OpenFragmentTag = Symbol.for('OpenFragmentTag');
|
|
13
|
+
export const CloseFragmentTag = Symbol.for('CloseFragmentTag');
|
|
12
14
|
export const ReferenceTag = Symbol.for('ReferenceTag');
|
|
13
15
|
export const ShiftTag = Symbol.for('ShiftTag');
|
|
14
16
|
export const GapTag = Symbol.for('GapTag');
|
package/lib/template.js
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import * as t from './builders.js';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
LiteralTag,
|
|
4
|
+
EmbeddedNode,
|
|
5
|
+
ReferenceTag,
|
|
6
|
+
ArrayTag,
|
|
7
|
+
OpenFragmentTag,
|
|
8
|
+
OpenNodeTag,
|
|
9
|
+
DoctypeTag,
|
|
10
|
+
CloseFragmentTag,
|
|
11
|
+
} from './symbols.js';
|
|
3
12
|
import * as btree from './btree.js';
|
|
13
|
+
import { getOpenTag, getRoot } from './tree.js';
|
|
4
14
|
|
|
5
15
|
const { isArray } = Array;
|
|
6
16
|
const { freeze } = Object;
|
|
7
17
|
const { isFinite } = Number;
|
|
8
18
|
|
|
9
|
-
export const interpolateArray = (
|
|
19
|
+
export const interpolateArray = (fragment) => {
|
|
20
|
+
const value = getRoot(fragment);
|
|
21
|
+
|
|
10
22
|
if (isArray(value)) {
|
|
11
23
|
if (isFinite(value[0])) {
|
|
12
24
|
return [...btree.traverse(value)];
|
|
@@ -18,21 +30,41 @@ export const interpolateArray = (value) => {
|
|
|
18
30
|
}
|
|
19
31
|
};
|
|
20
32
|
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
let
|
|
26
|
-
for (
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
export function* interpolateFragmentChildren(value, ref) {
|
|
34
|
+
const open = getOpenTag(value);
|
|
35
|
+
|
|
36
|
+
if (open.type === OpenFragmentTag) {
|
|
37
|
+
let currentRef = null;
|
|
38
|
+
for (let child of btree.traverse(value.children)) {
|
|
39
|
+
if (
|
|
40
|
+
child.type === DoctypeTag ||
|
|
41
|
+
child.type === OpenFragmentTag ||
|
|
42
|
+
child.type === CloseFragmentTag
|
|
43
|
+
) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (child.type === ArrayTag) {
|
|
48
|
+
// if (notAlreadyInitialized) {
|
|
49
|
+
yield child;
|
|
50
|
+
// }
|
|
51
|
+
} else if (child.type === ReferenceTag) {
|
|
52
|
+
currentRef = child;
|
|
53
|
+
if (child.value.name === '.') {
|
|
54
|
+
yield freeze({ ...ref });
|
|
55
|
+
} else {
|
|
56
|
+
yield child;
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
yield child;
|
|
60
|
+
}
|
|
30
61
|
}
|
|
31
|
-
|
|
62
|
+
} else if (open.type === OpenNodeTag) {
|
|
63
|
+
yield freeze({ ...ref });
|
|
32
64
|
} else {
|
|
33
|
-
|
|
65
|
+
throw new Error();
|
|
34
66
|
}
|
|
35
|
-
}
|
|
67
|
+
}
|
|
36
68
|
|
|
37
69
|
const validateTag = (tag) => {
|
|
38
70
|
if (!tag || (tag.type !== LiteralTag && tag.type !== EmbeddedNode)) {
|
package/lib/tree.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Coroutine } from '@bablr/coroutine';
|
|
2
2
|
import emptyStack from '@iter-tools/imm-stack';
|
|
3
|
-
import { nodeFlags,
|
|
3
|
+
import { nodeFlags, buildEmbeddedNode } from './builders.js';
|
|
4
4
|
import {
|
|
5
5
|
printPrettyCSTML as printPrettyCSTMLFromStream,
|
|
6
6
|
printCSTML as printCSTMLFromStream,
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
DoctypeTag,
|
|
12
12
|
OpenNodeTag,
|
|
13
13
|
CloseNodeTag,
|
|
14
|
+
OpenFragmentTag,
|
|
15
|
+
CloseFragmentTag,
|
|
14
16
|
ReferenceTag,
|
|
15
17
|
ShiftTag,
|
|
16
18
|
GapTag,
|
|
@@ -33,11 +35,11 @@ const { isArray } = Array;
|
|
|
33
35
|
const { hasOwn, freeze } = Object;
|
|
34
36
|
|
|
35
37
|
export const get = (node, path) => {
|
|
36
|
-
const {
|
|
37
|
-
const { 1: name, 2: index } = /^([^\.]
|
|
38
|
+
const { properties } = node;
|
|
39
|
+
const { 1: name, 2: index } = /^([^\.]+|\.)(?:\.(\d+))?/.exec(path) || [];
|
|
38
40
|
|
|
39
41
|
if (!hasOwn(properties, name)) {
|
|
40
|
-
|
|
42
|
+
return null;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
if (index != null) {
|
|
@@ -88,14 +90,15 @@ function* __treeFromStream(tokens) {
|
|
|
88
90
|
continue;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
if (held && tag.type !==
|
|
93
|
+
if (held && tag.type !== OpenNodeTag && tag.type !== GapTag) {
|
|
92
94
|
throw new Error('cannot eat this type of tag while holding');
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
switch (tag.type) {
|
|
96
98
|
case LiteralTag:
|
|
97
99
|
case ReferenceTag:
|
|
98
|
-
case CloseNodeTag:
|
|
100
|
+
case CloseNodeTag:
|
|
101
|
+
case CloseFragmentTag: {
|
|
99
102
|
break;
|
|
100
103
|
}
|
|
101
104
|
|
|
@@ -134,10 +137,7 @@ function* __treeFromStream(tokens) {
|
|
|
134
137
|
}
|
|
135
138
|
|
|
136
139
|
case OpenNodeTag: {
|
|
137
|
-
const { flags, type } = tag.value;
|
|
138
|
-
|
|
139
|
-
const language = type ? tag.value.language : doctype.value.attributes['bablr-language'];
|
|
140
|
-
const attributes = type ? tag.value.attributes : doctype.value.attributes;
|
|
140
|
+
const { flags, type, language, attributes } = tag.value;
|
|
141
141
|
|
|
142
142
|
const node = createNode(flags, language, type, [], {}, attributes);
|
|
143
143
|
|
|
@@ -145,26 +145,48 @@ function* __treeFromStream(tokens) {
|
|
|
145
145
|
|
|
146
146
|
path = { parent: path, node, depth: (path.depth || -1) + 1 };
|
|
147
147
|
|
|
148
|
-
if (parentPath)
|
|
149
|
-
const { node: parentNode } = path;
|
|
150
|
-
if (!(flags.escape || flags.trivia)) {
|
|
151
|
-
if (!parentNode.children.length) {
|
|
152
|
-
throw new Error('Nodes must follow references');
|
|
153
|
-
}
|
|
148
|
+
if (!parentPath) throw new Error();
|
|
154
149
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
parentNode.children.push(buildEmbeddedNode(node));
|
|
150
|
+
const { node: parentNode } = path;
|
|
151
|
+
if (!(flags.escape || flags.trivia)) {
|
|
152
|
+
if (!parentNode.children.length) {
|
|
153
|
+
throw new Error('Nodes must follow references');
|
|
160
154
|
}
|
|
155
|
+
|
|
156
|
+
const ref = arrayLast(parentNode.children);
|
|
157
|
+
|
|
158
|
+
add(parentNode, ref, node);
|
|
161
159
|
} else {
|
|
162
|
-
|
|
160
|
+
parentNode.children.push(buildEmbeddedNode(node));
|
|
163
161
|
}
|
|
164
162
|
|
|
165
163
|
break;
|
|
166
164
|
}
|
|
167
165
|
|
|
166
|
+
case OpenFragmentTag: {
|
|
167
|
+
const { flags } = tag.value;
|
|
168
|
+
|
|
169
|
+
const language = doctype.value.attributes['bablr-language'];
|
|
170
|
+
const attributes = doctype.value.attributes;
|
|
171
|
+
|
|
172
|
+
const node = freeze({
|
|
173
|
+
flags,
|
|
174
|
+
language,
|
|
175
|
+
type: null,
|
|
176
|
+
children: [],
|
|
177
|
+
properties: {},
|
|
178
|
+
attributes,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (path) throw new Error();
|
|
182
|
+
|
|
183
|
+
path = { parent: null, node, depth: 0 };
|
|
184
|
+
|
|
185
|
+
rootPath = path;
|
|
186
|
+
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
|
|
168
190
|
default: {
|
|
169
191
|
throw new Error();
|
|
170
192
|
}
|
|
@@ -193,7 +215,7 @@ function* __treeFromStream(tokens) {
|
|
|
193
215
|
return rootPath.node;
|
|
194
216
|
}
|
|
195
217
|
|
|
196
|
-
export const treeFromStream = (tags) =>
|
|
218
|
+
export const treeFromStream = (tags) => __treeFromStream(tags);
|
|
197
219
|
|
|
198
220
|
export const treeFromStreamSync = (tokens) => {
|
|
199
221
|
return evaluateReturnSync(treeFromStream(tokens));
|
|
@@ -223,13 +245,64 @@ export const evaluateReturnAsync = async (generator) => {
|
|
|
223
245
|
|
|
224
246
|
export const streamFromTree = (rootNode) => __streamFromTree(rootNode);
|
|
225
247
|
|
|
248
|
+
export const isEmpty = (node) => {
|
|
249
|
+
const { properties } = node;
|
|
250
|
+
|
|
251
|
+
for (const child of btree.traverse(node.children)) {
|
|
252
|
+
switch (child.type) {
|
|
253
|
+
case ReferenceTag: {
|
|
254
|
+
const { name } = child.value;
|
|
255
|
+
|
|
256
|
+
if (properties[name]) {
|
|
257
|
+
const value = properties[name];
|
|
258
|
+
|
|
259
|
+
if (value != null || value.type !== sym.null || (isArray(value) && value.length)) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
case EmbeddedNode: {
|
|
267
|
+
if (node.value.flags.escape) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
case LiteralTag:
|
|
274
|
+
case GapTag:
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return true;
|
|
279
|
+
};
|
|
280
|
+
|
|
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
|
+
export const buildStubNode = (tag) => {
|
|
291
|
+
return freeze({
|
|
292
|
+
flags: nodeFlags,
|
|
293
|
+
language: null,
|
|
294
|
+
type: symbolTypeFor(tag.type),
|
|
295
|
+
children: freeze([tag]),
|
|
296
|
+
properties: freeze({}),
|
|
297
|
+
attributes: freeze({}),
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
|
|
226
301
|
function* __streamFromTree(rootNode) {
|
|
227
302
|
if (!rootNode || rootNode.type === GapTag) {
|
|
228
303
|
return rootNode;
|
|
229
304
|
}
|
|
230
305
|
|
|
231
|
-
yield buildDoctypeTag(rootNode.attributes);
|
|
232
|
-
|
|
233
306
|
let stack = emptyStack.push(rootNode);
|
|
234
307
|
const resolver = new Resolver();
|
|
235
308
|
|
|
@@ -273,7 +346,8 @@ function* __streamFromTree(rootNode) {
|
|
|
273
346
|
|
|
274
347
|
case GapTag:
|
|
275
348
|
case NullTag:
|
|
276
|
-
case CloseNodeTag:
|
|
349
|
+
case CloseNodeTag:
|
|
350
|
+
case CloseFragmentTag: {
|
|
277
351
|
stack = stack.pop();
|
|
278
352
|
resolver.advance(tag);
|
|
279
353
|
yield tag;
|
|
@@ -397,8 +471,9 @@ export const printSource = (rootNode) => __printSource(rootNode);
|
|
|
397
471
|
export const sourceTextFor = printSource;
|
|
398
472
|
|
|
399
473
|
export const getOpenTag = (node) => {
|
|
400
|
-
const tag = btree.getAt(0, node.children);
|
|
401
|
-
if (tag
|
|
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();
|
|
402
477
|
return tag;
|
|
403
478
|
};
|
|
404
479
|
|
|
@@ -473,15 +548,19 @@ export const acceptNode = (node, accepted) => {
|
|
|
473
548
|
return node;
|
|
474
549
|
};
|
|
475
550
|
|
|
476
|
-
export const getRoot = (
|
|
477
|
-
|
|
551
|
+
export const getRoot = (node) => {
|
|
552
|
+
return node.type == null ? node.properties['.'] : node;
|
|
553
|
+
};
|
|
478
554
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
555
|
+
export function* traverseProperties(properties) {
|
|
556
|
+
for (const value of Object.values(properties)) {
|
|
557
|
+
if (isArray(value)) {
|
|
558
|
+
yield* btree.traverse(value);
|
|
559
|
+
} else {
|
|
560
|
+
yield value;
|
|
482
561
|
}
|
|
483
562
|
}
|
|
484
|
-
}
|
|
563
|
+
}
|
|
485
564
|
|
|
486
565
|
export class Resolver {
|
|
487
566
|
constructor(
|
|
@@ -494,6 +573,7 @@ export class Resolver {
|
|
|
494
573
|
this.reference = reference;
|
|
495
574
|
this.popped = popped;
|
|
496
575
|
this.held = held;
|
|
576
|
+
this.doctype = null;
|
|
497
577
|
}
|
|
498
578
|
|
|
499
579
|
get idx() {
|
|
@@ -552,16 +632,23 @@ export class Resolver {
|
|
|
552
632
|
break;
|
|
553
633
|
}
|
|
554
634
|
|
|
555
|
-
case OpenNodeTag:
|
|
635
|
+
case OpenNodeTag:
|
|
636
|
+
case OpenFragmentTag: {
|
|
556
637
|
const { flags } = tag.value;
|
|
557
638
|
const isRootNode = states.size === 1;
|
|
558
639
|
|
|
559
|
-
if (
|
|
560
|
-
throw new Error();
|
|
561
|
-
}
|
|
640
|
+
if (tag.type === OpenFragmentTag && (!isRootNode || this.reference)) throw new Error();
|
|
562
641
|
|
|
563
|
-
if (
|
|
564
|
-
this.
|
|
642
|
+
if (flags.trivia || flags.escape) {
|
|
643
|
+
if (this.reference?.type === ReferenceTag)
|
|
644
|
+
throw new Error('embedded nodes cannot follow references');
|
|
645
|
+
if (this.reference?.type !== EmbeddedNode) {
|
|
646
|
+
this.states = states.push({ properties: new Map(), idx: 0 });
|
|
647
|
+
}
|
|
648
|
+
} else {
|
|
649
|
+
if (!isRootNode && !this.reference) {
|
|
650
|
+
throw new Error();
|
|
651
|
+
}
|
|
565
652
|
}
|
|
566
653
|
|
|
567
654
|
this.reference = null;
|
|
@@ -611,10 +698,21 @@ export class Resolver {
|
|
|
611
698
|
break;
|
|
612
699
|
}
|
|
613
700
|
|
|
614
|
-
case CloseNodeTag:
|
|
701
|
+
case CloseNodeTag:
|
|
702
|
+
case CloseFragmentTag: {
|
|
615
703
|
this.states = states.pop();
|
|
616
704
|
this.popped = true;
|
|
705
|
+
break;
|
|
617
706
|
}
|
|
707
|
+
|
|
708
|
+
case DoctypeTag:
|
|
709
|
+
this.doctype = tag;
|
|
710
|
+
break;
|
|
711
|
+
case LiteralTag:
|
|
712
|
+
break;
|
|
713
|
+
|
|
714
|
+
default:
|
|
715
|
+
throw new Error();
|
|
618
716
|
}
|
|
619
717
|
|
|
620
718
|
return this;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bablr/agast-helpers",
|
|
3
3
|
"description": "Helper functions for working with agAST trees",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.1",
|
|
5
5
|
"author": "Conrad Buck<conartist6@gmail.com>",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"sideEffects": false,
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@bablr/btree": "0.1.0",
|
|
23
|
+
"@bablr/btree": "^0.1.0",
|
|
24
24
|
"@bablr/coroutine": "0.1.0",
|
|
25
25
|
"@iter-tools/imm-stack": "1.1.0"
|
|
26
26
|
},
|