@bablr/boot 0.9.0 → 0.11.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/miniparser.js CHANGED
@@ -4,25 +4,33 @@ import isString from 'iter-tools-es/methods/is-string';
4
4
  import isObject from 'iter-tools-es/methods/is-object';
5
5
  import find from 'iter-tools-es/methods/find';
6
6
  import every from 'iter-tools-es/methods/every';
7
- import * as sym from '@bablr/agast-vm-helpers/symbols';
8
7
  import { Match } from './match.js';
9
8
  import { isRegex, isArray, getPrototypeOf } from './utils.js';
10
9
  import { ReferenceTag, LiteralTag } from '@bablr/agast-helpers/symbols';
11
10
  import {
12
11
  buildCloseNodeTag,
13
12
  buildLiteralTag,
13
+ buildNullTag,
14
14
  buildOpenNodeTag,
15
15
  buildReferenceTag,
16
+ buildShiftTag,
16
17
  nodeFlags,
17
18
  } from '@bablr/agast-helpers/builders';
18
- import { add, buildToken, shift } from '@bablr/agast-helpers/tree';
19
- import * as sumtree from '@bablr/agast-helpers/children';
20
- import { buildPathSegment, get } from '@bablr/agast-helpers/path';
19
+ import { buildToken } from '@bablr/agast-helpers/tree';
20
+ import * as Tags from '@bablr/agast-helpers/tags';
21
+ import {
22
+ buildNode,
23
+ buildNullNode,
24
+ buildPathSegment,
25
+ get,
26
+ getTags,
27
+ Path,
28
+ } from '@bablr/agast-helpers/path';
21
29
  import { parseReference } from '@bablr/agast-helpers/shorthand';
22
30
 
23
- const Escape = Symbol.for('Escape');
31
+ let Escape = Symbol.for('Escape');
24
32
 
25
- const getProduction = (grammar, type) => {
33
+ let getProduction = (grammar, type) => {
26
34
  return getPrototypeOf(grammar)[type];
27
35
  };
28
36
 
@@ -82,11 +90,13 @@ export class TemplateParser {
82
90
  }
83
91
 
84
92
  get matchIsNode() {
85
- return this.language.covers.get(sym.node).has(this.m.type) && !this.matchIsCover;
93
+ return (
94
+ this.language.covers.get(Symbol.for('@bablr/node')).has(this.m.name) && !this.matchIsCover
95
+ );
86
96
  }
87
97
 
88
98
  get matchIsCover() {
89
- return this.language.covers.has(this.m.type);
99
+ return this.language.covers.has(this.m.name);
90
100
  }
91
101
 
92
102
  get matchIsFragment() {
@@ -102,27 +112,27 @@ export class TemplateParser {
102
112
  }
103
113
 
104
114
  get slicedQuasi() {
105
- const { idx, quasi } = this;
115
+ let { idx, quasi } = this;
106
116
  return quasi.slice(idx);
107
117
  }
108
118
 
109
119
  get guardedSlicedQuasi() {
110
- const { span, slicedQuasi } = this;
111
- const { guard } = span;
120
+ let { span, slicedQuasi } = this;
121
+ let { guard } = span;
112
122
 
113
123
  if (!guard) return slicedQuasi;
114
124
 
115
- const pat = new RegExp(escapeRegex(guard), 'y');
116
- const res = pat.exec(slicedQuasi);
125
+ let pat = new RegExp(escapeRegex(guard), 'y');
126
+ let res = pat.exec(slicedQuasi);
117
127
 
118
128
  return res ? slicedQuasi.slice(0, pat.lastIndex - res[0].length) : slicedQuasi;
119
129
  }
120
130
 
121
131
  matchSticky(pattern, attrs) {
122
- const { slicedQuasi, guardedSlicedQuasi } = this;
123
- const { balancer } = attrs;
132
+ let { slicedQuasi, guardedSlicedQuasi } = this;
133
+ let { balancer } = attrs;
124
134
 
125
- const source = balancer ? slicedQuasi : guardedSlicedQuasi;
135
+ let source = balancer ? slicedQuasi : guardedSlicedQuasi;
126
136
 
127
137
  if (isString(pattern)) {
128
138
  return source.startsWith(pattern) ? pattern : null;
@@ -130,7 +140,7 @@ export class TemplateParser {
130
140
  if (!pattern.sticky) throw new Error('be sticky!');
131
141
  pattern.lastIndex = 0;
132
142
 
133
- const result = pattern.exec(source);
143
+ let result = pattern.exec(source);
134
144
 
135
145
  return result ? result[0] : null;
136
146
  } else {
@@ -138,10 +148,19 @@ export class TemplateParser {
138
148
  }
139
149
  }
140
150
 
141
- eval(id, attrs = {}, props = {}, shift_ = null) {
142
- const parentMatch = this.m;
143
- const parentPath = this.path?.node ? this.path : this.path?.parent;
144
- const { type } = id;
151
+ eatProduction(_id, attrs = {}, props = {}, shift_ = null) {
152
+ if (_id === null) {
153
+ let path_ = parseReference(attrs.path);
154
+
155
+ this.path.node = Path.from(this.node).advance(path_).advance(buildNullNode()).node;
156
+
157
+ return null;
158
+ }
159
+ let id = isObject(_id) ? _id : this.buildId(_id);
160
+
161
+ let parentMatch = this.m;
162
+ let parentPath = this.path?.node ? this.path : this.path?.parent;
163
+ let { name } = id;
145
164
 
146
165
  if (parentMatch && this.matchIsNode) {
147
166
  if (this.matchIsCover && Object.keys(attrs).length) {
@@ -155,39 +174,36 @@ export class TemplateParser {
155
174
  this.m = Match.from(this.rootLanguage, id, attrs);
156
175
  }
157
176
 
158
- const { covers } = this.language;
159
- const isNode = this.matchIsNode;
160
- const isCover = this.matchIsCover;
161
- const isEmbedded = this.language !== this.m.parent?.resolvedLanguage;
162
- const { path, grammar } = this;
177
+ let { covers } = this.language;
178
+ let isNode = this.matchIsNode;
179
+ let isCover = this.matchIsCover;
180
+ let isEmbedded = this.language !== this.m.parent?.resolvedLanguage;
181
+ let { path, grammar } = this;
163
182
 
164
- if (!type) throw new Error('eval requires a type');
183
+ if (!name) throw new Error('eval requires a type');
165
184
 
166
185
  if (parentPath?.node && this.atExpression && !attrs.noInterpolate && (isNode || isCover)) {
167
- const { quasisDone } = this;
186
+ let { quasisDone } = this;
168
187
 
169
188
  if (quasisDone) throw new Error('there must be more quasis than expressions');
170
189
 
171
- const result = this.expression;
190
+ let result = this.expression;
172
191
 
173
192
  this.expressionIdx++;
174
193
  this.quasiIdx++;
175
194
  this.idx = 0;
176
195
 
177
- if (parentPath?.node && (isNode || covers.has(type))) {
178
- const { node } = parentPath;
179
- const path = parseReference(this.m.attrs.path);
196
+ if (!parentPath?.node || isNode || covers.has(name)) {
197
+ let path = parseReference(this.m.attrs.path);
180
198
 
181
199
  if (isArray(result)) {
182
- for (const value of result) {
183
- node.children = sumtree.push(node.children, path);
184
-
185
- add(node, path, value);
200
+ for (let value of result) {
201
+ parentPath.node = Path.from(parentPath.node).advance(path).advance(value).node;
186
202
  }
187
203
  } else {
188
- node.children = sumtree.push(node.children, path);
189
-
190
- add(node, path, result);
204
+ parentPath.node = Path.from(parentPath.node)
205
+ .advance(path)
206
+ .advance(result ? result : buildNullTag()).node;
191
207
  }
192
208
  }
193
209
  } else {
@@ -195,42 +211,44 @@ export class TemplateParser {
195
211
  this.spans.push({ type: 'Bare', guard: null });
196
212
  }
197
213
 
198
- if (!getProduction(grammar, type)) {
199
- throw new Error(`Unknown production {type: ${type}}`);
214
+ if (!getProduction(grammar, name)) {
215
+ throw new Error(`Unknown production {type: ${name}}`);
200
216
  }
201
217
 
202
- if (isNode) {
203
- let { node } = this.path;
204
- node.children = sumtree.push(node.children, buildOpenNodeTag(nodeFlags, node.type));
205
- }
218
+ // if (isNode) {
219
+ // let { node } = this.path;
220
+ // this.path.node = Path.from(node).advance(buildOpenNodeTag(nodeFlags, node.type)).node;
221
+ // }
206
222
 
207
- const result = getPrototypeOf(grammar)[type].call(grammar, this, props);
223
+ let result = getPrototypeOf(grammar)[name].call(grammar, this, props);
208
224
 
209
225
  if (isEmbedded) {
210
226
  this.spans.pop();
211
227
  }
212
228
 
213
- if (isNode) {
214
- const { node } = this.path;
229
+ if (isNode || !parentPath) {
215
230
  if (result?.attrs) {
216
- node.attributes = result.attrs;
217
- node.children = sumtree.replaceAt(
218
- 0,
219
- node.children,
220
- buildOpenNodeTag(nodeFlags, node.type, result.attrs),
221
- );
231
+ this.path.node = Path.from(this.node).replaceWith(
232
+ buildNode(
233
+ Tags.replaceAt(
234
+ 0,
235
+ this.path.node.value.tags,
236
+ buildOpenNodeTag(nodeFlags, this.path.name, isCover ? '_' : null, result.attrs),
237
+ ),
238
+ ),
239
+ ).node;
222
240
  }
223
241
 
224
- node.children = sumtree.push(node.children, buildCloseNodeTag());
242
+ this.path.node = Path.from(this.node).advance(buildCloseNodeTag()).node;
225
243
 
226
- if (parentPath?.node && !covers.has(type)) {
227
- const path = parseReference(this.m.attrs.path);
244
+ if (parentPath?.node && !covers.has(name)) {
245
+ let path = this.m.attrs.path
246
+ ? parseReference(this.m.attrs.path)
247
+ : buildReferenceTag(parentPath.node.value.type === Symbol.for('_') ? '_' : '.');
228
248
 
229
- if (shift_ == null) {
230
- add(parentPath.node, path, node);
231
- } else {
232
- shift(parentPath.node, path, node);
233
- }
249
+ parentPath.node = Path.from(parentPath.node)
250
+ .advance(shift_ == null ? path : buildShiftTag())
251
+ .advance(this.node).node;
234
252
  }
235
253
  }
236
254
 
@@ -242,11 +260,11 @@ export class TemplateParser {
242
260
  this.m = this.m.parent;
243
261
 
244
262
  if (this.path?.node) {
245
- const isTag = (child) => [LiteralTag, Escape].includes(child.type);
263
+ let isTag = (child) => [LiteralTag, Escape].includes(child.type);
246
264
 
247
- const { children } = this.path.node;
265
+ let tags = getTags(this.path.node);
248
266
 
249
- if (find(isTag, sumtree.traverse(children)) && every(isTag, sumtree.traverse(children))) {
267
+ if (find(isTag, Tags.traverse(tags)) && every(isTag, Tags.traverse(tags))) {
250
268
  throw new Error('strings must be wrapped in nodes');
251
269
  }
252
270
  }
@@ -254,7 +272,7 @@ export class TemplateParser {
254
272
  }
255
273
 
256
274
  updateSpans(attrs) {
257
- const { startSpan, endSpan, balanced, balancer } = attrs;
275
+ let { startSpan, endSpan, balanced, balancer } = attrs;
258
276
  if (endSpan || balancer) {
259
277
  if (!this.span.guard) {
260
278
  throw new Error('Only balanced spans can be closed with endSpan');
@@ -262,51 +280,50 @@ export class TemplateParser {
262
280
  this.popSpan();
263
281
  }
264
282
  if (startSpan || balanced) {
265
- const type = startSpan || this.span.type;
283
+ let type = startSpan || this.span.type;
266
284
  this.pushSpan({ type, guard: balanced });
267
285
  }
268
286
  }
269
287
 
270
288
  buildId(id) {
271
- let type;
289
+ let name;
272
290
  let language = this.language.name;
273
291
 
274
292
  if (id.includes(':')) {
275
- ({ 0: language, 1: type } = id.split(':'));
293
+ ({ 0: language, 1: name } = id.split(':'));
276
294
  } else {
277
- type = id;
295
+ name = id;
278
296
  }
279
297
 
280
- return { type, language };
281
- }
282
-
283
- eatProduction(id, attrs = {}, props = {}) {
284
- return this.eval(this.buildId(id), attrs, props);
298
+ return {
299
+ name,
300
+ language,
301
+ };
285
302
  }
286
303
 
287
- eatHeldProduction(type, attrs) {
304
+ eatHeldProduction(name, attrs) {
288
305
  if (!this.held) {
289
306
  throw new Error();
290
307
  }
291
308
 
292
- const { held, node } = this;
309
+ let { held, node } = this;
293
310
 
294
311
  this.held = null;
295
312
 
296
- const path = parseReference(attrs.path);
313
+ let path = parseReference(attrs.path);
297
314
 
298
- add(node, path, held);
315
+ this.path.node = Path.from(node).advance(path).advance(held).node;
299
316
 
300
317
  return held;
301
318
  }
302
319
 
303
320
  shiftProduction(id, attrs = {}, props = {}) {
304
- const { node } = this;
321
+ let { node } = this;
305
322
  // don't push a new path onto the stack
306
323
 
307
324
  // get the most recently produced node and detach it from its parent
308
325
 
309
- const ref = sumtree.getAt(-3, node.children);
326
+ let ref = Tags.getAt(-1, getTags(node)).value.tags[0];
310
327
 
311
328
  if (!ref.value.flags.expression) throw new Error();
312
329
 
@@ -316,22 +333,20 @@ export class TemplateParser {
316
333
 
317
334
  this.held = get(buildPathSegment(ref.value.name, -1), node);
318
335
 
319
- let id_ = this.buildId(id);
320
-
321
- const shifted = this.eval(id_, attrs, props, 1);
336
+ let shifted = this.eatProduction(id, attrs, props, 1);
322
337
 
323
338
  // shift(node, ref, shifted);
324
339
 
325
340
  return shifted;
326
341
  }
327
342
 
328
- eat(pattern, type, attrs) {
329
- if (!isString(type)) throw new Error('Cannot eat anonymous token');
343
+ eat(pattern, name, attrs) {
344
+ if (name && !isString(name)) throw new Error('bad name');
330
345
  if (!isObject(attrs) || !attrs.path) throw new Error('a node must have a path');
331
346
 
332
- const { path, ..._attrs } = attrs;
347
+ let { path, ..._attrs } = attrs;
333
348
 
334
- const result = this.matchSticky(pattern, attrs, this);
349
+ let result = this.matchSticky(pattern, attrs, this);
335
350
 
336
351
  if (!result) this.fail();
337
352
 
@@ -339,9 +354,11 @@ export class TemplateParser {
339
354
 
340
355
  this.updateSpans(attrs);
341
356
 
342
- const path_ = parseReference(attrs.path);
357
+ let path_ = parseReference(attrs.path);
343
358
 
344
- add(this.node, path_, buildToken(type, result, _attrs));
359
+ this.path.node = Path.from(this.node)
360
+ .advance(path_)
361
+ .advance(buildToken(name, result, _attrs)).node;
345
362
 
346
363
  return result;
347
364
  }
@@ -351,99 +368,111 @@ export class TemplateParser {
351
368
  return this.matchSticky(pattern, attrs, this);
352
369
  }
353
370
 
354
- eatMatch(pattern, type, attrs) {
355
- if (!isString(type)) throw new Error('Cannot eatMatch anonymous token');
371
+ eatMatch(pattern, name, attrs) {
372
+ if (name && !isString(name)) throw new Error('bad name');
356
373
  if (!isObject(attrs) || !attrs.path) throw new Error('a node must have a path');
357
374
 
358
- const result = this.matchSticky(pattern, attrs, this);
375
+ let result = this.matchSticky(pattern, attrs, this);
359
376
 
360
377
  if (result) {
361
378
  this.updateSpans(attrs);
362
379
 
363
380
  this.idx += result.length;
364
381
 
365
- const path = parseReference(attrs.path);
382
+ let { path, ..._attrs } = attrs;
383
+
384
+ let path_ = parseReference(path);
366
385
 
367
- add(this.node, path, buildToken(type, result));
386
+ this.path.node = Path.from(this.node)
387
+ .advance(path_)
388
+ .advance(buildToken(name, result, _attrs)).node;
368
389
  }
369
390
  return result;
370
391
  }
371
392
 
372
393
  eatTrivia(pattern) {
373
- const result = this.matchSticky(pattern, {}, this);
394
+ let result = this.matchSticky(pattern, {}, this);
374
395
 
375
396
  if (!result) this.fail();
376
397
 
377
398
  this.idx += result.length;
378
399
 
379
- add(this.node, buildReferenceTag('#'), buildToken('Space', result));
400
+ this.path.node = Path.from(this.node)
401
+ .advance(buildReferenceTag('#'))
402
+ .advance(buildToken('Space', result)).node;
380
403
 
381
404
  return result;
382
405
  }
383
406
 
384
407
  eatMatchTrivia(pattern) {
385
- const result = this.matchSticky(pattern, {}, this);
408
+ let result = this.matchSticky(pattern, {}, this);
386
409
 
387
410
  if (result) {
388
411
  this.idx += result.length;
389
412
 
390
- add(this.node, buildReferenceTag('#'), buildToken('Space', result));
413
+ this.path.node = Path.from(this.node)
414
+ .advance(buildReferenceTag('#'))
415
+ .advance(buildToken('Space', result)).node;
391
416
  }
392
417
 
393
418
  return result;
394
419
  }
395
420
 
396
421
  eatEscape(pattern) {
397
- const result = this.matchSticky(pattern, {}, this);
422
+ let result = this.matchSticky(pattern, {}, this);
398
423
 
399
424
  if (!result) this.fail();
400
425
 
401
426
  this.idx += result.length;
402
427
 
403
- const raw = result;
404
- const cooked = this.language.cookEscape(result, this.span);
405
- const attributes = { cooked };
428
+ let raw = result;
429
+ let cooked = this.language.cookEscape(result, this.span);
430
+ let attributes = { cooked };
406
431
 
407
- add(this.node, buildReferenceTag('@'), buildToken('Escape', raw, attributes));
432
+ this.path.node = Path.from(this.node)
433
+ .advance(buildReferenceTag('@'))
434
+ .advance(buildToken('Escape', raw, attributes)).node;
408
435
 
409
436
  return result;
410
437
  }
411
438
 
412
439
  eatMatchEscape(pattern) {
413
- const result = this.matchSticky(pattern, {}, this);
440
+ let result = this.matchSticky(pattern, {}, this);
414
441
 
415
442
  if (result) {
416
443
  this.idx += result.length;
417
444
 
418
- const raw = result;
419
- const cooked = this.language.cookEscape(result, this.span);
420
- const attributes = { cooked };
445
+ let raw = result;
446
+ let cooked = this.language.cookEscape(result, this.span);
447
+ let attributes = { cooked };
421
448
 
422
- add(this.node, buildReferenceTag('@'), buildToken('Escape', raw, attributes));
449
+ this.path.node = Path.from(this.node)
450
+ .advance(buildReferenceTag('@'))
451
+ .advance(buildToken('Escape', raw, attributes)).node;
423
452
  }
424
453
 
425
454
  return result;
426
455
  }
427
456
 
428
457
  eatLiteral(pattern) {
429
- const result = this.matchSticky(pattern, {}, this);
458
+ let result = this.matchSticky(pattern, {}, this);
430
459
 
431
460
  if (!result) this.fail();
432
461
 
433
462
  this.idx += result.length;
434
463
 
435
- this.node.children = sumtree.push(this.node.children, buildLiteralTag(result));
464
+ this.path.node = buildNode(Tags.push(this.node.value.tags, buildLiteralTag(result)));
436
465
 
437
466
  return result;
438
467
  }
439
468
 
440
469
  eatMatchLiteral(pattern) {
441
- const result = this.matchSticky(pattern, {}, this);
470
+ let result = this.matchSticky(pattern, {}, this);
442
471
 
443
472
  if (result) {
444
473
  this.idx += result.length;
445
474
 
446
- this.node.children = sumtree.push(this.node.children, buildLiteralTag(result));
475
+ this.path.node = buildNode(Tags.push(this.node.value.tags, buildLiteralTag(result)));
447
476
  }
448
477
 
449
478
  return result;
@@ -466,6 +495,6 @@ export class TemplateParser {
466
495
  }
467
496
 
468
497
  fail() {
469
- throw new Error(`miniparser: parsing \`${this.quasis}\` failed`);
498
+ throw new Error(`miniparser: parsing \`${this.guardedSlicedQuasi}\` failed`);
470
499
  }
471
500
  }
package/lib/path.js CHANGED
@@ -1,11 +1,9 @@
1
- const { hasOwn } = Object;
2
- const { isArray } = Array;
3
-
4
1
  export class Path {
5
2
  constructor(id, attributes, parent = null) {
6
3
  this.id = id;
7
4
  this.attributes = attributes;
8
5
  this.parent = parent;
6
+ this.depth = parent ? parent.depth + 1 : 0;
9
7
 
10
8
  this.node = null;
11
9
  }
@@ -26,6 +24,10 @@ export class Path {
26
24
  return this.id.type;
27
25
  }
28
26
 
27
+ get name() {
28
+ return this.id.name;
29
+ }
30
+
29
31
  generate(id, attrs) {
30
32
  return new Path(id, attrs, this);
31
33
  }
@@ -34,36 +36,3 @@ export class Path {
34
36
  return new Path(id, attrs);
35
37
  }
36
38
  }
37
-
38
- export class PathResolver {
39
- constructor(node) {
40
- this.node = node;
41
- this.counters = {};
42
- }
43
-
44
- get(path) {
45
- const { node, counters } = this;
46
-
47
- const { isArray: pathIsArray, name } = path;
48
-
49
- if (!hasOwn(node.properties, name)) {
50
- throw new Error(`cannot resolve {path: ${name}}`);
51
- }
52
-
53
- let value = node.properties[name];
54
-
55
- if (pathIsArray) {
56
- if (!isArray(value)) {
57
- throw new Error(`cannot resolve {path: ${name}}: not an array`);
58
- }
59
-
60
- const counter = counters[name] ?? 0;
61
-
62
- counters[name] = counter + 1;
63
-
64
- value = value[counter];
65
- }
66
-
67
- return value;
68
- }
69
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bablr/boot",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Compile-time tools for bootstrapping BABLR VM",
5
5
  "engines": {
6
6
  "node": ">=12.0.0"
@@ -20,9 +20,9 @@
20
20
  ],
21
21
  "sideEffects": false,
22
22
  "dependencies": {
23
- "@bablr/agast-helpers": "0.8.0",
24
- "@bablr/agast-vm-helpers": "0.8.0",
25
- "@iter-tools/imm-stack": "1.1.0",
23
+ "@bablr/agast-helpers": "0.10.0",
24
+ "@bablr/agast-vm-helpers": "0.10.0",
25
+ "@iter-tools/imm-stack": "1.2.0",
26
26
  "escape-string-regexp": "5.0.0",
27
27
  "iter-tools-es": "^7.0.2"
28
28
  },
@@ -40,7 +40,10 @@
40
40
  "expect": "^29.6.2",
41
41
  "prettier": "^2.0.5"
42
42
  },
43
- "repository": "git@github.com:bablr-lang/boot.git",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+ssh://git@github.com/bablr-lang/boot.git"
46
+ },
44
47
  "homepage": "https://github.com/bablr-lang/boot",
45
48
  "author": "Conrad Buck <conartist6@gmail.com>",
46
49
  "license": "MIT"