@bablr/boot 0.1.5 → 0.1.6

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
@@ -35,6 +35,6 @@ const spam = buildTag(spamex, 'Matcher');
35
35
  const re = buildTag(regex, 'Pattern');
36
36
  const str = buildTag(string, 'String');
37
37
  const num = buildTag(number, 'Number');
38
- const cst = buildTag(cstml, 'Node');
38
+ const cst = buildTag(cstml, 'Fragment');
39
39
 
40
40
  module.exports = { re, spam, str, num, i, cst };
@@ -19,28 +19,87 @@ const covers = buildCovers({
19
19
  'Property',
20
20
  'TagType',
21
21
  'Node',
22
+ 'OpenFragmentNodeTag',
23
+ 'OpenTerminalNodeTag',
22
24
  'OpenNodeTag',
23
25
  'CloseNodeTag',
24
26
  'Terminal',
25
27
  ],
26
- [sym.fragment]: ['Attributes'],
28
+ [sym.fragment]: ['Attributes', 'Fragment'],
27
29
  Attribute: ['MappingAttribute', 'BooleanAttribute'],
28
30
  AttributeValue: ['String:String', 'Number:Number'],
29
31
  TagType: ['Identifier', 'GlobalIdentifier'],
30
32
  Terminal: ['Literal', 'Trivia', 'Escape'],
33
+ EmbeddedTerminal: ['Literal', 'Escape'],
31
34
  });
32
35
 
33
36
  const grammar = class CSTMLMiniparserGrammar {
34
37
  Fragment(p) {
35
38
  p.eatMatchTrivia(_);
36
- while (p.match(/['"]|<[^/]/y) || p.atExpression) {
37
- if (p.match(/['"]/y)) {
38
- p.eatProduction('Terminal', { path: 'properties[]' });
39
+ p.eatProduction('OpenFragmentNodeTag', { path: 'open' });
40
+ p.eatProduction('FragmentChildren');
41
+ p.eatProduction('CloseNodeTag', { path: 'close' });
42
+ p.eatMatchTrivia(_);
43
+ }
44
+
45
+ FragmentChildren(p) {
46
+ p.eatMatchTrivia(_);
47
+ while (p.match(/#['"]|\w/y) || p.atExpression) {
48
+ if (p.match(/#['"]/y)) {
49
+ p.eatProduction('Trivia', { path: 'children[]' });
50
+ } else {
51
+ p.eatProduction('Property', { path: 'children[]' });
52
+ }
53
+ p.eatMatchTrivia(_);
54
+ }
55
+ }
56
+
57
+ // @Node
58
+ Node(p) {
59
+ const openType = p.match('<|') ? 'OpenTerminalNodeTag' : 'OpenNodeTag';
60
+ const childrenType = openType === 'OpenNodeTag' ? 'NodeChildren' : 'TerminalNodeChildren';
61
+
62
+ if (p.match('<>')) throw new Error('Fragment is not a node');
63
+
64
+ const open = p.eatProduction(openType, { path: 'open' });
65
+ if (!open.attributes.selfClosing) {
66
+ p.eatProduction(childrenType);
67
+ p.eatProduction('CloseNodeTag', { path: 'close' });
68
+ }
69
+ }
70
+
71
+ NodeChildren(p) {
72
+ let properties = 0;
73
+
74
+ p.eatMatchTrivia(_);
75
+ while (p.match(/#['"]|\w/y) || p.atExpression) {
76
+ if (p.match(/#['"]/y)) {
77
+ p.eatProduction('Trivia', { path: 'children[]' });
39
78
  } else {
40
- p.eatProduction('Property', { path: 'properties[]' });
79
+ p.eatProduction('Property', { path: 'children[]' });
80
+ properties++;
41
81
  }
42
82
  p.eatMatchTrivia(_);
43
83
  }
84
+
85
+ if (!properties) throw new Error('Nodes must match text');
86
+ }
87
+
88
+ TerminalNodeChildren(p) {
89
+ let properties = 0;
90
+
91
+ p.eatMatchTrivia(_);
92
+ while (p.match(/[!#]?['"]|\w/y) || p.atExpression) {
93
+ if (p.match(/[!#]?['"]/y)) {
94
+ p.eatProduction('Terminal', { path: 'children[]' });
95
+ } else {
96
+ p.eatProduction('Property', { path: 'children[]' });
97
+ properties++;
98
+ }
99
+ p.eatMatchTrivia(_);
100
+ }
101
+
102
+ if (!properties) throw new Error('Nodes must match text');
44
103
  }
45
104
 
46
105
  // @Node
@@ -53,10 +112,9 @@ const grammar = class CSTMLMiniparserGrammar {
53
112
  }
54
113
 
55
114
  // @Node
56
- Node(p) {
57
- p.eatProduction('OpenNodeTag', { path: 'open' });
58
- p.eatProduction('Fragment');
59
- p.eatProduction('CloseNodeTag', { path: 'close' });
115
+ OpenFragmentNodeTag(p) {
116
+ p.eat('<', PN, { path: 'open', startSpan: 'Tag', balanced: '>' });
117
+ p.eat('>', PN, { path: 'close', endSpan: 'Tag', balancer: true });
60
118
  }
61
119
 
62
120
  // @Node
@@ -75,6 +133,31 @@ const grammar = class CSTMLMiniparserGrammar {
75
133
  p.eat('>', PN, { path: 'close', endSpan: 'Tag', balancer: true });
76
134
  }
77
135
 
136
+ // @Node
137
+ OpenTerminalNodeTag(p) {
138
+ p.eat('<|', PN, { path: 'open', startSpan: 'Tag', balanced: '>' });
139
+ p.eatMatchTrivia(_);
140
+ p.eatProduction('TagType', { path: 'type' });
141
+
142
+ let sp = p.eatMatchTrivia(_);
143
+
144
+ let value;
145
+ if (sp && p.match(/['"/]/y)) {
146
+ value = p.eatProduction('EmbeddedTerminal', { path: 'value' });
147
+ sp = p.eatMatchTrivia(_);
148
+ }
149
+
150
+ if ((sp && p.match(/\w+/y)) || p.atExpression) {
151
+ p.eatProduction('Attributes');
152
+ sp = p.eatMatchTrivia(_);
153
+ }
154
+
155
+ p.eatMatchTrivia(_);
156
+ p.eat('|>', PN, { path: 'close', endSpan: 'Tag', balancer: true });
157
+
158
+ return { attrs: { selfClosing: !!value } };
159
+ }
160
+
78
161
  // @Node
79
162
  CloseNodeTag(p) {
80
163
  p.eat('</', PN, { path: 'open', startSpan: 'Tag', balanced: '>' });
@@ -158,8 +241,21 @@ const grammar = class CSTMLMiniparserGrammar {
158
241
  p.eatProduction('Escape');
159
242
  } else if (p.match(/#['"]/y)) {
160
243
  p.eatProduction('Trivia');
244
+ } else if (p.match(/['"]/y)) {
245
+ p.eatProduction('Literal');
161
246
  } else {
247
+ throw new Error();
248
+ }
249
+ }
250
+
251
+ // @Cover
252
+ EmbeddedTerminal(p) {
253
+ if (p.match(/!['"]/y)) {
254
+ p.eatProduction('Escape');
255
+ } else if (p.match(/['"]/y)) {
162
256
  p.eatProduction('Literal');
257
+ } else {
258
+ throw new Error();
163
259
  }
164
260
  }
165
261
 
package/lib/miniparser.js CHANGED
@@ -5,7 +5,8 @@ const isString = require('iter-tools-es/methods/is-string');
5
5
  const isObject = require('iter-tools-es/methods/is-object');
6
6
  const sym = require('./symbols.js');
7
7
  const { Match } = require('./match.js');
8
- const { set, isRegex, isArray, getPrototypeOf, buildNode, parsePath } = require('./utils.js');
8
+ const { parsePath } = require('./path.js');
9
+ const { set, isRegex, isArray, getPrototypeOf, buildNode } = require('./utils.js');
9
10
 
10
11
  class TemplateParser {
11
12
  constructor(rootLanguage, quasis, expressions) {
@@ -163,19 +164,20 @@ class TemplateParser {
163
164
  for (const { 0: key, 1: property } of Object.entries(result.properties)) {
164
165
  if (isArray(property)) {
165
166
  for (const value of property) {
166
- set(properties, `[${key}]`, value);
167
+ set(properties, { pathName: key, pathIsArray: true }, value);
167
168
  }
168
169
  } else {
169
- set(properties, key, property);
170
+ set(properties, { pathName: key, pathIsArray: false }, property);
170
171
  }
171
172
  }
172
173
  }
173
174
  } else if (parentPath?.node && (isNode || covers.has(type))) {
174
175
  const { properties, children } = parentPath.node;
176
+ const path = parsePath(this.m.attrs.path);
175
177
 
176
- children.push({ ...ref(this.m.attrs.path), id });
178
+ children.push({ ...ref(path), id });
177
179
 
178
- set(properties, this.m.attrs.path, result);
180
+ set(properties, path, result);
179
181
  }
180
182
  } else {
181
183
  if (isEmbedded) {
@@ -194,9 +196,11 @@ class TemplateParser {
194
196
  node.attributes = result.attrs;
195
197
  }
196
198
  if (parentPath?.node && !covers.has(type)) {
197
- parentPath.node.children.push(ref(this.m.attrs.path));
199
+ const path = parsePath(this.m.attrs.path);
198
200
 
199
- set(parentPath.node.properties, this.m.attrs.path, node);
201
+ parentPath.node.children.push(ref(path));
202
+
203
+ set(parentPath.node.properties, path, node);
200
204
  }
201
205
  }
202
206
  }
@@ -258,8 +262,10 @@ class TemplateParser {
258
262
 
259
263
  this.held = null;
260
264
 
261
- children.push(ref(attrs.path));
262
- set(properties, attrs.path, held);
265
+ const path = parsePath(attrs.path);
266
+
267
+ children.push(ref(path));
268
+ set(properties, path, held);
263
269
 
264
270
  return held;
265
271
  }
@@ -276,8 +282,7 @@ class TemplateParser {
276
282
  throw new Error();
277
283
  }
278
284
 
279
- const path = lastChild.value;
280
- const { pathIsArray, pathName } = parsePath(path);
285
+ const { pathIsArray, pathName } = lastChild.value;
281
286
 
282
287
  this.held = pathIsArray ? arrayLast(properties[pathName]) : properties[pathName];
283
288
 
@@ -304,9 +309,11 @@ class TemplateParser {
304
309
 
305
310
  this.updateSpans(attrs);
306
311
 
307
- set(this.node.properties, attrs.path, buildNode(this.buildId(type), [lit(result)]));
312
+ const path = parsePath(attrs.path);
308
313
 
309
- this.node.children.push(ref(attrs.path));
314
+ set(this.node.properties, path, buildNode(this.buildId(type), [lit(result)]));
315
+
316
+ this.node.children.push(ref(path));
310
317
 
311
318
  return result;
312
319
  }
@@ -332,9 +339,11 @@ class TemplateParser {
332
339
 
333
340
  this.idx += result.length;
334
341
 
335
- set(this.node.properties, attrs.path, buildNode(this.buildId(type), [lit(result)]));
342
+ const path = parsePath(attrs.path);
343
+
344
+ set(this.node.properties, path, buildNode(this.buildId(type), [lit(result)]));
336
345
 
337
- this.node.children.push(ref(attrs.path));
346
+ this.node.children.push(ref(path));
338
347
  }
339
348
  return result;
340
349
  }
package/lib/path.js CHANGED
@@ -10,6 +10,16 @@ const buildNode = (id) => {
10
10
  };
11
11
  };
12
12
 
13
+ const stripPathBraces = (str) => (str.endsWith('[]') ? str.slice(0, -2) : str);
14
+
15
+ const parsePath = (str) => {
16
+ const pathName = stripPathBraces(str);
17
+
18
+ if (!/^\w+$/.test(pathName)) throw new Error();
19
+
20
+ return { pathIsArray: pathName !== str, pathName };
21
+ };
22
+
13
23
  class Path {
14
24
  constructor(id, attributes, parent = null) {
15
25
  this.id = id;
@@ -44,4 +54,4 @@ class Path {
44
54
  }
45
55
  }
46
56
 
47
- module.exports = { Path, buildNode };
57
+ module.exports = { Path, buildNode, parsePath };
package/lib/utils.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const every = require('iter-tools-es/methods/every');
2
2
  const isArray = require('iter-tools-es/methods/is-array');
3
3
  const isString = require('iter-tools-es/methods/is-string');
4
- const { parsePath, stripPathBraces } = require('@bablr/boot-helpers/path');
5
4
 
6
5
  const { hasOwn, getPrototypeOf, getOwnPropertySymbols } = Object;
7
6
  const isSymbol = (value) => typeof value === 'symbol';
@@ -54,7 +53,11 @@ const buildCovers = (rawAliases) => {
54
53
  };
55
54
 
56
55
  const set = (obj, path, value) => {
57
- const { pathIsArray, pathName } = parsePath(path);
56
+ const { pathIsArray, pathName } = path;
57
+
58
+ if (!pathName) {
59
+ throw new Error();
60
+ }
58
61
 
59
62
  if (pathIsArray) {
60
63
  if (!obj[pathName]) {
@@ -105,8 +108,6 @@ const id = (...args) => {
105
108
  };
106
109
 
107
110
  module.exports = {
108
- parsePath,
109
- stripPathBraces,
110
111
  buildCovers,
111
112
  set,
112
113
  resolveDependentLanguage,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bablr/boot",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Compile-time tools for bootstrapping BABLR VM",
5
5
  "engines": {
6
6
  "node": ">=12.0.0"
@@ -18,7 +18,7 @@
18
18
  "@babel/helper-module-imports": "^7.22.15",
19
19
  "@babel/template": "^7.22.15",
20
20
  "@babel/types": "7.23.0",
21
- "@bablr/boot-helpers": "0.1.1",
21
+ "@bablr/boot-helpers": "0.1.2",
22
22
  "escape-string-regexp": "4.0.0",
23
23
  "iter-tools-es": "^7.5.3"
24
24
  },
@@ -23,20 +23,21 @@ const isBoolean = (v) => typeof v === 'boolean';
23
23
 
24
24
  const isPlainObject = (v) => isObject(v) && !isArray(v);
25
25
 
26
- const set = (obj, path, value, pathIsArray) => {
26
+ const set = (obj, path, value) => {
27
+ const { pathName, pathIsArray } = path;
27
28
  if (pathIsArray) {
28
- if (!obj[path]) {
29
- obj[path] = [];
29
+ if (!obj[pathName]) {
30
+ obj[pathName] = [];
30
31
  }
31
32
 
32
- if (!isArray(obj[path])) throw new Error('bad array value');
33
+ if (!isArray(obj[pathName])) throw new Error('bad array value');
33
34
 
34
- obj[path].push(value);
35
+ obj[pathName].push(value);
35
36
  } else {
36
- if (hasOwn(obj, path)) {
37
+ if (hasOwn(obj, pathName)) {
37
38
  throw new Error('duplicate child name');
38
39
  }
39
- obj[path] = value;
40
+ obj[pathName] = value;
40
41
  }
41
42
  };
42
43
 
@@ -64,7 +65,9 @@ const getASTValue = (v, exprs, bindings) => {
64
65
 
65
66
  const generateNodeChild = (child, bindings) => {
66
67
  if (child.type === 'Reference') {
67
- return expression(`%%t%%.ref\`${child.value}\``)({ t: bindings.t });
68
+ const { pathName, pathIsArray } = child.value;
69
+ const printedRef = pathIsArray ? `${pathName}[]` : pathName;
70
+ return expression(`%%t%%.ref\`${printedRef}\``)({ t: bindings.t });
68
71
  } else if (child.type === 'Literal') {
69
72
  return expression(`%%t%%.lit\`${child.value.replace(/\\/g, '\\\\')}\``)({ t: bindings.t });
70
73
  } else if (child.type === 'Trivia') {
@@ -82,7 +85,7 @@ const generateNodeChild = (child, bindings) => {
82
85
 
83
86
  const generateNode = (node, exprs, bindings) => {
84
87
  const resolver = new PathResolver(node);
85
- const { children, type, language, attributes, properties } = node;
88
+ const { children, type, language, attributes } = node;
86
89
  const properties_ = {};
87
90
  const children_ = [];
88
91
 
@@ -95,8 +98,8 @@ const generateNode = (node, exprs, bindings) => {
95
98
 
96
99
  if (child.type === 'Reference') {
97
100
  const path = child.value;
101
+ const { pathIsArray } = path;
98
102
  const resolved = resolver.get(path);
99
- const pathIsArray = isArray(properties[path]);
100
103
 
101
104
  let embedded = resolved;
102
105
  if (resolved) {
@@ -118,7 +121,7 @@ const generateNode = (node, exprs, bindings) => {
118
121
  }
119
122
  }
120
123
 
121
- set(properties_, path, embedded, pathIsArray);
124
+ set(properties_, path, embedded);
122
125
  }
123
126
  }
124
127
 
@@ -157,7 +160,7 @@ const topTypes = {
157
160
  spam: 'Matcher',
158
161
  str: 'String',
159
162
  num: 'Number',
160
- cst: 'Node',
163
+ cst: 'Fragment',
161
164
  };
162
165
 
163
166
  const getTopScope = (scope) => {