@bablr/agast-helpers 0.3.2 → 0.5.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/btree.js +1 -0
- package/lib/builders.js +191 -123
- package/lib/path.js +16 -6
- package/lib/print.js +101 -54
- package/lib/shorthand.js +16 -16
- package/lib/stream.js +205 -103
- package/lib/symbols.js +18 -0
- package/lib/template.js +63 -24
- package/lib/tree.js +412 -182
- package/package.json +3 -1
package/lib/btree.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@bablr/btree';
|
package/lib/builders.js
CHANGED
|
@@ -1,16 +1,51 @@
|
|
|
1
1
|
import * as sym from './symbols.js';
|
|
2
|
+
import * as btree from './btree.js';
|
|
3
|
+
import {
|
|
4
|
+
DoctypeTag,
|
|
5
|
+
OpenNodeTag,
|
|
6
|
+
CloseNodeTag,
|
|
7
|
+
OpenFragmentTag,
|
|
8
|
+
CloseFragmentTag,
|
|
9
|
+
ReferenceTag,
|
|
10
|
+
ShiftTag,
|
|
11
|
+
GapTag,
|
|
12
|
+
NullTag,
|
|
13
|
+
ArrayTag,
|
|
14
|
+
LiteralTag,
|
|
15
|
+
EmbeddedNode,
|
|
16
|
+
EmbeddedExpression,
|
|
17
|
+
EmbeddedTag,
|
|
18
|
+
TokenGroup,
|
|
19
|
+
} from './symbols.js';
|
|
2
20
|
|
|
3
21
|
const { freeze } = Object;
|
|
22
|
+
const { isArray } = Array;
|
|
4
23
|
|
|
5
24
|
const isObject = (val) => val !== null && typeof value !== 'object';
|
|
6
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
|
+
|
|
7
42
|
export const buildEmbeddedExpression = (expr) => {
|
|
8
43
|
if (!isObject(expr)) return expr;
|
|
9
|
-
return freeze({ type:
|
|
44
|
+
return freeze({ type: EmbeddedExpression, value: expr });
|
|
10
45
|
};
|
|
11
46
|
|
|
12
47
|
export const buildEmbeddedTag = (tag) => {
|
|
13
|
-
return freeze({ type:
|
|
48
|
+
return freeze({ type: EmbeddedTag, value: tag });
|
|
14
49
|
};
|
|
15
50
|
|
|
16
51
|
export const buildEffect = (value) => {
|
|
@@ -19,36 +54,32 @@ export const buildEffect = (value) => {
|
|
|
19
54
|
|
|
20
55
|
export const buildWriteEffect = (text, options = {}) => {
|
|
21
56
|
return buildEffect(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}),
|
|
29
|
-
),
|
|
57
|
+
freeze({
|
|
58
|
+
verb: 'write',
|
|
59
|
+
value: buildEmbeddedExpression(
|
|
60
|
+
freeze({ text, options: buildEmbeddedExpression(freeze(options)) }),
|
|
61
|
+
),
|
|
62
|
+
}),
|
|
30
63
|
);
|
|
31
64
|
};
|
|
32
65
|
|
|
33
66
|
export const buildAnsiPushEffect = (spans = '') => {
|
|
34
67
|
return buildEffect(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}),
|
|
42
|
-
),
|
|
68
|
+
freeze({
|
|
69
|
+
verb: 'ansi-push',
|
|
70
|
+
value: buildEmbeddedExpression(
|
|
71
|
+
freeze({ spans: spans === '' ? freeze([]) : freeze(spans.split(' ')) }),
|
|
72
|
+
),
|
|
73
|
+
}),
|
|
43
74
|
);
|
|
44
75
|
};
|
|
45
76
|
|
|
46
77
|
export const buildAnsiPopEffect = () => {
|
|
47
|
-
return buildEffect(
|
|
78
|
+
return buildEffect(freeze({ verb: 'ansi-pop', value: undefined }));
|
|
48
79
|
};
|
|
49
80
|
|
|
50
81
|
export const buildTokenGroup = (tokens) => {
|
|
51
|
-
return freeze({ type:
|
|
82
|
+
return freeze({ type: TokenGroup, value: tokens });
|
|
52
83
|
};
|
|
53
84
|
|
|
54
85
|
export const buildCall = (verb, ...args) => {
|
|
@@ -59,46 +90,42 @@ export const buildBeginningOfStreamToken = () => {
|
|
|
59
90
|
return freeze({ type: Symbol.for('@bablr/beginning-of-stream'), value: undefined });
|
|
60
91
|
};
|
|
61
92
|
|
|
62
|
-
export const
|
|
63
|
-
return freeze({ type:
|
|
93
|
+
export const buildReferenceTag = (name, isArray = false, hasGap = false) => {
|
|
94
|
+
return freeze({ type: ReferenceTag, value: freeze({ name, isArray, hasGap }) });
|
|
64
95
|
};
|
|
65
96
|
|
|
66
|
-
export const
|
|
67
|
-
return freeze({ type:
|
|
97
|
+
export const buildNullTag = () => {
|
|
98
|
+
return freeze({ type: NullTag, value: undefined });
|
|
68
99
|
};
|
|
69
100
|
|
|
70
|
-
export const
|
|
71
|
-
return freeze({ type:
|
|
101
|
+
export const buildArrayTag = () => {
|
|
102
|
+
return freeze({ type: ArrayTag, value: undefined });
|
|
72
103
|
};
|
|
73
104
|
|
|
74
|
-
export const
|
|
75
|
-
return freeze({ type:
|
|
105
|
+
export const buildGapTag = () => {
|
|
106
|
+
return freeze({ type: GapTag, value: undefined });
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export const buildShiftTag = () => {
|
|
110
|
+
return freeze({ type: ShiftTag, value: undefined });
|
|
76
111
|
};
|
|
77
112
|
|
|
78
113
|
export const buildEmbeddedNode = (node) => {
|
|
79
|
-
return freeze({ type:
|
|
114
|
+
return freeze({ type: EmbeddedNode, value: node });
|
|
80
115
|
};
|
|
81
116
|
|
|
82
|
-
export const buildDoctypeTag = (attributes) => {
|
|
117
|
+
export const buildDoctypeTag = (attributes = {}) => {
|
|
83
118
|
return freeze({
|
|
84
|
-
type:
|
|
119
|
+
type: DoctypeTag,
|
|
85
120
|
value: { doctype: 'cstml', version: 0, attributes: freeze(attributes) },
|
|
86
121
|
});
|
|
87
122
|
};
|
|
88
123
|
|
|
89
124
|
export const buildNodeOpenTag = (flags = {}, language = null, type = null, attributes = {}) => {
|
|
90
|
-
let { token, trivia, escape, expression, intrinsic } = flags;
|
|
91
|
-
|
|
92
|
-
token = !!token;
|
|
93
|
-
trivia = !!trivia;
|
|
94
|
-
escape = !!escape;
|
|
95
|
-
expression = !!expression;
|
|
96
|
-
intrinsic = !!intrinsic;
|
|
97
|
-
|
|
98
125
|
return freeze({
|
|
99
|
-
type:
|
|
126
|
+
type: OpenNodeTag,
|
|
100
127
|
value: freeze({
|
|
101
|
-
flags: freeze(
|
|
128
|
+
flags: freeze(flags),
|
|
102
129
|
language,
|
|
103
130
|
type,
|
|
104
131
|
attributes,
|
|
@@ -107,14 +134,31 @@ export const buildNodeOpenTag = (flags = {}, language = null, type = null, attri
|
|
|
107
134
|
};
|
|
108
135
|
|
|
109
136
|
export const buildNodeCloseTag = (type = null, language = null) => {
|
|
110
|
-
return freeze({ type:
|
|
137
|
+
return freeze({ type: CloseNodeTag, value: freeze({ language, type }) });
|
|
138
|
+
};
|
|
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 });
|
|
111
155
|
};
|
|
112
156
|
|
|
113
157
|
const isString = (val) => typeof val === 'string';
|
|
114
158
|
|
|
115
|
-
export const
|
|
159
|
+
export const buildLiteralTag = (value) => {
|
|
116
160
|
if (!isString(value)) throw new Error('invalid literal');
|
|
117
|
-
return freeze({ type:
|
|
161
|
+
return freeze({ type: LiteralTag, value });
|
|
118
162
|
};
|
|
119
163
|
|
|
120
164
|
export const buildNodeWithFlags = (
|
|
@@ -124,93 +168,117 @@ export const buildNodeWithFlags = (
|
|
|
124
168
|
children = [],
|
|
125
169
|
properties = {},
|
|
126
170
|
attributes = {},
|
|
127
|
-
) =>
|
|
128
|
-
|
|
171
|
+
) => {
|
|
172
|
+
const openTag = buildNodeOpenTag(flags, language, type, attributes);
|
|
173
|
+
const closeTag = buildNodeCloseTag(type);
|
|
174
|
+
|
|
175
|
+
return freeze({
|
|
129
176
|
flags,
|
|
130
177
|
language,
|
|
131
178
|
type,
|
|
132
|
-
children:
|
|
179
|
+
children: btree.addAt(0, btree.addAt(btree.getSum(children), children, closeTag), openTag),
|
|
133
180
|
properties: freeze(properties),
|
|
134
181
|
attributes: freeze(attributes),
|
|
135
182
|
});
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const flagsWithGap = new WeakMap();
|
|
186
|
+
|
|
187
|
+
export const getFlagsWithGap = (flags) => flagsWithGap.get(flags);
|
|
136
188
|
|
|
137
189
|
export const nodeFlags = freeze({
|
|
138
190
|
token: false,
|
|
139
191
|
escape: false,
|
|
140
192
|
trivia: false,
|
|
141
|
-
intrinsic: false,
|
|
142
193
|
expression: false,
|
|
194
|
+
hasGap: false,
|
|
143
195
|
});
|
|
144
196
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
+
|
|
218
|
+
export const buildNode = (language, type, children = [], properties = {}, attributes = {}) => {
|
|
219
|
+
const flags = getFlags(nodeFlags, children, properties);
|
|
220
|
+
return buildNodeWithFlags(flags, language, type, children, properties, attributes);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const buildFragmentWithFlags = (flags, children = [], properties = {}, attributes = {}) => {
|
|
224
|
+
const doctypeTag = buildDoctypeTag(attributes);
|
|
225
|
+
const openTag = buildFragmentOpenTag(flags);
|
|
226
|
+
const closeTag = buildFragmentCloseTag();
|
|
227
|
+
|
|
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
|
+
),
|
|
151
235
|
properties: freeze(properties),
|
|
152
236
|
attributes: freeze(attributes),
|
|
153
237
|
});
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
export const buildFragment = (children = [], properties = {}, attributes = {}) => {
|
|
241
|
+
const flags = getFlags(nodeFlags, children, properties);
|
|
242
|
+
return buildFragmentWithFlags(flags, children, properties, attributes);
|
|
243
|
+
};
|
|
154
244
|
|
|
155
245
|
export const syntacticFlags = freeze({
|
|
156
246
|
token: true,
|
|
157
247
|
escape: false,
|
|
158
248
|
trivia: false,
|
|
159
|
-
intrinsic: false,
|
|
160
249
|
expression: false,
|
|
250
|
+
hasGap: false,
|
|
161
251
|
});
|
|
162
252
|
|
|
163
|
-
export const buildSyntacticNode = (language, type, value
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
language,
|
|
167
|
-
type,
|
|
168
|
-
children: [buildLiteral(value)],
|
|
169
|
-
properties: freeze({}),
|
|
170
|
-
attributes: freeze(attributes),
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
export const syntacticIntrinsicFlags = freeze({
|
|
174
|
-
token: true,
|
|
175
|
-
escape: false,
|
|
176
|
-
trivia: false,
|
|
177
|
-
intrinsic: true,
|
|
178
|
-
expression: false,
|
|
179
|
-
});
|
|
180
|
-
export const buildSyntacticIntrinsicNode = (language, type, value, attributes = {}) =>
|
|
181
|
-
freeze({
|
|
182
|
-
flags: syntacticIntrinsicFlags,
|
|
183
|
-
language,
|
|
184
|
-
type,
|
|
185
|
-
children: [buildLiteral(value)],
|
|
186
|
-
properties: freeze({}),
|
|
187
|
-
attributes: freeze(attributes),
|
|
188
|
-
});
|
|
253
|
+
export const buildSyntacticNode = (language, type, value) => {
|
|
254
|
+
return buildNodeWithFlags(syntacticFlags, language, type, [buildLiteralTag(value)]);
|
|
255
|
+
};
|
|
189
256
|
|
|
190
257
|
export const escapeFlags = freeze({
|
|
191
258
|
token: false,
|
|
192
259
|
escape: true,
|
|
193
260
|
trivia: false,
|
|
194
|
-
intrinsic: false,
|
|
195
261
|
expression: false,
|
|
262
|
+
hasGap: false,
|
|
196
263
|
});
|
|
197
264
|
|
|
198
|
-
export const buildEscapeNode = (
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
265
|
+
export const buildEscapeNode = (
|
|
266
|
+
language,
|
|
267
|
+
type,
|
|
268
|
+
children = [],
|
|
269
|
+
properties = {},
|
|
270
|
+
attributes = {},
|
|
271
|
+
) => {
|
|
272
|
+
const flags = getFlags(escapeFlags, children, properties);
|
|
273
|
+
return buildNodeWithFlags(flags, language, type, children, properties, attributes);
|
|
274
|
+
};
|
|
207
275
|
|
|
208
276
|
export const syntacticEscapeFlags = freeze({
|
|
209
277
|
token: true,
|
|
210
278
|
escape: true,
|
|
211
279
|
trivia: false,
|
|
212
|
-
intrinsic: false,
|
|
213
280
|
expression: false,
|
|
281
|
+
hasGap: false,
|
|
214
282
|
});
|
|
215
283
|
|
|
216
284
|
export const buildSyntacticEscapeNode = (
|
|
@@ -219,22 +287,16 @@ export const buildSyntacticEscapeNode = (
|
|
|
219
287
|
children = [],
|
|
220
288
|
properties = {},
|
|
221
289
|
attributes = {},
|
|
222
|
-
) =>
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
language,
|
|
226
|
-
type,
|
|
227
|
-
children: freeze(children),
|
|
228
|
-
properties: freeze(properties),
|
|
229
|
-
attributes: freeze(attributes),
|
|
230
|
-
});
|
|
290
|
+
) => {
|
|
291
|
+
return buildNodeWithFlags(syntacticEscapeFlags, language, type, children, properties, attributes);
|
|
292
|
+
};
|
|
231
293
|
|
|
232
294
|
export const syntacticTriviaFlags = freeze({
|
|
233
295
|
token: true,
|
|
234
296
|
escape: false,
|
|
235
297
|
trivia: true,
|
|
236
|
-
intrinsic: false,
|
|
237
298
|
expression: false,
|
|
299
|
+
hasGap: false,
|
|
238
300
|
});
|
|
239
301
|
|
|
240
302
|
export const buildSyntacticTriviaNode = (
|
|
@@ -243,40 +305,46 @@ export const buildSyntacticTriviaNode = (
|
|
|
243
305
|
children = [],
|
|
244
306
|
properties = {},
|
|
245
307
|
attributes = {},
|
|
246
|
-
) =>
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
language,
|
|
250
|
-
type,
|
|
251
|
-
children: freeze(children),
|
|
252
|
-
properties: freeze(properties),
|
|
253
|
-
attributes: freeze(attributes),
|
|
254
|
-
});
|
|
308
|
+
) => {
|
|
309
|
+
return buildNodeWithFlags(syntacticTriviaFlags, language, type, children, properties, attributes);
|
|
310
|
+
};
|
|
255
311
|
|
|
256
312
|
export const triviaFlags = freeze({
|
|
257
313
|
token: false,
|
|
258
314
|
escape: false,
|
|
259
315
|
trivia: true,
|
|
260
|
-
intrinsic: false,
|
|
261
316
|
expression: false,
|
|
317
|
+
hasGap: false,
|
|
262
318
|
});
|
|
263
319
|
|
|
264
|
-
export const buildTriviaNode = (
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
320
|
+
export const buildTriviaNode = (
|
|
321
|
+
language,
|
|
322
|
+
type,
|
|
323
|
+
children = [],
|
|
324
|
+
properties = {},
|
|
325
|
+
attributes = {},
|
|
326
|
+
) => {
|
|
327
|
+
const flags = getFlags(triviaFlags, children, properties);
|
|
328
|
+
return buildNodeWithFlags(flags, language, type, children, properties, attributes);
|
|
329
|
+
};
|
|
273
330
|
|
|
274
|
-
export const buildNullNode = () => {
|
|
331
|
+
export const buildNullNode = (nullToken = buildNullTag()) => {
|
|
275
332
|
return freeze({
|
|
276
333
|
flags: nodeFlags,
|
|
277
334
|
language: null,
|
|
278
335
|
type: sym.null,
|
|
279
|
-
children: freeze([
|
|
336
|
+
children: btree.freeze([nullToken]),
|
|
337
|
+
properties: freeze({}),
|
|
338
|
+
attributes: freeze({}),
|
|
339
|
+
});
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export const buildGapNode = (gapToken = buildGapTag()) => {
|
|
343
|
+
return freeze({
|
|
344
|
+
flags: getGapFlags(nodeFlags),
|
|
345
|
+
language: null,
|
|
346
|
+
type: sym.gap,
|
|
347
|
+
children: btree.freeze([gapToken]),
|
|
280
348
|
properties: freeze({}),
|
|
281
349
|
attributes: freeze({}),
|
|
282
350
|
});
|
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
|
};
|