@bablr/boot 0.1.1 → 0.1.3

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.
@@ -22,7 +22,7 @@ const grammar = class CSTMLMiniparserGrammar {
22
22
  Fragment(p) {
23
23
  p.eatMatchTrivia(_);
24
24
  while (p.match(/<[^/]/y) || p.atExpression) {
25
- p.eatProduction('Property', { path: 'properties' });
25
+ p.eatProduction('Property', { path: 'properties[]' });
26
26
  p.eatMatchTrivia(_);
27
27
  }
28
28
  }
@@ -73,7 +73,7 @@ const grammar = class CSTMLMiniparserGrammar {
73
73
  if (p.atExpression) {
74
74
  p.eatProduction('Attributes'); // ??
75
75
  } else {
76
- p.eatProduction('Attribute', { path: '[attributes]' });
76
+ p.eatProduction('Attribute', { path: 'attributes[]' });
77
77
  }
78
78
  if (p.match(/\s+\w/y) || (p.match(/\s+$/y) && !p.quasisDone)) {
79
79
  sp = p.eatMatchTrivia(_);
@@ -69,7 +69,7 @@ const grammar = class InstructionMiniparserGrammar {
69
69
  let first = true;
70
70
  let sep;
71
71
  while (first || (sep && (p.match(/./y) || p.atExpression))) {
72
- p.eatProduction('Property', { path: '[properties]' });
72
+ p.eatProduction('Property', { path: 'properties[]' });
73
73
  sep = p.eatMatchTrivia(_);
74
74
  first = false;
75
75
  }
@@ -97,7 +97,7 @@ const grammar = class InstructionMiniparserGrammar {
97
97
  let first = true;
98
98
  let sep;
99
99
  while (first || (sep && (p.match(/./y) || p.atExpression))) {
100
- p.eatProduction('Expression', { path: '[elements]' });
100
+ p.eatProduction('Expression', { path: 'elements[]' });
101
101
  sep = p.eatMatchTrivia(_);
102
102
  first = false;
103
103
  }
@@ -113,7 +113,7 @@ const grammar = class InstructionMiniparserGrammar {
113
113
 
114
114
  let i = 0;
115
115
  while (i === 0 || (sep && (p.match(/./y) || p.atExpression))) {
116
- p.eatProduction('Expression', { path: '[values]' });
116
+ p.eatProduction('Expression', { path: 'values[]' });
117
117
  sep = p.eatMatchTrivia(_);
118
118
  i++;
119
119
  }
@@ -128,7 +128,7 @@ const grammar = class InstructionMiniparserGrammar {
128
128
 
129
129
  // @Node
130
130
  Null(p) {
131
- p.eat(/null/y, KW, { path: 'value' });
131
+ p.eat('null', KW, { path: 'value' });
132
132
  }
133
133
  };
134
134
 
@@ -98,9 +98,9 @@ const grammar = class RegexMiniparserGrammar {
98
98
  // @Node
99
99
  Pattern(p) {
100
100
  p.eat('/', PN, { path: 'open', balanced: '/' });
101
- p.eatProduction('Alternatives', { path: '[alternatives]' });
101
+ p.eatProduction('Alternatives', { path: 'alternatives[]' });
102
102
  p.eat('/', PN, { path: 'close', balancer: true });
103
- p.eatProduction('Flags', { path: '[flags]' });
103
+ p.eatProduction('Flags', { path: 'flags[]' });
104
104
  }
105
105
 
106
106
  Flags(p) {
@@ -116,19 +116,18 @@ const grammar = class RegexMiniparserGrammar {
116
116
  // @Node
117
117
  Flag(p) {
118
118
  const flag = p.eatMatch(/[gimsuy]/y, KW, { path: 'value' });
119
-
120
119
  return { attrs: { kind: flagsReverse[flag] } };
121
120
  }
122
121
 
123
122
  Alternatives(p) {
124
123
  do {
125
124
  p.eatProduction('Alternative');
126
- } while (p.eatMatch('|', PN, { path: '[separators]' }));
125
+ } while (p.eatMatch('|', PN, { path: 'separators[]' }));
127
126
  }
128
127
 
129
128
  // @Node
130
129
  Alternative(p) {
131
- p.eatProduction('Elements', { path: '[elements]' });
130
+ p.eatProduction('Elements', { path: 'elements[]' });
132
131
  }
133
132
 
134
133
  Elements(p) {
@@ -163,14 +162,14 @@ const grammar = class RegexMiniparserGrammar {
163
162
  // @Node
164
163
  Group(p) {
165
164
  p.eat('(?:', PN, { path: 'open', balanced: ')' });
166
- p.eatProduction('Alternatives', { path: '[alternatives]' });
165
+ p.eatProduction('Alternatives', { path: 'alternatives[]' });
167
166
  p.eat(')', PN, { path: 'close', balancer: true });
168
167
  }
169
168
 
170
169
  // @Node
171
170
  CapturingGroup(p) {
172
171
  p.eat('(', PN, { path: 'open', balanced: ')' });
173
- p.eatProduction('Alternatives', { path: '[alternatives]' });
172
+ p.eatProduction('Alternatives', { path: 'alternatives[]' });
174
173
  p.eat(')', PN, { path: 'close', balancer: true });
175
174
  }
176
175
 
@@ -226,7 +225,7 @@ const grammar = class RegexMiniparserGrammar {
226
225
 
227
226
  let first = !negate;
228
227
  while (p.match(/./sy)) {
229
- p.eatProduction('CharacterClassElement', { path: '[elements]' }, { first });
228
+ p.eatProduction('CharacterClassElement', { path: 'elements[]' }, { first });
230
229
  first = false;
231
230
  }
232
231
 
@@ -54,7 +54,7 @@ const grammar = class SpamexMiniparserGrammar {
54
54
  let sp = p.eatMatchTrivia(_);
55
55
 
56
56
  if ((sp && p.match(/\w+/y)) || p.atExpression) {
57
- p.eatProduction('Attributes', { path: '[attributes]' });
57
+ p.eatProduction('Attributes', { path: 'attributes[]' });
58
58
  sp = p.eatMatchTrivia(_);
59
59
  }
60
60
 
@@ -75,7 +75,7 @@ const grammar = class SpamexMiniparserGrammar {
75
75
  }
76
76
 
77
77
  if (sp && p.match(/\w+/y)) {
78
- p.eatProduction('Attributes', { path: '[attributes]' });
78
+ p.eatProduction('Attributes', { path: 'attributes[]' });
79
79
  sp = p.eatMatchTrivia(_);
80
80
  }
81
81
 
package/lib/miniparser.js CHANGED
@@ -5,7 +5,7 @@ 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, parsePath, getPrototypeOf, buildNode } = require('./utils.js');
8
+ const { set, isRegex, isArray, getPrototypeOf, buildNode, parsePath } = require('./utils.js');
9
9
 
10
10
  class TemplateParser {
11
11
  constructor(rootLanguage, quasis, expressions) {
@@ -172,9 +172,8 @@ class TemplateParser {
172
172
  }
173
173
  } else if (parentPath?.node && (isNode || covers.has(type))) {
174
174
  const { properties, children } = parentPath.node;
175
- const { pathName } = parsePath(this.m.attrs.path);
176
175
 
177
- children.push({ ...ref(pathName), id });
176
+ children.push({ ...ref(this.m.attrs.path), id });
178
177
 
179
178
  set(properties, this.m.attrs.path, result);
180
179
  }
@@ -195,9 +194,7 @@ class TemplateParser {
195
194
  node.attributes = result.attrs;
196
195
  }
197
196
  if (parentPath?.node && !covers.has(type)) {
198
- const { pathName } = parsePath(this.m.attrs.path);
199
-
200
- parentPath.node.children.push(ref(pathName));
197
+ parentPath.node.children.push(ref(this.m.attrs.path));
201
198
 
202
199
  set(parentPath.node.properties, this.m.attrs.path, node);
203
200
  }
@@ -280,16 +277,16 @@ class TemplateParser {
280
277
  }
281
278
 
282
279
  const path = lastChild.value;
283
- const pathIsArray = isArray(properties[path]);
280
+ const { pathIsArray, pathName } = parsePath(path);
284
281
 
285
- this.held = pathIsArray ? arrayLast(properties[path]) : properties[path];
282
+ this.held = pathIsArray ? arrayLast(properties[pathName]) : properties[pathName];
286
283
 
287
284
  children.pop();
288
285
 
289
286
  if (pathIsArray) {
290
- properties[path].pop();
287
+ properties[pathName].pop();
291
288
  } else {
292
- properties[path] = null;
289
+ properties[pathName] = null;
293
290
  }
294
291
 
295
292
  return this.eval(this.buildId(id), attrs, props);
@@ -309,7 +306,7 @@ class TemplateParser {
309
306
 
310
307
  set(this.node.properties, attrs.path, buildNode(this.buildId(type), [lit(result)]));
311
308
 
312
- this.node.children.push(ref(parsePath(attrs.path).pathName));
309
+ this.node.children.push(ref(attrs.path));
313
310
 
314
311
  return result;
315
312
  }
@@ -337,7 +334,7 @@ class TemplateParser {
337
334
 
338
335
  set(this.node.properties, attrs.path, buildNode(this.buildId(type), [lit(result)]));
339
336
 
340
- this.node.children.push(ref(parsePath(attrs.path).pathName));
337
+ this.node.children.push(ref(attrs.path));
341
338
  }
342
339
  return result;
343
340
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bablr/boot",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Compile-time tools for bootstrapping BABLR VM",
5
5
  "engines": {
6
6
  "node": ">=12.0.0"
@@ -10,14 +10,15 @@
10
10
  "./shorthand.macro": "./shorthand.macro.js"
11
11
  },
12
12
  "files": [
13
- "lib/**/*.js"
13
+ "lib/**/*.js",
14
+ "shorthand.macro.js"
14
15
  ],
15
16
  "sideEffects": false,
16
17
  "dependencies": {
17
18
  "@babel/helper-module-imports": "^7.22.15",
18
19
  "@babel/template": "^7.22.15",
19
20
  "@babel/types": "7.23.0",
20
- "@bablr/boot-helpers": "0.1.0",
21
+ "@bablr/boot-helpers": "0.1.1",
21
22
  "escape-string-regexp": "4.0.0",
22
23
  "iter-tools-es": "^7.5.3"
23
24
  },
@@ -0,0 +1,224 @@
1
+ const t = require('@babel/types');
2
+ const { expression } = require('@babel/template');
3
+ const isObject = require('iter-tools-es/methods/is-object');
4
+ const isUndefined = require('iter-tools-es/methods/is-undefined');
5
+ const isNull = require('iter-tools-es/methods/is-null');
6
+ const isString = require('iter-tools-es/methods/is-string');
7
+ const concat = require('iter-tools-es/methods/concat');
8
+ const { createMacro } = require('babel-plugin-macros');
9
+ const { TemplateParser } = require('./lib/miniparser.js');
10
+ const i = require('./lib/languages/instruction.js');
11
+ const re = require('./lib/languages/regex.js');
12
+ const spam = require('./lib/languages/spamex.js');
13
+ const str = require('./lib/languages/string.js');
14
+ const cstml = require('./lib/languages/cstml.js');
15
+ const { addNamespace, addNamed } = require('@babel/helper-module-imports');
16
+ const { PathResolver } = require('@bablr/boot-helpers/path');
17
+
18
+ const { hasOwn } = Object;
19
+ const { isArray } = Array;
20
+ const isNumber = (v) => typeof v === 'number';
21
+ const isBoolean = (v) => typeof v === 'boolean';
22
+
23
+ const isPlainObject = (v) => isObject(v) && !isArray(v);
24
+
25
+ const set = (obj, path, value, pathIsArray) => {
26
+ if (pathIsArray) {
27
+ if (!obj[path]) {
28
+ obj[path] = [];
29
+ }
30
+
31
+ if (!isArray(obj[path])) throw new Error('bad array value');
32
+
33
+ obj[path].push(value);
34
+ } else {
35
+ if (hasOwn(obj, path)) {
36
+ throw new Error('duplicate child name');
37
+ }
38
+ obj[path] = value;
39
+ }
40
+ };
41
+
42
+ const getASTValue = (v, exprs, bindings) => {
43
+ return isNull(v)
44
+ ? t.nullLiteral()
45
+ : isUndefined(v)
46
+ ? t.identifier('undefined')
47
+ : isString(v)
48
+ ? t.stringLiteral(v)
49
+ : isNumber(v)
50
+ ? t.numericLiteral(v)
51
+ : isBoolean(v)
52
+ ? t.booleanLiteral(v)
53
+ : isArray(v)
54
+ ? t.arrayExpression(v.map((v) => getASTValue(v, exprs, bindings)))
55
+ : isPlainObject(v) && !v.language
56
+ ? t.objectExpression(
57
+ Object.entries(v).map(([k, v]) =>
58
+ t.objectProperty(t.identifier(k), getASTValue(v, exprs, bindings)),
59
+ ),
60
+ )
61
+ : generateNode(v, exprs, bindings);
62
+ };
63
+
64
+ const generateNodeChild = (child, bindings) => {
65
+ if (child.type === 'Reference') {
66
+ return expression(`%%t%%.ref\`${child.value}\``)({ t: bindings.t });
67
+ } else if (child.type === 'Literal') {
68
+ return expression(`%%t%%.lit\`${child.value.replace(/\\/g, '\\\\')}\``)({ t: bindings.t });
69
+ } else if (child.type === 'Trivia') {
70
+ return expression(`%%t%%.trivia\` \``)({ t: bindings.t });
71
+ } else if (child.type === 'Escape') {
72
+ return expression(`%%t%%.esc(%%cooked%%, %%raw%%)`)({
73
+ t: bindings.t,
74
+ cooked: child.cooked,
75
+ raw: child.raw,
76
+ });
77
+ } else {
78
+ throw new Error(`Unknown child type ${child.type}`);
79
+ }
80
+ };
81
+
82
+ const generateNode = (node, exprs, bindings) => {
83
+ const resolver = new PathResolver(node);
84
+ const { children, type, language, attributes, properties } = node;
85
+ const properties_ = {};
86
+ const children_ = [];
87
+
88
+ if (!children) {
89
+ throw new Error();
90
+ }
91
+
92
+ for (const child of children) {
93
+ children_.push(generateNodeChild(child, bindings));
94
+
95
+ if (child.type === 'Reference') {
96
+ const path = child.value;
97
+ const resolved = resolver.get(path);
98
+ const pathIsArray = isArray(properties[path]);
99
+
100
+ let embedded = resolved;
101
+ if (resolved) {
102
+ embedded = generateNode(resolved, exprs, bindings);
103
+ } else {
104
+ embedded = exprs.pop();
105
+ const { interpolateArray, interpolateString } = bindings;
106
+
107
+ if (pathIsArray) {
108
+ embedded = expression('[...%%interpolateArray%%(%%embedded%%)]')({
109
+ interpolateArray,
110
+ embedded,
111
+ }).elements[0];
112
+ } else if (language === 'String' && type === 'String') {
113
+ embedded = expression('%%interpolateString%%(%%embedded%%)')({
114
+ interpolateString,
115
+ embedded,
116
+ });
117
+ }
118
+ }
119
+
120
+ set(properties_, path, embedded, pathIsArray);
121
+ }
122
+ }
123
+
124
+ return expression(
125
+ `%%t%%.node(%%language%%, %%type%%, %%children%%, %%properties%%, %%attributes%%)`,
126
+ )({
127
+ t: bindings.t,
128
+ language: t.stringLiteral(language),
129
+ type: t.stringLiteral(type),
130
+ children: t.arrayExpression(children_),
131
+ properties: t.objectExpression(
132
+ Object.entries(properties_).map(([key, value]) =>
133
+ t.objectProperty(t.identifier(key), isArray(value) ? t.arrayExpression(value) : value),
134
+ ),
135
+ ),
136
+ attributes: t.objectExpression(
137
+ Object.entries(attributes).map(([key, value]) =>
138
+ t.objectProperty(t.identifier(key), getASTValue(value, exprs, bindings)),
139
+ ),
140
+ ),
141
+ });
142
+ };
143
+
144
+ const languages = {
145
+ i,
146
+ re,
147
+ spam,
148
+ str,
149
+ cst: cstml,
150
+ };
151
+
152
+ const topTypes = {
153
+ i: 'Call',
154
+ re: 'Pattern',
155
+ spam: 'Matcher',
156
+ str: 'String',
157
+ cst: 'Node',
158
+ };
159
+
160
+ const getTopScope = (scope) => {
161
+ let top = scope;
162
+ while (top.parent) top = top.parent;
163
+ return top;
164
+ };
165
+
166
+ const shorthandMacro = ({ references }) => {
167
+ const bindings = {};
168
+
169
+ // decorator references
170
+
171
+ for (const ref of concat(
172
+ references.i,
173
+ references.spam,
174
+ references.re,
175
+ references.str,
176
+ references.cst,
177
+ )) {
178
+ if (!bindings.t) {
179
+ bindings.t = addNamespace(getTopScope(ref.scope).path, '@bablr/boot-helpers/types', {
180
+ nameHint: 't',
181
+ });
182
+ }
183
+
184
+ if (!bindings.interpolateArray) {
185
+ bindings.interpolateArray = addNamed(
186
+ getTopScope(ref.scope).path,
187
+ 'interpolateArray',
188
+ '@bablr/boot-helpers/template',
189
+ );
190
+ }
191
+
192
+ if (!bindings.interpolateString) {
193
+ bindings.interpolateString = addNamed(
194
+ getTopScope(ref.scope).path,
195
+ 'interpolateString',
196
+ '@bablr/boot-helpers/template',
197
+ );
198
+ }
199
+
200
+ const taggedTemplate =
201
+ ref.parentPath.type === 'MemberExpression' ? ref.parentPath.parentPath : ref.parentPath;
202
+
203
+ const { quasis, expressions } = taggedTemplate.node.quasi;
204
+
205
+ const tagName = ref.node.name;
206
+ const language = languages[tagName];
207
+ const type =
208
+ ref.parentPath.type === 'MemberExpression'
209
+ ? ref.parentPath.node.property.name
210
+ : topTypes[tagName];
211
+
212
+ if (!language) throw new Error();
213
+
214
+ const ast = new TemplateParser(
215
+ language,
216
+ quasis.map((q) => q.value.raw),
217
+ expressions.map(() => null),
218
+ ).eval({ language: language.name, type });
219
+
220
+ taggedTemplate.replaceWith(generateNode(ast, expressions.reverse(), bindings));
221
+ }
222
+ };
223
+
224
+ module.exports = createMacro(shorthandMacro);