@bablr/boot 0.5.0 → 0.6.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
@@ -2,8 +2,11 @@ const sym = require('@bablr/boot-helpers/symbols');
2
2
  const { freeze, getPrototypeOf } = Object;
3
3
  const { isArray } = Array;
4
4
  const {
5
+ DoctypeTag,
5
6
  OpenNodeTag,
6
7
  CloseNodeTag,
8
+ OpenFragmentTag,
9
+ CloseFragmentTag,
7
10
  ReferenceTag,
8
11
  GapTag,
9
12
  LiteralTag,
@@ -66,6 +69,13 @@ const interpolateString = (value) => {
66
69
  return buildNode('String', 'Content', children);
67
70
  };
68
71
 
72
+ const buildDoctypeTag = (attributes) => {
73
+ return freeze({
74
+ type: DoctypeTag,
75
+ value: freeze({ doctype: 'cstml', version: 0, attributes }),
76
+ });
77
+ };
78
+
69
79
  const buildReferenceTag = (name, isArray) => {
70
80
  return freeze({ type: ReferenceTag, value: freeze({ name, isArray }) });
71
81
  };
@@ -82,6 +92,19 @@ const buildNodeCloseTag = (type) => {
82
92
  return freeze({ type: CloseNodeTag, value: freeze({ type }) });
83
93
  };
84
94
 
95
+ const buildFragmentOpenTag = (flags = {}) => {
96
+ return freeze({
97
+ type: OpenFragmentTag,
98
+ value: freeze({
99
+ flags: freeze(flags),
100
+ }),
101
+ });
102
+ };
103
+
104
+ const buildFragmentCloseTag = (type = null, language = null) => {
105
+ return freeze({ type: CloseFragmentTag, value: freeze({ language, type }) });
106
+ };
107
+
85
108
  const buildLiteralTag = (value) => {
86
109
  return freeze({ type: LiteralTag, value });
87
110
  };
@@ -421,10 +444,13 @@ module.exports = {
421
444
  buildSpace,
422
445
  buildIdentifier,
423
446
  buildAttribute,
447
+ buildDoctypeTag,
424
448
  buildReferenceTag,
425
449
  buildGapTag,
426
450
  buildNodeOpenTag,
427
451
  buildNodeCloseTag,
452
+ buildFragmentOpenTag,
453
+ buildFragmentCloseTag,
428
454
  buildLiteralTag,
429
455
  buildNode,
430
456
  buildSyntacticNode,
@@ -432,6 +458,8 @@ module.exports = {
432
458
  buildSyntacticEscapeNode,
433
459
  buildTriviaNode,
434
460
  buildGapNode,
461
+ nodeFlags,
462
+
435
463
  ref,
436
464
  lit,
437
465
  gap,
package/lib/index.js CHANGED
@@ -5,11 +5,16 @@ const regex = require('./languages/regex.js');
5
5
  const instruction = require('./languages/instruction.js');
6
6
  const {
7
7
  buildLiteralTag,
8
+ buildDoctypeTag,
8
9
  buildNodeOpenTag,
9
10
  buildNodeCloseTag,
11
+ buildFragmentOpenTag,
12
+ buildFragmentCloseTag,
10
13
  buildTriviaNode,
11
14
  buildGapNode,
12
15
  buildSyntacticEscapeNode,
16
+ nodeFlags,
17
+ buildReferenceTag,
13
18
  } = require('./builders.js');
14
19
  const { TemplateParser } = require('./miniparser.js');
15
20
  const { Resolver } = require('./print.js');
@@ -62,7 +67,7 @@ const add = (obj, path, value) => {
62
67
 
63
68
  const buildTag = (language, defaultType) => {
64
69
  const defaultTag = (quasis, ...exprs) => {
65
- return getAgASTValue(
70
+ return getAgASTTree(
66
71
  language,
67
72
  new TemplateParser(language, quasis.raw, exprs).eval({
68
73
  language: language.name,
@@ -78,7 +83,7 @@ const buildTag = (language, defaultType) => {
78
83
 
79
84
  get(_, type) {
80
85
  return (quasis, ...exprs) => {
81
- return getAgASTValue(
86
+ return getAgASTTree(
82
87
  language,
83
88
  new TemplateParser(language, quasis.raw, exprs).eval({
84
89
  language: language.name,
@@ -97,6 +102,23 @@ const parse = (language, type, sourceText) => {
97
102
  });
98
103
  };
99
104
 
105
+ const getAgASTTree = (language, miniNode) => {
106
+ const attributes = { 'bablr-language': language.canonicalURL };
107
+ return {
108
+ flags: nodeFlags,
109
+ language: language.canonicalURL,
110
+ type: null,
111
+ children: [
112
+ buildDoctypeTag(attributes),
113
+ buildFragmentOpenTag(nodeFlags, null, null, null),
114
+ buildReferenceTag('.'),
115
+ buildFragmentCloseTag(),
116
+ ],
117
+ properties: { ['.']: getAgASTValue(language, miniNode) },
118
+ attributes,
119
+ };
120
+ };
121
+
100
122
  const getAgASTValue = (language, miniNode) => {
101
123
  if (!miniNode) return miniNode;
102
124
 
@@ -109,7 +131,7 @@ const getAgASTValue = (language, miniNode) => {
109
131
  escape: !!miniNode.flags?.escape,
110
132
  trivia: !!miniNode.flags?.trivia,
111
133
  token: false,
112
- intrinsic: false,
134
+ hasGap: false,
113
135
  };
114
136
  const properties = {};
115
137
  let children = [];
@@ -136,10 +158,6 @@ const getAgASTValue = (language, miniNode) => {
136
158
  flags.token = true;
137
159
  }
138
160
 
139
- if (type === 'Punctuator' || type === 'Keyword') {
140
- flags.intrinsic = true;
141
- }
142
-
143
161
  children = btree.push(
144
162
  children,
145
163
  buildNodeOpenTag(flags, resolvedLanguage.canonicalURL, type, attributes),
@@ -7,7 +7,6 @@ const _ = /\s+/y;
7
7
  const PN = 'Punctuator';
8
8
  const ID = 'Identifier';
9
9
  const KW = 'Keyword';
10
- const LIT = 'Literal';
11
10
 
12
11
  const name = 'CSTML';
13
12
 
@@ -105,7 +104,7 @@ const grammar = class CSTMLMiniparserGrammar {
105
104
 
106
105
  let sp = p.eatMatchTrivia(_);
107
106
 
108
- if ((sp && p.match(/[a-zA-Z]+/y)) || p.atExpression) {
107
+ if ((sp && p.match(/!?[a-zA-Z]/y)) || p.atExpression) {
109
108
  p.eatProduction('Attributes');
110
109
  sp = p.eatMatchTrivia(_);
111
110
  }
@@ -156,7 +155,7 @@ const grammar = class CSTMLMiniparserGrammar {
156
155
  } else {
157
156
  if (p.match(/<\*?#/y)) {
158
157
  p.eatProduction('Node');
159
- } else if (p.match(/[a-zA-Z]/y)) {
158
+ } else if (p.match(/[a-zA-Z]|\./y)) {
160
159
  p.eatProduction('Property');
161
160
  } else if (p.match(/['"]/y)) {
162
161
  p.eatProduction('LiteralTag');
@@ -184,10 +183,10 @@ const grammar = class CSTMLMiniparserGrammar {
184
183
  // @Node
185
184
  Flags(p) {
186
185
  let tr = p.eatMatch('#', PN, { path: 'triviaToken' });
187
- p.eatMatch('~', PN, { path: 'intrinsicToken' });
188
186
  p.eatMatch('*', PN, { path: 'tokenToken' });
189
187
  let esc = p.eatMatch('@', PN, { path: 'escapeToken' });
190
188
  let exp = p.eatMatch('+', PN, { path: 'expressionToken' });
189
+ p.eatMatch('$', PN, { path: 'hasGapToken' });
191
190
 
192
191
  if ((tr && esc) || (exp && (tr || esc))) throw new Error();
193
192
  }
@@ -210,12 +209,18 @@ const grammar = class CSTMLMiniparserGrammar {
210
209
 
211
210
  let sp = p.eatMatchTrivia(_);
212
211
 
213
- if (flags.properties.intrinsic && sp && (p.match(/['"/]/y) || p.atExpression)) {
214
- p.eatProduction('String', { path: 'intrinsicValue' });
212
+ let iv;
213
+
214
+ if (sp && (p.match(/['"]/y) || p.atExpression)) {
215
+ iv = p.eatProduction('String', { path: 'intrinsicValue' });
215
216
 
216
217
  sp = p.eatMatchTrivia(_);
217
218
  }
218
219
 
220
+ if (!flags.properties.tokenToken && iv) {
221
+ throw new Error();
222
+ }
223
+
219
224
  if ((sp && p.match(/[a-zA-Z]+/y)) || p.atExpression) {
220
225
  p.eatProduction('Attributes');
221
226
  sp = p.eatMatchTrivia(_);
@@ -235,13 +240,13 @@ const grammar = class CSTMLMiniparserGrammar {
235
240
  // @Fragment
236
241
  Attributes(p) {
237
242
  let sp = true;
238
- while (sp && (p.match(/[a-zA-Z]+/y) || p.atExpression)) {
243
+ while (sp && (p.match(/!?[a-zA-Z]/y) || p.atExpression)) {
239
244
  if (p.atExpression) {
240
245
  p.eatProduction('Attributes'); // ??
241
246
  } else {
242
247
  p.eatProduction('Attribute', { path: 'attributes[]' });
243
248
  }
244
- if (p.match(/\s+[a-zA-Z]/y) || (p.match(/\s+$/y) && !p.quasisDone)) {
249
+ if (p.match(/\s+!?[a-zA-Z]/y) || (p.match(/\s+$/y) && !p.quasisDone)) {
245
250
  sp = p.eatMatchTrivia(_);
246
251
  } else {
247
252
  sp = false;
@@ -251,7 +256,7 @@ const grammar = class CSTMLMiniparserGrammar {
251
256
 
252
257
  // @Cover
253
258
  Attribute(p) {
254
- if (p.match(/[a-zA-Z][[a-zA-Z]-_]*\s*=/y)) {
259
+ if (p.match(/[a-zA-Z][a-zA-Z-_]*\s*=/y)) {
255
260
  p.eatProduction('MappingAttribute');
256
261
  } else {
257
262
  p.eatProduction('BooleanAttribute');
@@ -260,13 +265,13 @@ const grammar = class CSTMLMiniparserGrammar {
260
265
 
261
266
  // @Node
262
267
  BooleanAttribute(p) {
263
- p.eat('!', KW, { path: 'negateToken' });
264
- p.eat(/[a-zA-Z]+/y, ID, { path: 'key' });
268
+ p.eatMatch('!', KW, { path: 'negateToken' });
269
+ p.eat(/[a-zA-Z][a-zA-Z-_]*/y, ID, { path: 'key' });
265
270
  }
266
271
 
267
272
  // @Node
268
273
  MappingAttribute(p) {
269
- p.eat(/[a-zA-Z]+/y, LIT, { path: 'key' });
274
+ p.eat(/[a-zA-Z][a-zA-Z-_]*/y, ID, { path: 'key' });
270
275
  p.eatMatchTrivia(_);
271
276
  p.eat('=', PN, { path: 'mapToken' });
272
277
  p.eatMatchTrivia(_);
@@ -283,7 +288,7 @@ const grammar = class CSTMLMiniparserGrammar {
283
288
  }
284
289
 
285
290
  TagType(p) {
286
- if (p.match(/[[a-zA-Z].]+:/y)) {
291
+ if (p.match(/[a-zA-Z\.]+:/y)) {
287
292
  p.eatProduction('LanguageReference', { path: 'language' });
288
293
  p.eat(':', PN, { path: 'namespaceSeparatorToken' });
289
294
  p.eatProduction('Identifier', { path: 'type' });
@@ -324,7 +329,11 @@ const grammar = class CSTMLMiniparserGrammar {
324
329
 
325
330
  // @Node
326
331
  ReferenceTag(p) {
327
- p.eatProduction('Identifier', { path: 'name' });
332
+ if (p.match('.')) {
333
+ p.eat('.', PN, { path: 'name' });
334
+ } else {
335
+ p.eatProduction('Identifier', { path: 'name' });
336
+ }
328
337
  p.eatMatchTrivia(_);
329
338
  p.eatMatch('[]', PN, { path: 'arrayToken' });
330
339
  p.eatMatchTrivia(_);
@@ -154,7 +154,7 @@ const grammar = class RegexMiniparserGrammar {
154
154
  }
155
155
 
156
156
  Elements(p) {
157
- while (p.match(/[^|]/y || p.atExpression)) {
157
+ while (p.match(/[^|]/y) || p.atExpression) {
158
158
  p.eatProduction('Element');
159
159
  }
160
160
  }
package/lib/miniparser.js CHANGED
@@ -178,9 +178,19 @@ class TemplateParser {
178
178
  const { properties, children } = parentPath.node;
179
179
  const path = parsePath(this.m.attrs.path);
180
180
 
181
- children.push({ ...ref(path), id });
181
+ if (isArray(result)) {
182
+ for (const value of result) {
183
+ children.push(ref(path));
182
184
 
183
- set(properties, path, result);
185
+ // TODO interpolate separators!
186
+
187
+ set(properties, path, value);
188
+ }
189
+ } else {
190
+ children.push(ref(path));
191
+
192
+ set(properties, path, result);
193
+ }
184
194
  }
185
195
  } else {
186
196
  if (isEmbedded) {
@@ -198,6 +208,7 @@ class TemplateParser {
198
208
  if (result?.attrs) {
199
209
  node.attributes = result.attrs;
200
210
  }
211
+
201
212
  if (parentPath?.node && !covers.has(type)) {
202
213
  const path = parsePath(this.m.attrs.path);
203
214
 
package/lib/print.js CHANGED
@@ -161,17 +161,11 @@ class Resolver {
161
161
  }
162
162
  }
163
163
 
164
- const buildDoctypeTag = () => {
165
- return freeze({ type: DoctypeTag, value: { doctype: 'cstml', version: 0 } });
166
- };
167
-
168
164
  function* streamFromTree(rootNode) {
169
165
  if (!rootNode || rootNode.type === GapTag) {
170
166
  return rootNode;
171
167
  }
172
168
 
173
- yield buildDoctypeTag(rootNode.attributes);
174
-
175
169
  let stack = emptyStack.push(rootNode);
176
170
  const resolver = new Resolver();
177
171
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bablr/boot",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Compile-time tools for bootstrapping BABLR VM",
5
5
  "engines": {
6
6
  "node": ">=12.0.0"
@@ -22,7 +22,7 @@
22
22
  "@babel/helper-module-imports": "^7.22.15",
23
23
  "@babel/template": "^7.22.15",
24
24
  "@babel/types": "7.23.0",
25
- "@bablr/boot-helpers": "0.3.0",
25
+ "@bablr/boot-helpers": "0.3.1",
26
26
  "@iter-tools/imm-stack": "1.1.0",
27
27
  "escape-string-regexp": "4.0.0",
28
28
  "iter-tools": "^7.5.3",
@@ -96,9 +96,7 @@ const generateBabelNodeChild = (child, exprs, bindings) => {
96
96
  };
97
97
 
98
98
  const getAgastNodeType = (flags) => {
99
- if (flags.intrinsic && flags.token) {
100
- return 's_i_node';
101
- } else if (flags.token && flags.trivia) {
99
+ if (flags.token && flags.trivia) {
102
100
  return 's_t_node';
103
101
  } else if (flags.token && flags.escape) {
104
102
  return 's_e_node';
@@ -140,7 +138,7 @@ const generateBabelNode = (node, exprs, bindings) => {
140
138
  } else {
141
139
  // gap
142
140
  const expr = exprs.pop();
143
- const { interpolateArray, interpolateArrayChildren, interpolateString } = bindings;
141
+ const { interpolateArray, interpolateFragmentChildren, interpolateString } = bindings;
144
142
 
145
143
  if (pathIsArray) {
146
144
  add(
@@ -155,17 +153,10 @@ const generateBabelNode = (node, exprs, bindings) => {
155
153
  children_ = btree.push(
156
154
  children_,
157
155
  t.spreadElement(
158
- expression('%%interpolateArrayChildren%%(%%expr%%, %%ref%%, %%sep%%)')({
159
- interpolateArrayChildren,
156
+ expression('%%interpolateFragmentChildren%%(%%expr%%, %%ref%%)')({
157
+ interpolateFragmentChildren,
160
158
  expr,
161
159
  ref: expression(`%%t%%.ref\`${printRef(child.value)}\``)({ t: bindings.t }),
162
-
163
- // Really really awful unsafe-as-heck hack, to be removed ASAP
164
- // Fixing this requires having interpolation happen during parsing
165
- // That way the grammar can deal with the separators!
166
- sep: expression(
167
- "%%t%%.embedded(%%t%%.t_node(%%l%%.Comment, null, [%%t%%.embedded(%%t%%.t_node('Space', 'Space', [%%t%%.lit(' ')]))]))",
168
- )({ t: bindings.t, l: bindings.l }),
169
160
  }),
170
161
  ),
171
162
  );
@@ -306,10 +297,10 @@ const shorthandMacro = ({ references }) => {
306
297
  );
307
298
  }
308
299
 
309
- if (!bindings.interpolateArrayChildren) {
310
- bindings.interpolateArrayChildren = addNamed(
300
+ if (!bindings.interpolateFragmentChildren) {
301
+ bindings.interpolateFragmentChildren = addNamed(
311
302
  getTopScope(ref.scope).path,
312
- 'interpolateArrayChildren',
303
+ 'interpolateFragmentChildren',
313
304
  '@bablr/agast-helpers/template',
314
305
  );
315
306
  }