@bablr/boot 0.3.0 → 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/builders.js CHANGED
@@ -1,5 +1,13 @@
1
+ const sym = require('@bablr/boot-helpers/symbols');
1
2
  const { freeze, getPrototypeOf } = Object;
2
3
  const { isArray } = Array;
4
+ const {
5
+ OpenNodeTag,
6
+ CloseNodeTag,
7
+ ReferenceTag,
8
+ GapTag,
9
+ LiteralTag,
10
+ } = require('@bablr/boot-helpers/symbols');
3
11
 
4
12
  const spreads = new WeakMap();
5
13
 
@@ -31,16 +39,16 @@ const interpolateArray = (values, buildSeparator) => {
31
39
  return children;
32
40
  };
33
41
 
34
- const buildTerminal = (term) => {
42
+ const buildTag = (term) => {
35
43
  switch (term?.type) {
36
- case 'Literal':
44
+ case LiteralTag:
37
45
  return term;
38
46
 
39
47
  // case 'Escape':
40
48
  // return buildEscapeNode;
41
49
 
42
50
  default:
43
- throw new Error('Invalid terminal of type ' + term.type);
51
+ throw new Error('Invalid tag of type ' + term.type);
44
52
  }
45
53
  };
46
54
 
@@ -48,34 +56,34 @@ const interpolateString = (value) => {
48
56
  const children = [];
49
57
  if (isArray(value)) {
50
58
  for (const element of value) {
51
- children.push(buildTerminal(element));
59
+ children.push(buildTag(element));
52
60
  }
53
61
  } else {
54
62
  // we can't safely interpolate strings here, though I wish we could
55
- children.push(buildTerminal(value));
63
+ children.push(buildTag(value));
56
64
  }
57
65
 
58
66
  return buildNode('String', 'Content', children);
59
67
  };
60
68
 
61
- const buildReference = (name, isArray) => {
62
- return freeze({ type: 'Reference', value: freeze({ name, isArray }) });
69
+ const buildReferenceTag = (name, isArray) => {
70
+ return freeze({ type: ReferenceTag, value: freeze({ name, isArray }) });
63
71
  };
64
72
 
65
- const buildGap = () => {
66
- return freeze({ type: 'Gap', value: undefined });
73
+ const buildGapTag = () => {
74
+ return freeze({ type: GapTag, value: undefined });
67
75
  };
68
76
 
69
- const buildNodeOpenTag = (flags, type, attributes = {}) => {
70
- return freeze({ type: 'OpenNodeTag', value: freeze({ flags, type, attributes }) });
77
+ const buildNodeOpenTag = (flags, language, type, attributes = {}) => {
78
+ return freeze({ type: OpenNodeTag, value: freeze({ flags, language, type, attributes }) });
71
79
  };
72
80
 
73
81
  const buildNodeCloseTag = (type) => {
74
- return freeze({ type: 'CloseNodeTag', value: freeze({ type }) });
82
+ return freeze({ type: CloseNodeTag, value: freeze({ type }) });
75
83
  };
76
84
 
77
- const buildLiteral = (value) => {
78
- return freeze({ type: 'Literal', value });
85
+ const buildLiteralTag = (value) => {
86
+ return freeze({ type: LiteralTag, value });
79
87
  };
80
88
 
81
89
  const buildProperty = (key, value) => {
@@ -87,43 +95,43 @@ const buildProperty = (key, value) => {
87
95
  };
88
96
 
89
97
  const buildString = (value) => {
90
- const terminals = [];
98
+ const Tags = [];
91
99
  let literal = '';
92
100
  for (const chr of value) {
93
101
  if (chr === "'") {
94
102
  if (literal)
95
- terminals.push(
103
+ Tags.push(
96
104
  freeze({
97
- type: 'Literal',
105
+ type: LiteralTag,
98
106
  value: literal,
99
107
  }),
100
108
  );
101
- terminals.push(
109
+ Tags.push(
102
110
  e_node(
103
111
  'String',
104
112
  'Escape',
105
113
  [ref`escape`, ref`escapee`],
106
114
  {
107
115
  escape: s_node('String', 'Punctuator', '\\'),
108
- escapee: s_node('String', 'Literal', "'"),
116
+ escapee: s_node('String', LiteralTag, "'"),
109
117
  },
110
118
  { cooked: chr },
111
119
  ),
112
120
  );
113
121
  } else if (chr === '\\') {
114
122
  if (literal)
115
- terminals.push({
116
- type: 'Literal',
123
+ Tags.push({
124
+ type: LiteralTag,
117
125
  value: literal,
118
126
  });
119
- terminals.push(
127
+ Tags.push(
120
128
  e_node(
121
129
  'String',
122
130
  'Escape',
123
131
  [ref`escape`, ref`escapee`],
124
132
  {
125
133
  escape: s_node('String', 'Punctuator', '\\'),
126
- escapee: s_node('String', 'Literal', '\\'),
134
+ escapee: s_node('String', LiteralTag, '\\'),
127
135
  },
128
136
  { cooked: chr },
129
137
  ),
@@ -133,9 +141,9 @@ const buildString = (value) => {
133
141
  }
134
142
  }
135
143
  if (literal)
136
- terminals.push(
144
+ Tags.push(
137
145
  freeze({
138
- type: 'Literal',
146
+ type: LiteralTag,
139
147
  value: literal,
140
148
  }),
141
149
  );
@@ -145,7 +153,7 @@ const buildString = (value) => {
145
153
  [ref`open`, ref`content`, ref`close`],
146
154
  {
147
155
  open: s_node('String', 'Punctuator', "'"),
148
- content: interpolateString(terminals),
156
+ content: interpolateString(Tags),
149
157
  close: s_node('String', 'Punctuator', "'"),
150
158
  },
151
159
  {},
@@ -174,7 +182,7 @@ const buildBoolean = (value) => {
174
182
  );
175
183
  };
176
184
 
177
- const buildNull = () => {
185
+ const buildNullTag = () => {
178
186
  return node(
179
187
  'Instruction',
180
188
  'Null',
@@ -186,7 +194,7 @@ const buildNull = () => {
186
194
  );
187
195
  };
188
196
 
189
- const buildArray = (elements) => {
197
+ const buildArrayTag = (elements) => {
190
198
  return node(
191
199
  'Instruction',
192
200
  'Array',
@@ -237,7 +245,7 @@ const buildSpace = () => {
237
245
  };
238
246
 
239
247
  const buildIdentifier = (name) => {
240
- return node('Instruction', 'Identifier', [buildLiteral(name)]);
248
+ return node('Instruction', 'Identifier', [buildLiteralTag(name)]);
241
249
  };
242
250
 
243
251
  const buildAttribute = (key, value) => {
@@ -249,7 +257,7 @@ const buildAttribute = (key, value) => {
249
257
  };
250
258
 
251
259
  const buildExpression = (expr) => {
252
- if (expr == null) return buildNull();
260
+ if (expr == null) return buildNullTag();
253
261
 
254
262
  switch (typeof expr) {
255
263
  case 'boolean':
@@ -259,7 +267,7 @@ const buildExpression = (expr) => {
259
267
  case 'object': {
260
268
  switch (getPrototypeOf(expr)) {
261
269
  case Array.prototype:
262
- return buildArray(expr);
270
+ return buildArrayTag(expr);
263
271
  case Object.prototype:
264
272
  if (expr.type && expr.language && expr.children && expr.properties) {
265
273
  return expr;
@@ -276,39 +284,48 @@ const buildExpression = (expr) => {
276
284
 
277
285
  const nodeFlags = freeze({ syntactic: false, escape: false, trivia: false });
278
286
 
279
- const buildNode = (language, type, children = [], properties = {}, attributes = {}) =>
280
- freeze({
287
+ const buildNode = (language, type, children = [], properties = {}, attributes = {}) => {
288
+ const openTag = buildNodeOpenTag(nodeFlags, language, type, attributes);
289
+ const closeTag = buildNodeCloseTag(type);
290
+ return freeze({
281
291
  flags: nodeFlags,
282
292
  language,
283
293
  type,
284
- children: freeze(children),
294
+ children: freeze([openTag, ...children, closeTag]),
285
295
  properties: freeze(properties),
286
296
  attributes: freeze(attributes),
287
297
  });
298
+ };
288
299
 
289
300
  const syntacticFlags = freeze({ syntactic: true, escape: false, trivia: false });
290
301
 
291
- const buildSyntacticNode = (language, type, value, attributes = {}) =>
292
- freeze({
302
+ const buildSyntacticNode = (language, type, value, attributes = {}) => {
303
+ const openTag = buildNodeOpenTag(syntacticFlags, language, type, attributes);
304
+ const closeTag = buildNodeCloseTag(type);
305
+ return freeze({
293
306
  flags: syntacticFlags,
294
307
  language,
295
308
  type,
296
- children: [buildLiteral(value)],
309
+ children: [openTag, buildLiteralTag(value), closeTag],
297
310
  properties: freeze({}),
298
311
  attributes: freeze(attributes),
299
312
  });
313
+ };
300
314
 
301
315
  const escapeFlags = freeze({ syntactic: false, escape: true, trivia: false });
302
316
 
303
- const buildEscapeNode = (language, type, children = [], properties = {}, attributes = {}) =>
304
- freeze({
317
+ const buildEscapeNode = (language, type, children = [], properties = {}, attributes = {}) => {
318
+ const openTag = buildNodeOpenTag(escapeFlags, language, type, attributes);
319
+ const closeTag = buildNodeCloseTag(type);
320
+ return freeze({
305
321
  flags: escapeFlags,
306
322
  language,
307
323
  type,
308
- children: freeze(children),
324
+ children: freeze([openTag, ...children, closeTag]),
309
325
  properties: freeze(properties),
310
326
  attributes: freeze(attributes),
311
327
  });
328
+ };
312
329
 
313
330
  const syntacticEscapeFlags = freeze({ syntactic: true, escape: true, trivia: false });
314
331
 
@@ -318,27 +335,44 @@ const buildSyntacticEscapeNode = (
318
335
  children = [],
319
336
  properties = {},
320
337
  attributes = {},
321
- ) =>
322
- freeze({
338
+ ) => {
339
+ const openTag = buildNodeOpenTag(syntacticEscapeFlags, language, type, attributes);
340
+ const closeTag = buildNodeCloseTag(type);
341
+ return freeze({
323
342
  flags: syntacticEscapeFlags,
324
343
  language,
325
344
  type,
326
- children: freeze(children),
345
+ children: freeze([openTag, ...children, closeTag]),
327
346
  properties: freeze(properties),
328
347
  attributes: freeze(attributes),
329
348
  });
349
+ };
330
350
 
331
351
  const triviaFlags = freeze({ syntactic: false, escape: false, trivia: true });
332
352
 
333
- const buildTriviaNode = (language, type, children = [], properties = {}, attributes = {}) =>
334
- freeze({
353
+ const buildTriviaNode = (language, type, children = [], properties = {}, attributes = {}) => {
354
+ const openTag = buildNodeOpenTag(triviaFlags, language, type, attributes);
355
+ const closeTag = buildNodeCloseTag(type);
356
+ return freeze({
335
357
  flags: triviaFlags,
336
358
  language,
337
359
  type,
338
- children: freeze(children),
360
+ children: freeze([openTag, ...children, closeTag]),
339
361
  properties: freeze(properties),
340
362
  attributes: freeze(attributes),
341
363
  });
364
+ };
365
+
366
+ const buildGapNode = (attributes = []) => {
367
+ return freeze({
368
+ flags: nodeFlags,
369
+ language: null,
370
+ type: sym.gap,
371
+ children: freeze([buildGapTag()]),
372
+ properties: freeze({}),
373
+ attributes: freeze(attributes),
374
+ });
375
+ };
342
376
 
343
377
  const stripArray = (val) => {
344
378
  if (isArray(val)) {
@@ -355,19 +389,21 @@ const ref = (path) => {
355
389
  if (isArray(path)) {
356
390
  const pathIsArray = path[0].endsWith('[]');
357
391
  const name = pathIsArray ? path[0].slice(0, -2) : path[0];
358
- return buildReference(name, pathIsArray);
392
+ return buildReferenceTag(name, pathIsArray);
359
393
  } else {
360
394
  const { name, pathIsArray } = path;
361
- return buildReference(name, pathIsArray);
395
+ return buildReferenceTag(name, pathIsArray);
362
396
  }
363
397
  };
364
398
 
365
- const lit = (str) => buildLiteral(stripArray(str));
399
+ const lit = (str) => buildLiteralTag(stripArray(str));
366
400
 
367
- const gap = buildGap;
401
+ const gap = buildGapTag;
402
+ const arr = buildArrayTag;
368
403
  const nodeOpen = buildNodeOpenTag;
369
404
  const nodeClose = buildNodeCloseTag;
370
405
  const node = buildNode;
406
+ const g_node = buildGapNode;
371
407
  const s_node = buildSyntacticNode;
372
408
  const e_node = buildEscapeNode;
373
409
  const s_e_node = buildSyntacticEscapeNode;
@@ -377,30 +413,33 @@ module.exports = {
377
413
  buildProperty,
378
414
  buildString,
379
415
  buildBoolean,
380
- buildNull,
381
- buildArray,
416
+ buildNullTag,
417
+ buildArrayTag,
382
418
  buildTuple,
383
419
  buildObject,
384
420
  buildExpression,
385
421
  buildSpace,
386
422
  buildIdentifier,
387
423
  buildAttribute,
388
- buildReference,
389
- buildGap,
424
+ buildReferenceTag,
425
+ buildGapTag,
390
426
  buildNodeOpenTag,
391
427
  buildNodeCloseTag,
392
- buildLiteral,
428
+ buildLiteralTag,
393
429
  buildNode,
394
430
  buildSyntacticNode,
395
431
  buildEscapeNode,
396
432
  buildSyntacticEscapeNode,
397
433
  buildTriviaNode,
434
+ buildGapNode,
398
435
  ref,
399
436
  lit,
400
437
  gap,
438
+ arr,
401
439
  nodeOpen,
402
440
  nodeClose,
403
441
  node,
442
+ g_node,
404
443
  s_node,
405
444
  e_node,
406
445
  s_e_node,
package/lib/index.js CHANGED
@@ -1,14 +1,48 @@
1
1
  const { PathResolver } = require('@bablr/boot-helpers/path');
2
2
  const cstml = require('./languages/cstml.js');
3
+ const spamex = require('./languages/spamex.js');
3
4
  const regex = require('./languages/regex.js');
4
5
  const instruction = require('./languages/instruction.js');
5
- const { buildLiteral } = require('./builders.js');
6
+ const {
7
+ buildLiteralTag,
8
+ buildNodeOpenTag,
9
+ buildNodeCloseTag,
10
+ buildTriviaNode,
11
+ buildGapNode,
12
+ buildSyntacticEscapeNode,
13
+ } = require('./builders.js');
6
14
  const { TemplateParser } = require('./miniparser.js');
15
+ const { Resolver } = require('./print.js');
16
+ const {
17
+ OpenNodeTag,
18
+ CloseNodeTag,
19
+ ReferenceTag,
20
+ ArrayTag,
21
+ EmbeddedNode,
22
+ Trivia,
23
+ Escape,
24
+ } = require('@bablr/boot-helpers/symbols');
25
+ const btree = require('@bablr/boot-helpers/btree');
7
26
 
8
27
  const { isArray } = Array;
9
28
  const { hasOwn } = Object;
10
29
 
11
- const set = (obj, path, value) => {
30
+ const get = (node, path) => {
31
+ const { type, properties } = node;
32
+ const { 1: name, 2: index } = /^([^\.]+)(?:\.(\d+))?/.exec(path) || [];
33
+
34
+ if (!hasOwn(properties, name)) {
35
+ throw new Error(`Cannot find {name: ${name}} on node of {type: ${type}}`);
36
+ }
37
+
38
+ if (index != null) {
39
+ return properties[name]?.[parseInt(index, 10)];
40
+ } else {
41
+ return properties[name];
42
+ }
43
+ };
44
+
45
+ const add = (obj, path, value) => {
12
46
  const { name, isArray: pathIsArray } = path;
13
47
  if (pathIsArray) {
14
48
  if (!obj[name]) {
@@ -17,7 +51,7 @@ const set = (obj, path, value) => {
17
51
 
18
52
  if (!isArray(obj[name])) throw new Error('bad array value');
19
53
 
20
- obj[name].push(value);
54
+ obj[name] = btree.push(obj[name], value);
21
55
  } else {
22
56
  if (hasOwn(obj, name)) {
23
57
  throw new Error('duplicate child name');
@@ -71,9 +105,14 @@ const getAgASTValue = (language, miniNode) => {
71
105
  }
72
106
 
73
107
  const { language: languageName, type, attributes } = miniNode;
74
- const flags = { escape: false, trivia: false, token: false, intrinsic: false };
108
+ const flags = {
109
+ escape: !!miniNode.flags?.escape,
110
+ trivia: !!miniNode.flags?.trivia,
111
+ token: false,
112
+ intrinsic: false,
113
+ };
75
114
  const properties = {};
76
- const children = [];
115
+ let children = [];
77
116
  const resolver = new PathResolver(miniNode);
78
117
  const resolvedLanguage =
79
118
  languageName !== language.name ? language.dependencies[languageName] : language;
@@ -90,7 +129,9 @@ const getAgASTValue = (language, miniNode) => {
90
129
  type === 'Punctuator' ||
91
130
  type === 'Keyword' ||
92
131
  type === 'Identifier' ||
93
- type === 'StringContent'
132
+ type === 'StringContent' ||
133
+ type === 'Escape' ||
134
+ miniNode.flags?.syntactic
94
135
  ) {
95
136
  flags.token = true;
96
137
  }
@@ -99,51 +140,84 @@ const getAgASTValue = (language, miniNode) => {
99
140
  flags.intrinsic = true;
100
141
  }
101
142
 
143
+ children = btree.push(
144
+ children,
145
+ buildNodeOpenTag(flags, resolvedLanguage.canonicalURL, type, attributes),
146
+ );
147
+
102
148
  for (const child of miniNode.children) {
103
- if (child.type === 'Reference') {
149
+ if (child.type === OpenNodeTag || child.type === CloseNodeTag) {
150
+ continue;
151
+ } else if (child.type === ReferenceTag) {
104
152
  const path = child.value;
105
- const node = resolver.get(path);
106
- set(properties, path, getAgASTValue(resolvedLanguage, node));
107
- children.push(child);
108
- } else if (child.type === 'Trivia') {
109
- children.push({
110
- type: 'Embedded',
111
- value: {
112
- flags: { escape: false, token: true, trivia: true, intrinsic: false },
113
- language: 'https://bablr.org/languages/core/en/space-tab-newline',
114
- type: 'Space',
115
- children: [buildLiteral(child.value)],
116
- properties: {},
117
- attributes: {},
118
- },
153
+ const { name, isArray } = path;
154
+ let node = resolver.get(child.value);
155
+
156
+ if (node === undefined) throw new Error();
157
+
158
+ const agASTNode = node === null ? buildGapNode() : getAgASTValue(resolvedLanguage, node);
159
+
160
+ if (isArray && !hasOwn(properties, name)) {
161
+ const newRef = { type: ReferenceTag, value: { name, isArray } };
162
+ const arrayTag = { type: ArrayTag, value: undefined };
163
+
164
+ children = btree.push(children, newRef);
165
+ children = btree.push(children, arrayTag);
166
+
167
+ add(properties, { name }, []);
168
+ }
169
+
170
+ add(properties, path, agASTNode);
171
+ children = btree.push(children, { type: ReferenceTag, value: { name, isArray } });
172
+ } else if (child.type === Trivia) {
173
+ children = btree.push(children, {
174
+ type: EmbeddedNode,
175
+ value: getAgASTValue(
176
+ resolvedLanguage,
177
+ buildTriviaNode(languageName, 'Space', [buildLiteralTag(child.value)]),
178
+ ),
119
179
  });
120
- } else if (child.type === 'Escape') {
180
+ } else if (child.type === Escape) {
121
181
  const { cooked, raw } = child.value;
122
182
  const attributes = { cooked };
123
183
 
124
- children.push({
125
- type: 'Embedded',
126
- value: {
127
- flags: { escape: true, token: true, trivia: false, intrinsic: false },
128
- language: cstml.canonicalURL,
129
- type: 'Escape',
130
- children: [buildLiteral(raw)],
131
- properties: {},
132
- attributes,
133
- },
184
+ children = btree.push(children, {
185
+ type: EmbeddedNode,
186
+ value: getAgASTValue(
187
+ resolvedLanguage,
188
+ buildSyntacticEscapeNode(languageName, 'Escape', [buildLiteralTag(raw)], {}, attributes),
189
+ ),
134
190
  });
135
191
  } else {
136
- children.push(child);
192
+ if (child.type === ArrayTag) throw new Error('badbad');
193
+ children = btree.push(children, child);
137
194
  }
138
195
  }
139
196
 
197
+ children = btree.push(children, buildNodeCloseTag());
198
+
140
199
  return { flags, language: resolvedLanguage.canonicalURL, type, children, properties, attributes };
141
200
  };
142
201
 
143
202
  const str = buildTag(cstml, 'String');
144
203
  const num = buildTag(cstml, 'Integer');
145
204
  const cst = buildTag(cstml, 'Node');
205
+ const spam = buildTag(spamex, 'Pattern');
146
206
  const re = buildTag(regex, 'Pattern');
147
207
  const i = buildTag(instruction, 'Call');
148
208
 
149
- module.exports = { str, num, cst, re, i, buildTag, set, getAgASTValue, TemplateParser, parse };
209
+ module.exports = {
210
+ str,
211
+ num,
212
+ cst,
213
+ spam,
214
+ re,
215
+ i,
216
+ buildTag,
217
+ get,
218
+ add,
219
+ getAgASTValue,
220
+ TemplateParser,
221
+ Resolver,
222
+ parse,
223
+ };
package/lib/index.mjs CHANGED
@@ -1,3 +1,17 @@
1
1
  import index from './index.js';
2
2
 
3
- export const { str, num, cst, re, i, buildTag, set, getAgASTValue, TemplateParser, parse } = index;
3
+ export const {
4
+ str,
5
+ num,
6
+ cst,
7
+ cst: cstml,
8
+ spam,
9
+ spam: spamex,
10
+ re,
11
+ i,
12
+ buildTag,
13
+ set,
14
+ getAgASTValue,
15
+ TemplateParser,
16
+ parse,
17
+ } = index;