@bablr/boot 0.9.0 → 0.10.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/index.js CHANGED
@@ -50,8 +50,9 @@ export const buildTag = (language, defaultType) => {
50
50
  });
51
51
  };
52
52
 
53
- export const parse = (language, type, sourceText) => {
54
- return new TemplateParser(language, [sourceText], []).eval({
53
+ export const parse = (language, type, sourceText, expressions = []) => {
54
+ let source = Array.isArray(sourceText) ? sourceText : [sourceText];
55
+ return new TemplateParser(language, source, expressions).eval({
55
56
  language: language.name,
56
57
  type,
57
58
  });
@@ -1,6 +1,6 @@
1
1
  import * as sym from '@bablr/agast-vm-helpers/symbols';
2
2
  import * as JSON from './json.js';
3
- import { isArray } from 'iter-tools-es';
3
+ import { get, sourceTextFor } from '@bablr/agast-helpers/tree';
4
4
 
5
5
  const _ = /\s+/y;
6
6
  const PN = 'Punctuator';
@@ -50,7 +50,7 @@ export const grammar = class CSTMLMiniparserGrammar {
50
50
  Document(p) {
51
51
  p.eatProduction('DoctypeTag', { path: 'doctype' });
52
52
  p.eatMatchTrivia(_);
53
- p.eatProduction('Node', { path: 'tree' }, { fragment: true });
53
+ p.eatProduction('Node', { path: 'tree' }, { forceFragment: true });
54
54
  }
55
55
 
56
56
  // @Node
@@ -98,25 +98,19 @@ export const grammar = class CSTMLMiniparserGrammar {
98
98
 
99
99
  // @Node
100
100
  Node(p, props) {
101
- let open = p.eatProduction('OpenNodeTag', { path: 'open' }, props);
101
+ let open = p.eatProduction('OpenNodeTag', { path: 'open', noInterpolate: true }, props);
102
102
 
103
103
  p.eatMatchTrivia(_);
104
104
 
105
- if (open.properties.flags?.token) {
106
- p.eatProduction('NodeChild', { path: 'children[]' }, { token: true });
107
- p.eatMatchTrivia(_);
108
- } else if (!open.properties.selfClosingTagToken) {
109
- let lastIndex = p.idx;
110
- while (!(p.match('</') || p.done)) {
111
- p.eatProduction('NodeChild', { path: 'children[]' });
105
+ if (open.attributes.balanced) {
106
+ let token = !!get(['flags', 'token'], open);
107
+
108
+ while (p.atExpression || !(p.match(/<\/[^/]/y) || p.done)) {
109
+ p.eatProduction('NodeChild', { path: 'children[]' }, { token });
112
110
  p.eatMatchTrivia(_);
113
- if (p.idx === lastIndex) break;
114
- lastIndex = p.idx;
115
111
  }
116
- }
117
112
 
118
- if (!open.properties.selfClosingTagToken) {
119
- p.eatProduction('CloseNodeTag', { path: 'close' });
113
+ p.eatProduction('CloseNodeTag', { path: 'close', noInterpolate: true });
120
114
  }
121
115
  }
122
116
 
@@ -129,15 +123,17 @@ export const grammar = class CSTMLMiniparserGrammar {
129
123
 
130
124
  if (token) {
131
125
  if (p.match(/<\*?@/y)) {
132
- p.eatProduction('Node');
126
+ p.eatProduction('Property');
133
127
  } else {
134
128
  p.eatProduction('LiteralTag');
135
129
  }
136
130
  } else {
137
- if (p.match(/[:<a-zA-Z`\\\u{80}-\u{10ffff}.]|[.#@]/uy)) {
131
+ if (p.match(/[:<a-zA-Z`\\\u{80}-\u{10ffff}.]|[.#@_]/uy) || p.atExpression) {
138
132
  p.eatProduction('Property');
139
133
  } else if (p.match(/['"]/y)) {
140
- p.eatProduction('LiteralTag');
134
+ p.eatProduction('Property');
135
+ } else {
136
+ p.fail();
141
137
  }
142
138
  }
143
139
  }
@@ -157,18 +153,23 @@ export const grammar = class CSTMLMiniparserGrammar {
157
153
 
158
154
  // @Node
159
155
  Property(p) {
160
- if (p.match(/[a-zA-Z`\\\u{80}-\u{10ffff}.]|[.#@]/uy)) {
161
- p.eatProduction('ReferenceTag', { path: 'reference' });
156
+ let ref = null;
157
+ if (p.match(/[a-zA-Z`\\\u{80}-\u{10ffff}.]|[.#@_]/uy)) {
158
+ ref = p.eatProduction('ReferenceTag', { path: 'reference' });
162
159
  }
163
160
  p.eatMatchTrivia(_);
164
161
  if (p.match(':')) {
165
162
  p.eatProduction('BindingTag', { path: 'binding' });
166
163
  p.eatMatchTrivia(_);
167
164
  }
168
- p.eatProduction('PropertyValue', { path: 'value' });
165
+ let refType = ref && get('type', ref);
166
+ p.eatProduction('PropertyValue', {
167
+ path: 'value',
168
+ allowFragment: refType && sourceTextFor(refType) === '_',
169
+ });
169
170
  }
170
171
 
171
- PropertyValue(p) {
172
+ PropertyValue(p, { allowFragment }) {
172
173
  if (p.match('null')) {
173
174
  p.eatProduction('NullTag');
174
175
  } else if (p.match(/\[\]|undefined/y)) {
@@ -176,7 +177,7 @@ export const grammar = class CSTMLMiniparserGrammar {
176
177
  } else if (p.match('<//>')) {
177
178
  p.eatProduction('GapTag');
178
179
  } else {
179
- p.eatProduction('Node');
180
+ p.eatProduction('Node', { allowFragment, propertyValue: true });
180
181
  }
181
182
  }
182
183
 
@@ -185,11 +186,16 @@ export const grammar = class CSTMLMiniparserGrammar {
185
186
  p.eatMatch('*', PN, { path: 'tokenToken' });
186
187
  p.eatMatch('$', PN, { path: 'hasGapToken' });
187
188
  p.eatMatch('_', PN, { path: 'fragmentToken' });
188
- p.eatMatch('_', PN, { path: 'coverFragmentToken' });
189
+ p.eatMatch('_', PN, { path: 'multiFragmentToken' });
189
190
  }
190
191
 
191
192
  // @Node
192
- OpenNodeTag(p, { fragment } = {}) {
193
+ OpenNodeTag(p, { forceFragment = false, allowFragment = true, propertyValue = false } = {}) {
194
+ if (p.match(/['"]/y)) {
195
+ p.eatProduction('JSON:String', { path: 'literalValue' });
196
+ return;
197
+ }
198
+
193
199
  p.eat('<', PN, { path: 'openToken', startSpan: 'Tag', balanced: '>' });
194
200
 
195
201
  let flags = null;
@@ -199,13 +205,23 @@ export const grammar = class CSTMLMiniparserGrammar {
199
205
 
200
206
  let sp = null;
201
207
 
202
- if (fragment && !flags.properties.fragmentToken) throw new Error();
208
+ let fragmentFlag = get('fragmentToken', flags);
209
+ let multiFragmentFlag = get('multiFragmentFlag', flags);
210
+ let tokenFlag = get('tokenToken', flags);
211
+
212
+ if (propertyValue && fragmentFlag && !multiFragmentFlag) throw new Error();
203
213
 
204
- if (!flags.properties.fragmentToken && !p.match(/./sy)) {
214
+ if (forceFragment && !fragmentFlag) throw new Error();
215
+
216
+ if (!fragmentFlag && !p.match(/./sy)) {
217
+ throw new Error();
218
+ }
219
+
220
+ if (fragmentFlag && !allowFragment) {
205
221
  throw new Error();
206
222
  }
207
223
 
208
- if (!flags.properties.fragmentToken) {
224
+ if (!fragmentFlag) {
209
225
  p.eatProduction('Identifier', { path: 'type' });
210
226
 
211
227
  sp = p.eatMatchTrivia(_);
@@ -213,24 +229,24 @@ export const grammar = class CSTMLMiniparserGrammar {
213
229
  let iv;
214
230
 
215
231
  if (sp && (p.match(/['"]/y) || p.atExpression)) {
216
- iv = p.eatProduction('JSON:String', { path: 'intrinsicValue' });
232
+ iv = p.eatProduction('JSON:String', { path: 'literalValue' });
217
233
 
218
234
  sp = p.eatMatchTrivia(_);
219
235
  }
220
236
 
221
- if (!flags.properties.tokenToken && iv) {
222
- throw new Error();
223
- }
224
-
225
237
  if (p.match('{') || p.atExpression) {
226
- p.eatProduction('JSON:Object');
238
+ p.eatProduction('JSON:Object', { path: 'attributes' });
227
239
  sp = p.eatMatchTrivia(_);
228
240
  }
229
241
 
230
242
  p.eatMatchTrivia(_);
231
243
  }
232
- p.eatMatch('/', PN, { path: 'selfClosingTagToken' });
244
+ let sc = p.eatMatch('/', PN, { path: 'selfClosingToken' });
233
245
  p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
246
+
247
+ const balanced = !sc && (p.path.depth > 0 || p.span.type !== 'Bare');
248
+
249
+ return { attrs: { balanced } };
234
250
  }
235
251
 
236
252
  // @Node
@@ -274,7 +290,7 @@ export const grammar = class CSTMLMiniparserGrammar {
274
290
  let lit, esc;
275
291
  do {
276
292
  if ((esc = p.match('\\'))) {
277
- p.eatMatchEscape(/\\(u(\{[0-9a-fA-F]\}|\d{4}))/y);
293
+ p.eatEscape(/\\(u(\{[0-9a-fA-F]\}|\d{4}))/y);
278
294
  } else {
279
295
  if (!quoted) {
280
296
  lit = p.eatMatchLiteral(/[a-zA-Z\u{80}-\u{10ffff}][a-zA-Z0-9_\u{80}-\u{10ffff}-]*/uy);
@@ -296,10 +312,12 @@ export const grammar = class CSTMLMiniparserGrammar {
296
312
 
297
313
  // @Node
298
314
  ReferenceTag(p) {
299
- let name;
300
- if ((name = p.match(/[.#@]/y))) {
301
- p.eat(name, PN, { path: 'type' });
302
- } else {
315
+ let type;
316
+ if ((type = p.match(/[.#@_]/y))) {
317
+ p.eat(type, PN, { path: 'type' });
318
+ }
319
+
320
+ if (!type || type === '#') {
303
321
  p.eatProduction('Identifier', { path: 'name' });
304
322
  }
305
323
  p.eatMatchTrivia(_);
@@ -7,7 +7,7 @@ import * as Regex from './regex.js';
7
7
  const _ = /\s+/y;
8
8
  const PN = 'Punctuator';
9
9
  const KW = 'Keyword';
10
- const ID = 'Identifier';
10
+ const LIT = 'Identifier';
11
11
 
12
12
  export const name = 'JSON';
13
13
 
@@ -34,6 +34,7 @@ export const covers = new Map([
34
34
  'Integer',
35
35
  'String',
36
36
  'StringContent',
37
+ 'Identifier',
37
38
  ]),
38
39
  ],
39
40
  ['Expression', new Set(['Object', 'Array', 'Boolean', 'Null', 'Number', 'String'])],
@@ -125,7 +126,11 @@ export const grammar = class JSONMiniparserGrammar {
125
126
 
126
127
  // @Node
127
128
  Property(p) {
128
- p.eat(/[a-zA-Z]+/y, ID, { path: 'key' });
129
+ if (p.match(/['"]/y)) {
130
+ p.eatProduction('String', { path: 'key' });
131
+ } else {
132
+ p.eatProduction('Identifier', { path: 'key' });
133
+ }
129
134
  p.eatMatchTrivia(_);
130
135
  p.eat(':', PN, { path: 'mapToken' });
131
136
  p.eatMatchTrivia(_);
@@ -216,6 +221,11 @@ export const grammar = class JSONMiniparserGrammar {
216
221
  p.eatLiteral(/\d/y);
217
222
  }
218
223
 
224
+ // @Node
225
+ Identifier(p) {
226
+ p.eat(/[a-zA-Z]+/y, LIT, { path: 'content' });
227
+ }
228
+
219
229
  // @Node
220
230
  String(p) {
221
231
  const q = p.match(/['"]/y) || '"';
@@ -2,6 +2,7 @@ import * as sym from '@bablr/agast-vm-helpers/symbols';
2
2
  import * as Regex from './regex.js';
3
3
  import * as CSTML from './cstml.js';
4
4
  import * as JSON from './json.js';
5
+ import { get } from '@bablr/agast-helpers/path';
5
6
 
6
7
  const _ = /\s+/y;
7
8
  const PN = 'Punctuator';
@@ -92,15 +93,18 @@ export const grammar = class SpamexMiniparserGrammar {
92
93
 
93
94
  // @Node
94
95
  ReferenceMatcher(p) {
95
- let name;
96
- if ((name = p.match(/[.#@]/y))) {
97
- name = p.eat(name, PN, { path: 'type' });
98
- } else if (p.match(/[A-Za-z]/y)) {
96
+ let name, type;
97
+ if ((type = p.match(/[.#@]/y))) {
98
+ p.eat(type, PN, { path: 'type' });
99
+ }
100
+
101
+ if ((!type || type === '#') && p.match(/[A-Za-z]/y)) {
99
102
  name = p.eatProduction('CSTML:Identifier', { path: 'name' });
100
103
  }
101
104
 
102
105
  let open =
103
- name && p.eatMatch('[', PN, { path: 'openIndexToken', startSpan: 'Index', balanced: ']' });
106
+ (name || type) &&
107
+ p.eatMatch('[', PN, { path: 'openIndexToken', startSpan: 'Index', balanced: ']' });
104
108
 
105
109
  if (open) {
106
110
  p.eatMatchTrivia(_);
@@ -143,10 +147,10 @@ export const grammar = class SpamexMiniparserGrammar {
143
147
  BasicNodeMatcher(p) {
144
148
  let open = p.eatProduction('OpenNodeMatcher', { path: 'open' });
145
149
 
146
- if (!open.properties.selfClosingTagToken) {
150
+ if (!get('selfClosingToken', open)) {
147
151
  p.eatMatchTrivia(_);
148
152
 
149
- if (open.properties.flags?.token) {
153
+ if (get('flags', open)?.token) {
150
154
  // p.eatProduction('NodeChild', { path: 'children[]' }, { token: true });
151
155
  // p.eatMatchTrivia(_);
152
156
  } else {
@@ -179,7 +183,7 @@ export const grammar = class SpamexMiniparserGrammar {
179
183
  let sp = p.eatMatchTrivia(_);
180
184
 
181
185
  if (sp && ((p.match(/['"/]/y) && !p.match('/>')) || p.atExpression)) {
182
- p.eatProduction('StringMatcher', { path: 'intrinsicValue' });
186
+ p.eatProduction('StringMatcher', { path: 'literalValue' });
183
187
 
184
188
  sp = p.eatMatchTrivia(_);
185
189
  }
@@ -190,7 +194,7 @@ export const grammar = class SpamexMiniparserGrammar {
190
194
  }
191
195
 
192
196
  p.eatMatchTrivia(_);
193
- p.eatMatch('/', PN, { path: 'selfClosingTagToken' });
197
+ p.eatMatch('/', PN, { path: 'selfClosingToken' });
194
198
  p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
195
199
  }
196
200
 
package/lib/match.js CHANGED
@@ -51,8 +51,7 @@ export class Match {
51
51
 
52
52
  if (isNode && !isCover) {
53
53
  path.node = createNode();
54
- path.node.type = type;
55
- path.node.language = resolvedLanguage.canonicalURL;
54
+ path.node.type = Symbol.for(type);
56
55
  }
57
56
 
58
57
  return new Match(null, resolvedLanguage, id, attrs, path);
@@ -78,7 +77,6 @@ export class Match {
78
77
  }
79
78
  path.node = createNode();
80
79
  path.node.type = Symbol.for(type);
81
- path.node.language = resolvedLanguage.canonicalURL;
82
80
  }
83
81
 
84
82
  return new Match(this, resolvedLanguage, id, { ...baseAttrs, ...attrs }, path);
package/lib/miniparser.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  nodeFlags,
17
17
  } from '@bablr/agast-helpers/builders';
18
18
  import { add, buildToken, shift } from '@bablr/agast-helpers/tree';
19
- import * as sumtree from '@bablr/agast-helpers/children';
19
+ import * as Tags from '@bablr/agast-helpers/tags';
20
20
  import { buildPathSegment, get } from '@bablr/agast-helpers/path';
21
21
  import { parseReference } from '@bablr/agast-helpers/shorthand';
22
22
 
@@ -180,12 +180,12 @@ export class TemplateParser {
180
180
 
181
181
  if (isArray(result)) {
182
182
  for (const value of result) {
183
- node.children = sumtree.push(node.children, path);
183
+ node.tags = Tags.push(node.tags, path);
184
184
 
185
185
  add(node, path, value);
186
186
  }
187
187
  } else {
188
- node.children = sumtree.push(node.children, path);
188
+ node.tags = Tags.push(node.tags, path);
189
189
 
190
190
  add(node, path, result);
191
191
  }
@@ -201,7 +201,7 @@ export class TemplateParser {
201
201
 
202
202
  if (isNode) {
203
203
  let { node } = this.path;
204
- node.children = sumtree.push(node.children, buildOpenNodeTag(nodeFlags, node.type));
204
+ node.tags = Tags.push(node.tags, buildOpenNodeTag(nodeFlags, node.type));
205
205
  }
206
206
 
207
207
  const result = getPrototypeOf(grammar)[type].call(grammar, this, props);
@@ -214,14 +214,14 @@ export class TemplateParser {
214
214
  const { node } = this.path;
215
215
  if (result?.attrs) {
216
216
  node.attributes = result.attrs;
217
- node.children = sumtree.replaceAt(
217
+ node.tags = Tags.replaceAt(
218
218
  0,
219
- node.children,
219
+ node.tags,
220
220
  buildOpenNodeTag(nodeFlags, node.type, result.attrs),
221
221
  );
222
222
  }
223
223
 
224
- node.children = sumtree.push(node.children, buildCloseNodeTag());
224
+ node.tags = Tags.push(node.tags, buildCloseNodeTag());
225
225
 
226
226
  if (parentPath?.node && !covers.has(type)) {
227
227
  const path = parseReference(this.m.attrs.path);
@@ -244,9 +244,9 @@ export class TemplateParser {
244
244
  if (this.path?.node) {
245
245
  const isTag = (child) => [LiteralTag, Escape].includes(child.type);
246
246
 
247
- const { children } = this.path.node;
247
+ const { tags } = this.path.node;
248
248
 
249
- if (find(isTag, sumtree.traverse(children)) && every(isTag, sumtree.traverse(children))) {
249
+ if (find(isTag, Tags.traverse(tags)) && every(isTag, Tags.traverse(tags))) {
250
250
  throw new Error('strings must be wrapped in nodes');
251
251
  }
252
252
  }
@@ -306,7 +306,7 @@ export class TemplateParser {
306
306
 
307
307
  // get the most recently produced node and detach it from its parent
308
308
 
309
- const ref = sumtree.getAt(-3, node.children);
309
+ const ref = Tags.getAt(-1, node.tags).value.tags[0];
310
310
 
311
311
  if (!ref.value.flags.expression) throw new Error();
312
312
 
@@ -432,7 +432,7 @@ export class TemplateParser {
432
432
 
433
433
  this.idx += result.length;
434
434
 
435
- this.node.children = sumtree.push(this.node.children, buildLiteralTag(result));
435
+ this.node.tags = Tags.push(this.node.tags, buildLiteralTag(result));
436
436
 
437
437
  return result;
438
438
  }
@@ -443,7 +443,7 @@ export class TemplateParser {
443
443
  if (result) {
444
444
  this.idx += result.length;
445
445
 
446
- this.node.children = sumtree.push(this.node.children, buildLiteralTag(result));
446
+ this.node.tags = Tags.push(this.node.tags, buildLiteralTag(result));
447
447
  }
448
448
 
449
449
  return result;
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
  }
@@ -34,36 +32,3 @@ export class Path {
34
32
  return new Path(id, attrs);
35
33
  }
36
34
  }
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.10.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.9.0",
24
+ "@bablr/agast-vm-helpers": "0.9.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
  },