@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 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: 'EmbeddedExpression', value: expr });
44
+ return freeze({ type: EmbeddedExpression, value: expr });
10
45
  };
11
46
 
12
47
  export const buildEmbeddedTag = (tag) => {
13
- return freeze({ type: 'EmbeddedTag', value: tag });
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
- buildEmbeddedExpression(
23
- freeze({
24
- verb: 'write',
25
- value: buildEmbeddedExpression(
26
- freeze({ text, options: buildEmbeddedExpression(freeze(options)) }),
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
- buildEmbeddedExpression(
36
- freeze({
37
- verb: 'ansi-push',
38
- value: buildEmbeddedExpression(
39
- freeze({ spans: spans === '' ? freeze([]) : freeze(spans.split(' ')) }),
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(buildEmbeddedExpression(freeze({ verb: 'ansi-pop', value: undefined })));
78
+ return buildEffect(freeze({ verb: 'ansi-pop', value: undefined }));
48
79
  };
49
80
 
50
81
  export const buildTokenGroup = (tokens) => {
51
- return freeze({ type: 'TokenGroup', value: tokens });
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 buildReference = (name, isArray) => {
63
- return freeze({ type: 'Reference', value: freeze({ name, isArray }) });
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 buildNull = () => {
67
- return freeze({ type: 'Null', value: undefined });
97
+ export const buildNullTag = () => {
98
+ return freeze({ type: NullTag, value: undefined });
68
99
  };
69
100
 
70
- export const buildGap = () => {
71
- return freeze({ type: 'Gap', value: undefined });
101
+ export const buildArrayTag = () => {
102
+ return freeze({ type: ArrayTag, value: undefined });
72
103
  };
73
104
 
74
- export const buildShift = () => {
75
- return freeze({ type: 'Shift', value: undefined });
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: 'EmbeddedNode', value: node });
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: 'DoctypeTag',
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: 'OpenNodeTag',
126
+ type: OpenNodeTag,
100
127
  value: freeze({
101
- flags: freeze({ token, trivia, escape, intrinsic, expression }),
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: 'CloseNodeTag', value: freeze({ language, 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 buildLiteral = (value) => {
159
+ export const buildLiteralTag = (value) => {
116
160
  if (!isString(value)) throw new Error('invalid literal');
117
- return freeze({ type: 'Literal', value });
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
- freeze({
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: freeze(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
- export const buildNode = (language, type, children = [], properties = {}, attributes = {}) =>
146
- freeze({
147
- flags: nodeFlags,
148
- language,
149
- type,
150
- children: freeze(children),
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, attributes = {}) =>
164
- freeze({
165
- flags: syntacticFlags,
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 = (language, type, children = [], properties = {}, attributes = {}) =>
199
- freeze({
200
- flags: escapeFlags,
201
- language,
202
- type,
203
- children: freeze(children),
204
- properties: freeze(properties),
205
- attributes: freeze(attributes),
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
- freeze({
224
- flags: syntacticEscapeFlags,
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
- freeze({
248
- flags: syntacticTriviaFlags,
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 = (language, type, children = [], properties = {}, attributes = {}) =>
265
- freeze({
266
- flags: triviaFlags,
267
- language,
268
- type,
269
- children: freeze(children),
270
- properties: freeze(properties),
271
- attributes: freeze(attributes),
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([buildNull()]),
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
- const isArray = str.endsWith('[]');
3
- const name = isArray ? str.slice(0, -2) : str;
2
+ let name = str;
3
+ const hasGap = name.endsWith('$');
4
4
 
5
- if (!/^[a-zA-Z]+$/.test(name)) throw new Error();
5
+ if (hasGap) name = name.slice(0, -1);
6
6
 
7
- return { isArray, name };
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
  };