@bablr/agast-helpers 0.7.0 → 0.8.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/print.js CHANGED
@@ -8,8 +8,10 @@ import {
8
8
  NullTag,
9
9
  InitializerTag,
10
10
  LiteralTag,
11
+ AttributeDefinition,
12
+ BindingTag,
11
13
  } from './symbols.js';
12
- import { isEmptyReference, isGapNode, isNullNode, printSource, referenceFlags } from './tree.js';
14
+ import { referenceFlags } from './tree.js';
13
15
 
14
16
  let { isInteger, isFinite } = Number;
15
17
  let { isArray } = Array;
@@ -30,39 +32,6 @@ export const printObject = (obj) => {
30
32
  : '{}';
31
33
  };
32
34
 
33
- export const printPropertyMatcher = (matcher) => {
34
- let { refMatcher, nodeMatcher } = matcher;
35
- let ref = { type: ReferenceTag, value: refMatcher };
36
- let refPart = refMatcher && !isEmptyReference(ref) ? `${printReferenceTag(ref)} ` : '';
37
- let nodePart;
38
-
39
- if (isArray(nodeMatcher)) {
40
- if (nodeMatcher.length) throw new Error();
41
- nodePart = '[]';
42
- } else if (isGapNode(nodeMatcher)) {
43
- nodePart = '<//>';
44
- } else if (isNullNode(nodeMatcher)) {
45
- nodePart = 'null';
46
- } else {
47
- nodePart = printOpenNodeMatcher(nodeMatcher);
48
- }
49
- return `${refPart}${nodePart}`;
50
- };
51
-
52
- export const printOpenNodeMatcher = (matcher) => {
53
- let { flags, language: tagLanguage, type, attributes, intrinsicValue } = matcher;
54
-
55
- let printedAttributes = printAttributes(attributes);
56
- let attributesFrag = printedAttributes ? ` ${printedAttributes}` : '';
57
-
58
- let intrinsicFrag = intrinsicValue
59
- ? ` ${isString(intrinsicValue) ? printExpression(intrinsicValue) : printSource(intrinsicValue)}`
60
- : '';
61
- let typeFrag = type !== Symbol.for('@bablr/fragment') ? printTagPath(tagLanguage, type) : ' ';
62
-
63
- return `<${printNodeFlags(flags)}${typeFrag}${intrinsicFrag}${attributesFrag}/>`;
64
- };
65
-
66
35
  export const printExpression = (expr) => {
67
36
  if (isString(expr)) {
68
37
  return printString(expr);
@@ -91,19 +60,8 @@ export const printAttributes = (attributes) => {
91
60
  return !printed || printed === '{}' ? '' : printed;
92
61
  };
93
62
 
94
- export const printLanguage = (language) => {
95
- if (isString(language)) {
96
- return printSingleString(language);
97
- } else {
98
- return language.join('.');
99
- }
100
- };
101
-
102
- export const printTagPath = (language, type) => {
103
- return [
104
- ...when(type && language?.length, () => [printLanguage(language)]),
105
- ...when(type, [printType(type)]),
106
- ].join(':');
63
+ export const printIdentifierPath = (path) => {
64
+ return path.map(printIdentifier).join('.');
107
65
  };
108
66
 
109
67
  let escapeReplacer = (esc) => {
@@ -122,6 +80,14 @@ let escapeReplacer = (esc) => {
122
80
  }
123
81
  };
124
82
 
83
+ export const printIdentifier = (id) => {
84
+ return /^[a-zA-Z\u{80}-\u{10ffff}][a-zA-Z\u{80}-\u{10ffff}0-9_-]+$/u.test(id)
85
+ ? id
86
+ : `\`${id
87
+ .replace(/[`\\]/g, '\\`')
88
+ .replace(/[\x00-\x20\x7f]/g, (value) => `\\u00${value.toString(16)}`)}\``;
89
+ };
90
+
125
91
  export const printSingleString = (str) => {
126
92
  return `'${str.replace(/['\\\0\r\n\t\u0000-\u001A]/g, escapeReplacer)}'`;
127
93
  };
@@ -159,10 +125,18 @@ export const printShiftTag = (tag) => {
159
125
  export const printReferenceTag = (tag) => {
160
126
  if (tag?.type !== ReferenceTag) throw new Error();
161
127
 
162
- let { name, isArray, flags, index } = tag.value;
163
- let pathBraces = isArray ? `[${index || ''}]` : '';
128
+ let { type, name, isArray, flags } = tag.value;
129
+ let pathBraces = isArray ? `[]` : '';
164
130
 
165
- return `${name || ''}${pathBraces}${printReferenceFlags(flags)}:`;
131
+ if (type && name) throw new Error();
132
+ if (type && !['.', '#', '@'].includes(type)) throw new Error();
133
+
134
+ return `${type || printIdentifier(name) || ''}${pathBraces}${printReferenceFlags(flags)}:`;
135
+ };
136
+
137
+ export const printBindingTag = (tag) => {
138
+ let { languagePath } = tag.value;
139
+ return `:${languagePath ? printIdentifierPath(languagePath) : ''}:`;
166
140
  };
167
141
 
168
142
  export const printNullTag = (tag) => {
@@ -177,7 +151,7 @@ export const printType = (type) => {
177
151
  return typeof type === 'string'
178
152
  ? type
179
153
  : typeof type === 'symbol'
180
- ? type.description.replace('@bablr/', '')
154
+ ? printIdentifier(type.description)
181
155
  : String(type);
182
156
  };
183
157
 
@@ -209,40 +183,35 @@ export const printNodeFlags = (flags) => {
209
183
  if (flags.cover && !flags.fragment) throw new Error();
210
184
  let star = flags.token ? '*' : '';
211
185
  let dollar = flags.hasGap ? '$' : '';
212
- let frag = flags.fragment ? '_' : '';
213
- let cover = flags.cover ? '_' : '';
214
186
 
215
- return `${star}${dollar}${frag}${cover}`;
187
+ return `${star}${dollar}`;
216
188
  };
217
189
 
218
190
  export const printOpenNodeTag = (tag) => {
219
191
  if (tag?.type !== OpenNodeTag) throw new Error();
220
192
 
221
- let { flags, language: tagLanguage, type, attributes } = tag.value;
193
+ let { flags, type, attributes } = tag.value;
222
194
 
223
195
  if (!type) {
224
- return `<${printNodeFlags(flags)}>`;
196
+ return `<${printNodeFlags(flags)}_>`;
225
197
  }
226
198
 
227
199
  let printedAttributes = printAttributes(attributes);
228
200
  let attributesFrag = printedAttributes ? ` ${printedAttributes}` : '';
229
201
 
230
- return `<${printNodeFlags(flags)}${printTagPath(tagLanguage, type)}${attributesFrag}>`;
202
+ return `<${printNodeFlags(flags)}${printType(type)}${attributesFrag}>`;
231
203
  };
232
204
 
233
205
  export const printSelfClosingNodeTag = (tag, intrinsicValue) => {
234
206
  if (tag?.type !== OpenNodeTag) throw new Error();
235
207
 
236
- let { flags, language: tagLanguage, type, attributes } = tag.value;
208
+ let { flags, type, attributes } = tag.value;
237
209
 
238
210
  let printedAttributes = printAttributes(attributes);
239
211
  let attributesFrag = printedAttributes ? ` ${printedAttributes}` : '';
240
212
  let intrinsicFrag = intrinsicValue ? ` ${printString(intrinsicValue)}` : '';
241
213
 
242
- return `<${printNodeFlags(flags)}${printTagPath(
243
- tagLanguage,
244
- type,
245
- )}${intrinsicFrag}${attributesFrag} />`;
214
+ return `<${printNodeFlags(flags)}${printType(type)}${intrinsicFrag}${attributesFrag} />`;
246
215
  };
247
216
 
248
217
  export const printCloseNodeTag = (tag) => {
@@ -251,38 +220,31 @@ export const printCloseNodeTag = (tag) => {
251
220
  return `</>`;
252
221
  };
253
222
 
254
- export const printTag = (tag) => {
255
- if (!isObject(tag)) throw new Error();
256
-
257
- switch (tag?.type || NullTag) {
258
- case NullTag:
259
- return printNullTag(tag);
260
-
261
- case GapTag:
262
- return printGapTag(tag);
263
-
264
- case InitializerTag:
265
- return printInitializerTag(tag);
223
+ export const printAttributeDefinition = (tag) => {
224
+ if (tag?.type !== AttributeDefinition) throw new Error();
225
+ let { path, value } = tag.value;
266
226
 
267
- case ShiftTag:
268
- return printShiftTag(tag);
269
-
270
- case LiteralTag:
271
- return printLiteralTag(tag);
272
-
273
- case DoctypeTag:
274
- return printDoctypeTag(tag);
227
+ return `{ ${printIdentifierPath(path)}: ${printExpression(value)} }`;
228
+ };
275
229
 
276
- case ReferenceTag:
277
- return printReferenceTag(tag);
230
+ const printers = {
231
+ [NullTag]: printNullTag,
232
+ [GapTag]: printGapTag,
233
+ [BindingTag]: printBindingTag,
234
+ [InitializerTag]: printInitializerTag,
235
+ [ShiftTag]: printShiftTag,
236
+ [LiteralTag]: printLiteralTag,
237
+ [DoctypeTag]: printDoctypeTag,
238
+ [ReferenceTag]: printReferenceTag,
239
+ [OpenNodeTag]: printOpenNodeTag,
240
+ [CloseNodeTag]: printCloseNodeTag,
241
+ [AttributeDefinition]: printAttributeDefinition,
242
+ };
278
243
 
279
- case OpenNodeTag:
280
- return printOpenNodeTag(tag);
244
+ export const printTag = (tag) => {
245
+ if (!isObject(tag)) throw new Error();
281
246
 
282
- case CloseNodeTag:
283
- return printCloseNodeTag(tag);
247
+ let printer = printers[tag?.type];
284
248
 
285
- default:
286
- throw new Error();
287
- }
249
+ return printer(tag);
288
250
  };
package/lib/shorthand.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  buildLiteralTag,
7
7
  buildInitializerTag,
8
8
  } from './builders.js';
9
- import { parseReference, treeFromStreamSync } from './tree.js';
9
+ import { buildReferenceTag, treeFromStreamSync } from './tree.js';
10
10
 
11
11
  export * from './builders.js';
12
12
 
@@ -23,6 +23,31 @@ const stripArray = (val) => {
23
23
  }
24
24
  };
25
25
 
26
+ export const parseReference = (str) => {
27
+ let {
28
+ 1: type,
29
+ 2: name,
30
+ 3: isArray,
31
+ 4: index,
32
+ 5: expressionToken,
33
+ 6: hasGapToken,
34
+ } = /^\s*(?:([.#@])|([a-zA-Z\u{80}-\u{10ffff}][a-zA-Z0-9_\u{80}-\u{10ffff}-]*))\s*(\[\s*(\d+\s*)?\])?\s*(\+)?(\$)?\s*$/u.exec(
35
+ str,
36
+ );
37
+
38
+ let flags = {
39
+ expression: !!expressionToken,
40
+ hasGap: !!hasGapToken,
41
+ };
42
+
43
+ index = index ? parseInt(index, 10) : null;
44
+ isArray = !!isArray;
45
+ type = type || null;
46
+ name = name || null;
47
+
48
+ return buildReferenceTag(type, name, isArray, flags, index);
49
+ };
50
+
26
51
  export const ref = (path) => {
27
52
  return parseReference(isArray(path) ? path[0] : path);
28
53
  };
package/lib/stream.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Coroutine } from '@bablr/coroutine';
2
2
  import emptyStack from '@iter-tools/imm-stack';
3
3
  import { printSelfClosingNodeTag, printTag } from './print.js';
4
- import { buildTokenGroup } from './builders.js';
4
+ import { buildOpenNodeTag, buildTokenGroup } from './builders.js';
5
5
  import {
6
6
  DoctypeTag,
7
7
  OpenNodeTag,
@@ -13,6 +13,8 @@ import {
13
13
  InitializerTag,
14
14
  LiteralTag,
15
15
  TokenGroup,
16
+ BindingTag,
17
+ AttributeDefinition,
16
18
  } from './symbols.js';
17
19
 
18
20
  export * from './print.js';
@@ -120,6 +122,10 @@ export class StreamGenerator {
120
122
  return this.generator.return(value);
121
123
  }
122
124
 
125
+ [Symbol.iterator]() {
126
+ return this;
127
+ }
128
+
123
129
  [Symbol.for('@@streamIterator')]() {
124
130
  return this;
125
131
  }
@@ -175,7 +181,7 @@ function* __isEmpty(tags) {
175
181
  case OpenNodeTag:
176
182
  ++depth;
177
183
 
178
- if (depth === 0 && ref.value.name === '@') {
184
+ if (depth === 0 && ref.value.type === '@') {
179
185
  return false;
180
186
  }
181
187
 
@@ -319,8 +325,17 @@ function* __prettyGroupTags(tags) {
319
325
 
320
326
  if (
321
327
  (tag.type === 'Effect' && tag.value.verb === 'write') ||
322
- [ReferenceTag, DoctypeTag, GapTag, NullTag, InitializerTag, ShiftTag].includes(tag.type) ||
323
- (tag.type === OpenNodeTag && (!tag.value.type || ref?.value.name === '@'))
328
+ [
329
+ ReferenceTag,
330
+ DoctypeTag,
331
+ BindingTag,
332
+ GapTag,
333
+ NullTag,
334
+ InitializerTag,
335
+ ShiftTag,
336
+ AttributeDefinition,
337
+ ].includes(tag.type) ||
338
+ (tag.type === OpenNodeTag && (!tag.value.type || ref?.value.type === '@'))
324
339
  ) {
325
340
  state.broken = true;
326
341
 
@@ -352,12 +367,7 @@ function* __prettyGroupTags(tags) {
352
367
  }
353
368
 
354
369
  if (tag.type === OpenNodeTag) {
355
- if (!tag.value.type) {
356
- states = states.push({ holding: [], broken: false, open: tag });
357
- yield tag;
358
- } else {
359
- states = states.push({ holding: [tag], broken: false, open: tag });
360
- }
370
+ states = states.push({ holding: [tag], broken: false, open: tag });
361
371
 
362
372
  state = states.value;
363
373
  }
@@ -393,12 +403,17 @@ function* __generatePrettyCSTML(tags, options) {
393
403
  continue;
394
404
  }
395
405
 
406
+ if (tag.type === BindingTag && !tag.value.languagePath?.length) {
407
+ continue;
408
+ }
409
+
396
410
  inline =
397
411
  inlineOption &&
398
412
  inline &&
399
413
  ref &&
400
414
  (tag.type === NullTag ||
401
415
  tag.type === GapTag ||
416
+ tag.type === BindingTag ||
402
417
  tag.type === InitializerTag ||
403
418
  tag.type === TokenGroup);
404
419
 
@@ -421,7 +436,12 @@ function* __generatePrettyCSTML(tags, options) {
421
436
  if (tag.type === TokenGroup) {
422
437
  ref = null;
423
438
  const intrinsicValue = tag.value[0].value.flags.token ? getCooked(tag.value) : null;
424
- yield* printSelfClosingNodeTag(tag.value[0], intrinsicValue);
439
+ let openTag = tag.value[0];
440
+ let { flags, type, attributes } = openTag.value;
441
+ yield* printSelfClosingNodeTag(buildOpenNodeTag(flags, type, attributes), intrinsicValue);
442
+ } else if (tag.type === OpenNodeTag) {
443
+ let { flags, type, attributes } = tag.value;
444
+ yield* printTag(buildOpenNodeTag(flags, type, attributes));
425
445
  } else {
426
446
  yield* printTag(tag);
427
447
  }
@@ -486,11 +506,11 @@ export const getCooked = (tags) => {
486
506
  }
487
507
  }
488
508
 
489
- if (!(ref.value.name === '#' || (ref.value.name === '@' && attributes.cooked))) {
509
+ if (!(ref.value.type === '#' || (ref.value.type === '@' && attributes.cooked))) {
490
510
  throw new Error('cookable nodes must not contain other nodes');
491
511
  }
492
512
 
493
- if (ref.value.name === '@') {
513
+ if (ref.value.type === '@') {
494
514
  const { cooked: cookedValue } = tag.value.attributes;
495
515
 
496
516
  if (!cookedValue) throw new Error('cannot cook string: it contains uncooked escapes');
package/lib/symbols.js CHANGED
@@ -4,10 +4,12 @@ export const CloseNodeTag = Symbol.for('CloseNodeTag');
4
4
  export const ReferenceTag = Symbol.for('ReferenceTag');
5
5
  export const ShiftTag = Symbol.for('ShiftTag');
6
6
  export const GapTag = Symbol.for('GapTag');
7
+ export const BindingTag = Symbol.for('BindingTag');
7
8
  export const NullTag = Symbol.for('NullTag');
8
9
  export const InitializerTag = Symbol.for('InitializerTag');
10
+ export const AttributeDefinition = Symbol.for('AttributeDefinition');
9
11
  export const LiteralTag = Symbol.for('LiteralTag');
10
12
 
11
13
  export const TokenGroup = Symbol.for('TokenGroup');
12
14
 
13
- export const EmbeddedNode = Symbol.for('EmbeddedNode');
15
+ export const Property = Symbol.for('Property');
package/lib/template.js CHANGED
@@ -5,10 +5,10 @@ import {
5
5
  OpenNodeTag,
6
6
  CloseNodeTag,
7
7
  DoctypeTag,
8
- EmbeddedNode,
9
- GapTag,
8
+ Property,
9
+ BindingTag,
10
10
  } from './symbols.js';
11
- import * as sumtree from './sumtree.js';
11
+ import * as Children from './children.js';
12
12
  import { getOpenTag, get, isFragmentNode } from './tree.js';
13
13
 
14
14
  const { freeze } = Object;
@@ -21,16 +21,13 @@ export const buildFilledGapFunction = (expressions) => (value) => {
21
21
  export function* interpolateFragment(node, ref, expressions) {
22
22
  const open = getOpenTag(node);
23
23
 
24
- if (node.type !== null) throw new Error();
25
-
26
24
  const gap = buildFilledGapFunction(expressions);
27
25
 
28
- const counters = new Map();
29
-
30
26
  if (!open.value.type) {
31
27
  let currentRef = null;
28
+ let currentBinding = null;
32
29
  let isFragment = isFragmentNode(node);
33
- for (let tag of sumtree.traverse(node.children)) {
30
+ for (let tag of Children.traverse(node.children)) {
34
31
  switch (tag.type) {
35
32
  case DoctypeTag: {
36
33
  break;
@@ -48,11 +45,15 @@ export function* interpolateFragment(node, ref, expressions) {
48
45
  break;
49
46
  }
50
47
 
48
+ case BindingTag: {
49
+ currentBinding = tag;
50
+ break;
51
+ }
52
+
51
53
  case InitializerTag: {
52
- const { name } = currentRef.value;
53
- counters.set(name, -1);
54
- if (name === '.') {
55
- yield freeze({ ...ref });
54
+ const { type } = currentRef.value;
55
+ if (type === '.') {
56
+ yield freeze(ref);
56
57
  } else {
57
58
  yield currentRef;
58
59
  }
@@ -60,35 +61,21 @@ export function* interpolateFragment(node, ref, expressions) {
60
61
  break;
61
62
  }
62
63
 
63
- case GapTag: {
64
- const { name, isArray, flags } = currentRef.value;
64
+ case Property: {
65
+ let { reference } = tag.value;
66
+ const { type } = reference;
65
67
 
66
- if (name === '.') {
68
+ if (type === '.') {
67
69
  // TODO check/combine flags
68
- yield freeze({ ...ref });
70
+ yield freeze(ref);
71
+ yield currentBinding;
69
72
  } else {
70
73
  yield currentRef;
74
+ yield currentBinding;
71
75
  }
72
76
 
73
- const count = counters.get(name) + 1;
74
-
75
- counters.set(name, count);
76
-
77
- const resolvedRef = t.buildReferenceTag(name, isArray, flags, count);
77
+ yield gap(tag.value.node);
78
78
 
79
- yield gap(get(resolvedRef, node));
80
-
81
- break;
82
- }
83
-
84
- case EmbeddedNode: {
85
- const { name } = currentRef.value;
86
- if (name === '.') {
87
- yield freeze({ ...ref });
88
- } else {
89
- yield currentRef;
90
- }
91
- yield gap(tag.value);
92
79
  break;
93
80
  }
94
81
 
@@ -99,8 +86,8 @@ export function* interpolateFragment(node, ref, expressions) {
99
86
  }
100
87
  }
101
88
  } else if (open.type === OpenNodeTag) {
102
- yield freeze({ ...ref });
103
- yield gap(get(ref, node));
89
+ yield freeze(ref);
90
+ yield gap(node);
104
91
  } else {
105
92
  throw new Error();
106
93
  }