@bablr/agast-helpers 0.7.1 → 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
 
@@ -216,7 +190,7 @@ export const printNodeFlags = (flags) => {
216
190
  export const printOpenNodeTag = (tag) => {
217
191
  if (tag?.type !== OpenNodeTag) throw new Error();
218
192
 
219
- let { flags, language: tagLanguage, type, attributes } = tag.value;
193
+ let { flags, type, attributes } = tag.value;
220
194
 
221
195
  if (!type) {
222
196
  return `<${printNodeFlags(flags)}_>`;
@@ -225,22 +199,19 @@ export const printOpenNodeTag = (tag) => {
225
199
  let printedAttributes = printAttributes(attributes);
226
200
  let attributesFrag = printedAttributes ? ` ${printedAttributes}` : '';
227
201
 
228
- return `<${printNodeFlags(flags)}${printTagPath(tagLanguage, type)}${attributesFrag}>`;
202
+ return `<${printNodeFlags(flags)}${printType(type)}${attributesFrag}>`;
229
203
  };
230
204
 
231
205
  export const printSelfClosingNodeTag = (tag, intrinsicValue) => {
232
206
  if (tag?.type !== OpenNodeTag) throw new Error();
233
207
 
234
- let { flags, language: tagLanguage, type, attributes } = tag.value;
208
+ let { flags, type, attributes } = tag.value;
235
209
 
236
210
  let printedAttributes = printAttributes(attributes);
237
211
  let attributesFrag = printedAttributes ? ` ${printedAttributes}` : '';
238
212
  let intrinsicFrag = intrinsicValue ? ` ${printString(intrinsicValue)}` : '';
239
213
 
240
- return `<${printNodeFlags(flags)}${printTagPath(
241
- tagLanguage,
242
- type,
243
- )}${intrinsicFrag}${attributesFrag} />`;
214
+ return `<${printNodeFlags(flags)}${printType(type)}${intrinsicFrag}${attributesFrag} />`;
244
215
  };
245
216
 
246
217
  export const printCloseNodeTag = (tag) => {
@@ -249,38 +220,31 @@ export const printCloseNodeTag = (tag) => {
249
220
  return `</>`;
250
221
  };
251
222
 
252
- export const printTag = (tag) => {
253
- if (!isObject(tag)) throw new Error();
254
-
255
- switch (tag?.type || NullTag) {
256
- case NullTag:
257
- return printNullTag(tag);
258
-
259
- case GapTag:
260
- return printGapTag(tag);
261
-
262
- case InitializerTag:
263
- return printInitializerTag(tag);
223
+ export const printAttributeDefinition = (tag) => {
224
+ if (tag?.type !== AttributeDefinition) throw new Error();
225
+ let { path, value } = tag.value;
264
226
 
265
- case ShiftTag:
266
- return printShiftTag(tag);
267
-
268
- case LiteralTag:
269
- return printLiteralTag(tag);
270
-
271
- case DoctypeTag:
272
- return printDoctypeTag(tag);
227
+ return `{ ${printIdentifierPath(path)}: ${printExpression(value)} }`;
228
+ };
273
229
 
274
- case ReferenceTag:
275
- 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
+ };
276
243
 
277
- case OpenNodeTag:
278
- return printOpenNodeTag(tag);
244
+ export const printTag = (tag) => {
245
+ if (!isObject(tag)) throw new Error();
279
246
 
280
- case CloseNodeTag:
281
- return printCloseNodeTag(tag);
247
+ let printer = printers[tag?.type];
282
248
 
283
- default:
284
- throw new Error();
285
- }
249
+ return printer(tag);
286
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
  }