@bablr/boot 0.5.0 → 0.6.1

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),
@@ -202,7 +220,7 @@ const getAgASTValue = (language, miniNode) => {
202
220
  const str = buildTag(cstml, 'String');
203
221
  const num = buildTag(cstml, 'Integer');
204
222
  const cst = buildTag(cstml, 'Node');
205
- const spam = buildTag(spamex, 'Pattern');
223
+ const spam = buildTag(spamex, 'Matcher');
206
224
  const re = buildTag(regex, 'Pattern');
207
225
  const i = buildTag(instruction, 'Call');
208
226
 
@@ -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
 
@@ -67,9 +66,12 @@ const covers = buildCovers({
67
66
  'NullTag',
68
67
  'GapTag',
69
68
  'Node',
69
+ 'Fragment',
70
70
  'IdentifierPath',
71
71
  'OpenNodeTag',
72
72
  'CloseNodeTag',
73
+ 'OpenFragmentTag',
74
+ 'CloseFragmentTag',
73
75
  'Tag',
74
76
  'Number',
75
77
  'Digit',
@@ -93,7 +95,7 @@ const grammar = class CSTMLMiniparserGrammar {
93
95
  Document(p) {
94
96
  p.eatProduction('DoctypeTag', { path: 'doctype' });
95
97
  p.eatMatchTrivia(_);
96
- p.eatProduction('Node', { path: 'tree' });
98
+ p.eatProduction('Fragment', { path: 'tree' });
97
99
  }
98
100
 
99
101
  // @Node
@@ -105,7 +107,7 @@ const grammar = class CSTMLMiniparserGrammar {
105
107
 
106
108
  let sp = p.eatMatchTrivia(_);
107
109
 
108
- if ((sp && p.match(/[a-zA-Z]+/y)) || p.atExpression) {
110
+ if ((sp && p.match(/!?[a-zA-Z]/y)) || p.atExpression) {
109
111
  p.eatProduction('Attributes');
110
112
  sp = p.eatMatchTrivia(_);
111
113
  }
@@ -156,7 +158,7 @@ const grammar = class CSTMLMiniparserGrammar {
156
158
  } else {
157
159
  if (p.match(/<\*?#/y)) {
158
160
  p.eatProduction('Node');
159
- } else if (p.match(/[a-zA-Z]/y)) {
161
+ } else if (p.match(/[a-zA-Z]|\./y)) {
160
162
  p.eatProduction('Property');
161
163
  } else if (p.match(/['"]/y)) {
162
164
  p.eatProduction('LiteralTag');
@@ -164,6 +166,25 @@ const grammar = class CSTMLMiniparserGrammar {
164
166
  }
165
167
  }
166
168
 
169
+ // @Node
170
+ Fragment(p) {
171
+ let open = p.eatProduction('OpenFragmentTag', { path: 'open' });
172
+
173
+ p.eatMatchTrivia(_);
174
+
175
+ if (open.properties.flags?.token) {
176
+ p.eatProduction('NodeChild', { path: 'children[]' }, { token: true });
177
+ p.eatMatchTrivia(_);
178
+ }
179
+
180
+ while (!(p.match('</') || p.done)) {
181
+ p.eatProduction('NodeChild', { path: 'children[]' });
182
+ p.eatMatchTrivia(_);
183
+ }
184
+
185
+ p.eatProduction('CloseFragmentTag', { path: 'close' });
186
+ }
187
+
167
188
  // @Node
168
189
  Property(p) {
169
190
  p.eatProduction('ReferenceTag', { path: 'reference' });
@@ -184,10 +205,10 @@ const grammar = class CSTMLMiniparserGrammar {
184
205
  // @Node
185
206
  Flags(p) {
186
207
  let tr = p.eatMatch('#', PN, { path: 'triviaToken' });
187
- p.eatMatch('~', PN, { path: 'intrinsicToken' });
188
208
  p.eatMatch('*', PN, { path: 'tokenToken' });
189
209
  let esc = p.eatMatch('@', PN, { path: 'escapeToken' });
190
210
  let exp = p.eatMatch('+', PN, { path: 'expressionToken' });
211
+ p.eatMatch('$', PN, { path: 'hasGapToken' });
191
212
 
192
213
  if ((tr && esc) || (exp && (tr || esc))) throw new Error();
193
214
  }
@@ -210,12 +231,18 @@ const grammar = class CSTMLMiniparserGrammar {
210
231
 
211
232
  let sp = p.eatMatchTrivia(_);
212
233
 
213
- if (flags.properties.intrinsic && sp && (p.match(/['"/]/y) || p.atExpression)) {
214
- p.eatProduction('String', { path: 'intrinsicValue' });
234
+ let iv;
235
+
236
+ if (sp && (p.match(/['"]/y) || p.atExpression)) {
237
+ iv = p.eatProduction('String', { path: 'intrinsicValue' });
215
238
 
216
239
  sp = p.eatMatchTrivia(_);
217
240
  }
218
241
 
242
+ if (!flags.properties.tokenToken && iv) {
243
+ throw new Error();
244
+ }
245
+
219
246
  if ((sp && p.match(/[a-zA-Z]+/y)) || p.atExpression) {
220
247
  p.eatProduction('Attributes');
221
248
  sp = p.eatMatchTrivia(_);
@@ -232,16 +259,37 @@ const grammar = class CSTMLMiniparserGrammar {
232
259
  p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
233
260
  }
234
261
 
262
+ // @Node
263
+ OpenFragmentTag(p) {
264
+ p.eat('<', PN, { path: 'openToken', startSpan: 'Tag', balanced: '>' });
265
+
266
+ let flags = null;
267
+ if (!p.atExpression) {
268
+ flags = p.eatProduction('Flags', { path: 'flags' });
269
+ }
270
+
271
+ if (p.done) {
272
+ p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
273
+ return;
274
+ }
275
+ }
276
+
277
+ // @Node
278
+ CloseFragmentTag(p) {
279
+ p.eat('</', PN, { path: 'openToken', startSpan: 'Tag', balanced: '>' });
280
+ p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
281
+ }
282
+
235
283
  // @Fragment
236
284
  Attributes(p) {
237
285
  let sp = true;
238
- while (sp && (p.match(/[a-zA-Z]+/y) || p.atExpression)) {
286
+ while (sp && (p.match(/!?[a-zA-Z]/y) || p.atExpression)) {
239
287
  if (p.atExpression) {
240
288
  p.eatProduction('Attributes'); // ??
241
289
  } else {
242
290
  p.eatProduction('Attribute', { path: 'attributes[]' });
243
291
  }
244
- if (p.match(/\s+[a-zA-Z]/y) || (p.match(/\s+$/y) && !p.quasisDone)) {
292
+ if (p.match(/\s+!?[a-zA-Z]/y) || (p.match(/\s+$/y) && !p.quasisDone)) {
245
293
  sp = p.eatMatchTrivia(_);
246
294
  } else {
247
295
  sp = false;
@@ -251,7 +299,7 @@ const grammar = class CSTMLMiniparserGrammar {
251
299
 
252
300
  // @Cover
253
301
  Attribute(p) {
254
- if (p.match(/[a-zA-Z][[a-zA-Z]-_]*\s*=/y)) {
302
+ if (p.match(/[a-zA-Z][a-zA-Z-_]*\s*=/y)) {
255
303
  p.eatProduction('MappingAttribute');
256
304
  } else {
257
305
  p.eatProduction('BooleanAttribute');
@@ -260,13 +308,13 @@ const grammar = class CSTMLMiniparserGrammar {
260
308
 
261
309
  // @Node
262
310
  BooleanAttribute(p) {
263
- p.eat('!', KW, { path: 'negateToken' });
264
- p.eat(/[a-zA-Z]+/y, ID, { path: 'key' });
311
+ p.eatMatch('!', KW, { path: 'negateToken' });
312
+ p.eat(/[a-zA-Z][a-zA-Z-_]*/y, ID, { path: 'key' });
265
313
  }
266
314
 
267
315
  // @Node
268
316
  MappingAttribute(p) {
269
- p.eat(/[a-zA-Z]+/y, LIT, { path: 'key' });
317
+ p.eat(/[a-zA-Z][a-zA-Z-_]*/y, ID, { path: 'key' });
270
318
  p.eatMatchTrivia(_);
271
319
  p.eat('=', PN, { path: 'mapToken' });
272
320
  p.eatMatchTrivia(_);
@@ -283,7 +331,7 @@ const grammar = class CSTMLMiniparserGrammar {
283
331
  }
284
332
 
285
333
  TagType(p) {
286
- if (p.match(/[[a-zA-Z].]+:/y)) {
334
+ if (p.match(/['"]|[a-zA-Z\.]+:/y)) {
287
335
  p.eatProduction('LanguageReference', { path: 'language' });
288
336
  p.eat(':', PN, { path: 'namespaceSeparatorToken' });
289
337
  p.eatProduction('Identifier', { path: 'type' });
@@ -324,7 +372,11 @@ const grammar = class CSTMLMiniparserGrammar {
324
372
 
325
373
  // @Node
326
374
  ReferenceTag(p) {
327
- p.eatProduction('Identifier', { path: 'name' });
375
+ if (p.match('.')) {
376
+ p.eat('.', PN, { path: 'name' });
377
+ } else {
378
+ p.eatProduction('Identifier', { path: 'name' });
379
+ }
328
380
  p.eatMatchTrivia(_);
329
381
  p.eatMatch('[]', PN, { path: 'arrayToken' });
330
382
  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
  }
@@ -14,18 +14,22 @@ const canonicalURL = 'https://bablr.org/languages/core/en/spamex';
14
14
  const dependencies = { CSTML, Regex };
15
15
 
16
16
  const covers = buildCovers({
17
- [sym.node]: ['Attribute', 'Identifier', 'Pattern', 'Matcher', 'Literal', 'CSTML:Flags'],
17
+ [sym.node]: [
18
+ 'Attribute',
19
+ 'Identifier',
20
+ 'Matcher',
21
+ 'OpenNodeMatcher',
22
+ 'CloseNodeMatcher',
23
+ 'Literal',
24
+ 'CSTML:Flags',
25
+ ],
18
26
  Attribute: ['MappingAttribute', 'BooleanAttribute'],
19
27
  AttributeValue: ['CSTML:String', 'CSTML:Number'],
20
- Matcher: ['NodeMatcher', 'OpenNodeMatcher', 'CloseNodeMatcher', 'StringMatcher'],
28
+ Matcher: ['NodeMatcher', 'StringMatcher'],
21
29
  StringMatcher: ['CSTML:String', 'Regex:Pattern'],
22
30
  });
23
31
 
24
32
  const grammar = class SpamexMiniparserGrammar {
25
- Pattern(p) {
26
- p.eatProduction('Matcher', { path: 'matcher' });
27
- }
28
-
29
33
  // @Cover
30
34
  Matcher(p) {
31
35
  if (p.match(/<[^!/]/y)) {
@@ -67,10 +71,8 @@ const grammar = class SpamexMiniparserGrammar {
67
71
  p.eatProduction('CSTML:Flags', { path: 'flags' });
68
72
  }
69
73
 
70
- if (p.match(/[a-zA-Z]+:/y)) {
71
- p.eat(/[a-zA-Z]+/y, ID, { path: 'language' });
72
- p.eat(':', PN, { path: 'namespaceSeparatorToken' });
73
- p.eat(/[a-zA-Z]+/y, ID, { path: 'type' });
74
+ if (p.match(/['"]|[a-zA-Z]+:/y)) {
75
+ p.eatProduction('CSTML:TagType', { path: 'type' });
74
76
  } else if (p.match('?')) {
75
77
  p.eat('?', PN, { path: 'type' });
76
78
  } else if (p.match(' ')) {
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.1",
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
  }