@bablr/boot 0.1.9 → 0.2.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.
@@ -1,118 +1,198 @@
1
- const Regex = require('./regex.js');
2
- const StringLanguage = require('./string.js');
3
- const Number = require('./number.js');
1
+ const objectEntries = require('iter-tools-es/methods/object-entries');
2
+
4
3
  const { buildCovers } = require('../utils.js');
5
4
  const sym = require('../symbols.js');
6
5
 
7
6
  const _ = /\s+/y;
8
7
  const PN = 'Punctuator';
9
8
  const ID = 'Identifier';
9
+ const KW = 'Keyword';
10
10
  const LIT = 'Literal';
11
11
 
12
12
  const name = 'CSTML';
13
13
 
14
- const dependencies = { Regex, String: StringLanguage, Number };
14
+ const canonicalURL = 'https://bablr.org/languages/core/cstml';
15
+
16
+ const dependencies = {};
17
+
18
+ const escapables = new Map(
19
+ objectEntries({
20
+ n: '\n',
21
+ r: '\r',
22
+ t: '\t',
23
+ 0: '\0',
24
+ }),
25
+ );
26
+
27
+ const cookEscape = (escape, span) => {
28
+ let hexMatch;
29
+
30
+ if (!escape.startsWith('\\')) {
31
+ throw new Error('string escape must start with \\');
32
+ }
33
+
34
+ if ((hexMatch = /\\u([0-9a-f]{4})/iy.exec(escape))) {
35
+ //continue
36
+ } else if ((hexMatch = /\\u{([0-9a-f]+)}/iy.exec(escape))) {
37
+ //continue
38
+ }
39
+
40
+ if (hexMatch) {
41
+ return String.fromCodePoint(parseInt(hexMatch[1], 16));
42
+ }
43
+
44
+ const litPattern = span === 'Single' ? /\\([\\gnrt0'])/y : /\\([\\gnrt0"])/y;
45
+ const litMatch = litPattern.exec(escape);
46
+
47
+ if (litMatch) {
48
+ if (litMatch[1] === 'g') {
49
+ return null;
50
+ } else {
51
+ return escapables.get(litMatch[1]) || litMatch[1];
52
+ }
53
+ }
54
+
55
+ throw new Error('unable to cook string escape');
56
+ };
15
57
 
16
58
  const covers = buildCovers({
17
59
  [sym.node]: [
60
+ 'Document',
61
+ 'DocumentVersion',
62
+ 'DoctypeTag',
18
63
  'Attribute',
19
64
  'Property',
65
+ 'Reference',
20
66
  'TagType',
67
+ 'Null',
68
+ 'Gap',
21
69
  'Node',
22
- 'OpenFragmentNodeTag',
23
- 'TerminalNodeShorthandTag',
70
+ 'IdentifierPath',
71
+ 'OpenFragmentTag',
24
72
  'OpenNodeTag',
25
73
  'CloseNodeTag',
74
+ 'CloseFragmentTag',
26
75
  'Terminal',
76
+ 'Number',
77
+ 'Digit',
78
+ 'String',
79
+ 'Content',
80
+ 'UnsignedInteger',
27
81
  ],
28
82
  [sym.fragment]: ['Attributes', 'Fragment'],
29
83
  Attribute: ['MappingAttribute', 'BooleanAttribute'],
30
- AttributeValue: ['String:String', 'Number:Number'],
84
+ AttributeValue: ['String', 'Number'],
31
85
  TagType: ['Identifier', 'GlobalIdentifier'],
32
86
  Terminal: ['Literal', 'Trivia', 'Escape'],
87
+ PropertyValue: ['Gap', 'Node', 'Null'],
33
88
  EmbeddedTerminal: ['Literal', 'Escape'],
89
+ Number: ['Integer', 'Infinity'],
34
90
  });
35
91
 
36
92
  const grammar = class CSTMLMiniparserGrammar {
37
93
  Fragment(p) {
38
94
  p.eatMatchTrivia(_);
39
- p.eatProduction('OpenFragmentNodeTag', { path: 'open' });
40
- p.eatProduction('FragmentChildren');
41
- p.eatProduction('CloseNodeTag', { path: 'close' });
42
- p.eatMatchTrivia(_);
43
- }
44
-
45
- FragmentChildren(p) {
95
+ p.eatProduction('OpenFragmentTag', { path: 'open' });
46
96
  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
- }
97
+ while (p.match(/<[^/]/y) || p.atExpression) {
98
+ p.eatProduction('Node', { path: 'root' });
53
99
  p.eatMatchTrivia(_);
54
100
  }
101
+ p.eatProduction('CloseFragmentTag', { path: 'close' });
102
+ p.eatMatchTrivia(_);
55
103
  }
56
104
 
57
105
  // @Node
58
- Node(p) {
59
- const openType = p.match('<|') ? 'TerminalNodeShorthandTag' : 'OpenNodeTag';
60
- const childrenType = openType === 'OpenNodeTag' ? 'NodeChildren' : 'TerminalNodeChildren';
106
+ Document(p) {
107
+ p.eatProduction('DoctypeTag', { path: 'doctype' });
108
+ p.eatProduction('Fragment', { path: 'tree' });
109
+ }
61
110
 
62
- if (p.match('<>')) throw new Error('Fragment is not a node');
111
+ // @Node
112
+ DoctypeTag(p) {
113
+ p.eat('<!', PN, { path: 'open' });
114
+ p.eatProduction('UnsignedInteger', { path: 'version' });
115
+ p.eat(':', PN, { path: 'versionSeparator' });
116
+ p.eat('cstml', KW, { path: 'doctype' });
63
117
 
64
- const open = p.eatProduction(openType, { path: 'open' });
65
- if (!open.attributes.selfClosing) {
66
- p.eatProduction(childrenType);
67
- p.eatProduction('CloseNodeTag', { path: 'close' });
118
+ let sp = p.eatMatchTrivia(_);
119
+
120
+ if ((sp && p.match(/\w+/y)) || p.atExpression) {
121
+ p.eatProduction('Attributes');
122
+ sp = p.eatMatchTrivia(_);
68
123
  }
124
+
125
+ p.eat('>', PN, { path: 'close' });
126
+ }
127
+
128
+ // @Node
129
+ Null(p) {
130
+ p.eat('null', KW, { path: 'value' });
69
131
  }
70
132
 
71
- NodeChildren(p) {
72
- let properties = 0;
133
+ // @Node
134
+ Gap(p) {
135
+ p.eat('<//>', PN, { path: 'value' });
136
+ }
137
+
138
+ // @Node
139
+ Node(p) {
140
+ if (p.match('<>')) throw new Error('Fragment is not a node');
141
+
142
+ let open = p.eatProduction('OpenNodeTag', { path: 'open' });
73
143
 
74
144
  p.eatMatchTrivia(_);
75
- while (p.match(/#['"]|\w/y) || p.atExpression) {
76
- if (p.match(/#['"]/y)) {
77
- p.eatProduction('Trivia', { path: 'children[]' });
78
- } else {
79
- p.eatProduction('Property', { path: 'children[]' });
80
- properties++;
81
- }
145
+
146
+ if (open.properties.flags?.token) {
147
+ p.eatProduction('NodeChild', { path: 'children[]' }, { token: true });
82
148
  p.eatMatchTrivia(_);
149
+ } else {
150
+ while (!p.match('</')) {
151
+ p.eatProduction('NodeChild', { path: 'children[]' });
152
+ p.eatMatchTrivia(_);
153
+ }
83
154
  }
84
155
 
85
- if (!properties) throw new Error('Nodes must match text');
156
+ p.eatProduction('CloseNodeTag', { path: 'close' });
86
157
  }
87
158
 
88
- TerminalNodeChildren(p) {
89
- let properties = 0;
159
+ NodeChild(p, _, props) {
160
+ const { token } = props || {};
90
161
 
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++;
162
+ if (token) {
163
+ p.eatProduction('Literal');
164
+ } else {
165
+ if (p.match(/<\*?#/y)) {
166
+ p.eatProduction('Node');
167
+ } else if (p.match(/\w/y)) {
168
+ p.eatProduction('Property');
169
+ } else if (p.match(/['"]/y)) {
170
+ p.eatProduction('Literal');
98
171
  }
99
- p.eatMatchTrivia(_);
100
172
  }
101
-
102
- if (!properties) throw new Error('Nodes must match text');
103
173
  }
104
174
 
105
175
  // @Node
106
176
  Property(p) {
107
- p.eat(/\w+/y, ID, { path: 'key' });
177
+ p.eatProduction('Reference', { path: 'reference' });
108
178
  p.eatMatchTrivia(_);
109
179
  p.eat(':', PN, { path: 'mapOperator' });
110
180
  p.eatMatchTrivia(_);
111
- p.eatProduction('Node', { path: 'value' });
181
+ p.eatProduction('PropertyValue', { path: 'value' });
182
+ }
183
+
184
+ PropertyValue(p) {
185
+ if (p.match('null')) {
186
+ p.eatProduction('Null');
187
+ } else if (p.match('<//>')) {
188
+ p.eatProduction('Gap');
189
+ } else {
190
+ p.eatProduction('Node');
191
+ }
112
192
  }
113
193
 
114
194
  // @Node
115
- OpenFragmentNodeTag(p) {
195
+ OpenFragmentTag(p) {
116
196
  p.eat('<', PN, { path: 'open', startSpan: 'Tag', balanced: '>' });
117
197
  p.eat('>', PN, { path: 'close', endSpan: 'Tag', balancer: true });
118
198
  }
@@ -120,30 +200,22 @@ const grammar = class CSTMLMiniparserGrammar {
120
200
  // @Node
121
201
  OpenNodeTag(p) {
122
202
  p.eat('<', PN, { path: 'open', startSpan: 'Tag', balanced: '>' });
123
- p.eatProduction('TagType', { path: 'type' });
124
-
125
- let sp = p.eatMatchTrivia(_);
126
203
 
127
- if ((sp && p.match(/\w+/y)) || p.atExpression) {
128
- p.eatProduction('Attributes');
129
- sp = p.eatMatchTrivia(_);
130
- }
204
+ let tr = p.eatMatch('#', PN, { path: 'triviaFlag' });
205
+ let tok = p.eatMatch('*', PN, { path: 'tokenFlag' });
206
+ let esc = p.eatMatch('@', PN, { path: 'escapeFlag' });
207
+ let exp = p.eatMatch('+', PN, { path: 'expressionFlag' });
131
208
 
132
- p.eatMatchTrivia(_);
133
- p.eat('>', PN, { path: 'close', endSpan: 'Tag', balancer: true });
134
- }
209
+ if ((tr && esc) || (exp && (tr || esc))) throw new Error();
135
210
 
136
- // @Node
137
- TerminalNodeShorthandTag(p) {
138
- p.eat('<|', PN, { path: 'open', startSpan: 'Tag', balanced: '>' });
139
- p.eatMatchTrivia(_);
140
211
  p.eatProduction('TagType', { path: 'type' });
141
212
 
142
213
  let sp = p.eatMatchTrivia(_);
143
214
 
144
- let value;
145
- if (sp && p.match(/['"/]/y)) {
146
- value = p.eatProduction('EmbeddedTerminal', { path: 'value' });
215
+ let iv;
216
+ if (tok && sp && (p.match(/['"/]/y) || p.atExpression)) {
217
+ iv = p.eatProduction('String', { path: 'intrinsicValue' });
218
+
147
219
  sp = p.eatMatchTrivia(_);
148
220
  }
149
221
 
@@ -153,7 +225,10 @@ const grammar = class CSTMLMiniparserGrammar {
153
225
  }
154
226
 
155
227
  p.eatMatchTrivia(_);
156
- p.eat('|>', PN, { path: 'close', endSpan: 'Tag', balancer: true });
228
+ if (iv) {
229
+ p.eat('/', PN, { path: 'selfClosingToken' });
230
+ }
231
+ p.eat('>', PN, { path: 'close', endSpan: 'Tag', balancer: true });
157
232
  }
158
233
 
159
234
  // @Node
@@ -162,6 +237,12 @@ const grammar = class CSTMLMiniparserGrammar {
162
237
  p.eat('>', PN, { path: 'close', endSpan: 'Tag', balancer: true });
163
238
  }
164
239
 
240
+ // @Node
241
+ CloseFragmentTag(p) {
242
+ p.eat('</', PN, { path: 'open', startSpan: 'Tag', balanced: '>' });
243
+ p.eat('>', PN, { path: 'close', endSpan: 'Tag', balancer: true });
244
+ }
245
+
165
246
  // @Fragment
166
247
  Attributes(p) {
167
248
  let sp = true;
@@ -181,7 +262,7 @@ const grammar = class CSTMLMiniparserGrammar {
181
262
 
182
263
  // @Cover
183
264
  Attribute(p) {
184
- if (p.match(/\w+\s*=/y)) {
265
+ if (p.match(/\w[\w-_]*\s*=/y)) {
185
266
  p.eatProduction('MappingAttribute');
186
267
  } else {
187
268
  p.eatProduction('BooleanAttribute');
@@ -190,6 +271,7 @@ const grammar = class CSTMLMiniparserGrammar {
190
271
 
191
272
  // @Node
192
273
  BooleanAttribute(p) {
274
+ p.eat('!', KW, { path: 'negated' });
193
275
  p.eat(/\w+/y, ID, { path: 'key' });
194
276
  }
195
277
 
@@ -205,31 +287,41 @@ const grammar = class CSTMLMiniparserGrammar {
205
287
  // @Cover
206
288
  AttributeValue(p) {
207
289
  if (p.match(/['"]/y)) {
208
- p.eatProduction('String:String');
209
- } else if (p.match(/-|\d/y)) {
210
- p.eatProduction('Number:Number');
290
+ p.eatProduction('String');
291
+ } else if (p.match(/[\d+-]/y)) {
292
+ p.eatProduction('Number');
211
293
  }
212
294
  }
213
295
 
214
- // @Cover
215
296
  TagType(p) {
216
- if (p.match(/\w+:/y)) {
217
- p.eatProduction('GlobalIdentifier');
297
+ if (p.match(/[\w.]+:/y)) {
298
+ p.eatProduction('LanguageReference', { path: 'language' });
299
+ p.eat(':', PN, { path: 'namespaceOperator' });
300
+ p.eatProduction('Identifier', { path: 'type' });
218
301
  } else {
219
- p.eat(/\w+/y, ID, { path: 'type' });
302
+ p.eatProduction('Identifier', { path: 'type' });
220
303
  }
221
304
  }
222
305
 
223
- // @Node
224
- GlobalIdentifier(p) {
225
- p.eat(/\w+/y, ID, { path: 'language' });
226
- p.eat(':', PN, { path: 'namespaceOperator' });
227
- p.eat(/\w+/y, ID, { path: 'type' });
306
+ LanguageReference(p) {
307
+ if (p.match(/['"]/y)) {
308
+ p.eatProduction('String');
309
+ } else {
310
+ p.eatProduction('IdentifierPath');
311
+ }
312
+ }
313
+
314
+ IdentifierPath(p) {
315
+ p.eatProduction('Identifier', { path: 'segments[]' });
316
+ while (p.match('.')) {
317
+ p.eat('.', PN, { path: 'separators[]' });
318
+ p.eatProduction('Identifier', { path: 'segments[]' });
319
+ }
228
320
  }
229
321
 
230
322
  // @Node
231
323
  Identifier(p) {
232
- p.eatLiteral(/\w+/y);
324
+ p.eatLiteral(/\w[\w-_]*/y);
233
325
  }
234
326
 
235
327
  // @Cover
@@ -245,6 +337,13 @@ const grammar = class CSTMLMiniparserGrammar {
245
337
  }
246
338
  }
247
339
 
340
+ // @Node
341
+ Reference(p) {
342
+ p.eatProduction('Identifier', { path: 'name' });
343
+ p.eatMatchTrivia(_);
344
+ p.eatMatch('[]', PN, { path: 'arrayOperator' });
345
+ }
346
+
248
347
  // @Cover
249
348
  EmbeddedTerminal(p) {
250
349
  if (p.match(/!['"]/y)) {
@@ -259,22 +358,91 @@ const grammar = class CSTMLMiniparserGrammar {
259
358
  // @Node
260
359
  Escape(p) {
261
360
  p.eat('!', PN, { path: 'escapeOperator' });
262
- p.eatProduction('String:String', { path: 'rawValue' });
361
+ p.eatProduction('String', { path: 'rawValue' });
263
362
  p.eatMatchTrivia(_);
264
363
  p.eat(':', PN, { path: 'rawOperator' });
265
- p.eatProduction('String:String', { path: 'value' });
364
+ p.eatProduction('String', { path: 'value' });
266
365
  }
267
366
 
268
367
  // @Node
269
368
  Trivia(p) {
270
369
  p.eat('#', PN, { path: 'trivializeOperator' });
271
- p.eatProduction('String:String', { path: 'value' });
370
+ p.eatProduction('String', { path: 'value' });
272
371
  }
273
372
 
274
373
  // @Node
275
374
  Literal(p) {
276
- p.eatProduction('String:String', { path: 'value' });
375
+ p.eatProduction('String', { path: 'value' });
376
+ }
377
+
378
+ Number(p) {
379
+ if (p.match(/-?\d/y)) {
380
+ p.eatProduction('Integer');
381
+ } else {
382
+ p.eatProduction('Infinity');
383
+ }
384
+ }
385
+
386
+ // @Node
387
+ Integer(p) {
388
+ p.eatMatch('-', 'Punctuator', { path: 'negative' });
389
+ p.eatProduction('Digits', { path: 'digits[]' });
390
+ }
391
+
392
+ // @Node
393
+ UnsignedInteger(p) {
394
+ p.eatProduction('Digits', { path: 'digits[]' });
395
+ }
396
+
397
+ // @Node
398
+ Infinity(p) {
399
+ p.eatMatch(/[+-]/, 'Punctuator', { path: 'sign' });
400
+ p.eat('Infinity', 'Keyword', { path: 'value' });
401
+ }
402
+
403
+ Digits(p) {
404
+ while (p.match(/\d/y)) {
405
+ p.eatProduction('Digit');
406
+ }
407
+ }
408
+
409
+ // @Node
410
+ Digit(p) {
411
+ p.eatLiteral(/\d/y);
412
+ }
413
+
414
+ // @Node
415
+ String(p) {
416
+ const q = p.match(/['"]/y) || '"';
417
+
418
+ const span = q === '"' ? 'Double' : 'Single';
419
+
420
+ p.eat(q, PN, { path: 'open', startSpan: span, balanced: q });
421
+ while (p.match(/./sy) || p.atExpression) {
422
+ p.eatProduction('Content', { path: 'content' });
423
+ }
424
+ p.eat(q, PN, { path: 'close', endSpan: span, balancer: true });
425
+ }
426
+
427
+ // @Node
428
+ Content(p) {
429
+ let esc, lit;
430
+ let i = 0;
431
+ do {
432
+ esc =
433
+ p.span.type === 'Single'
434
+ ? p.eatMatchEscape(/\\(u(\{\d{1,6}\}|\d{4})|[\\gnrt0'])/y)
435
+ : p.eatMatchEscape(/\\(u(\{\d{1,6}\}|\d{4})|[\\gnrt0"])/y);
436
+ lit =
437
+ p.span.type === 'Single'
438
+ ? p.eatMatchLiteral(/[^\r\n\0\\']+/y)
439
+ : p.eatMatchLiteral(/[^\r\n\0\\"]+/y);
440
+ i++;
441
+ } while (esc || lit);
442
+ if (i === 1 && !esc && !lit) {
443
+ throw new Error('Invalid string content');
444
+ }
277
445
  }
278
446
  };
279
447
 
280
- module.exports = { name, dependencies, covers, grammar };
448
+ module.exports = { name, canonicalURL, dependencies, covers, grammar, cookEscape, escapables };
@@ -1,6 +1,6 @@
1
1
  const Spamex = require('./spamex.js');
2
+ const CSTML = require('./cstml.js');
2
3
  const Regex = require('./regex.js');
3
- const StringLanguage = require('./string.js');
4
4
  const { node } = require('../symbols.js');
5
5
  const { buildCovers } = require('../utils.js');
6
6
 
@@ -12,7 +12,9 @@ const LIT = 'Literal';
12
12
 
13
13
  const name = 'Instruction';
14
14
 
15
- const dependencies = { Spamex, Regex, String: StringLanguage };
15
+ const canonicalURL = 'https://bablr.org/languages/core/bablr-vm-instruction';
16
+
17
+ const dependencies = { Spamex, CSTML, Regex };
16
18
 
17
19
  const covers = buildCovers({
18
20
  [node]: ['Call', 'Punctuator', 'Property', 'Expression'],
@@ -21,7 +23,8 @@ const covers = buildCovers({
21
23
  'Array',
22
24
  'Tuple',
23
25
  'Identifier',
24
- 'String:String',
26
+ 'CSTML:String',
27
+ 'CSTML:Gap',
25
28
  'Regex:Pattern',
26
29
  'Boolean',
27
30
  'Null',
@@ -47,7 +50,9 @@ const grammar = class InstructionMiniparserGrammar {
47
50
  } else if (p.match('(')) {
48
51
  p.eatProduction('Tuple');
49
52
  } else if (p.match(/['"]/y)) {
50
- p.eatProduction('String:String');
53
+ p.eatProduction('CSTML:String');
54
+ } else if (p.match('<//>')) {
55
+ p.eatProduction('CSTML:Gap');
51
56
  } else if (p.match('/')) {
52
57
  p.eatProduction('Regex:Pattern');
53
58
  } else if (p.match(/true|false/y)) {
@@ -133,4 +138,4 @@ const grammar = class InstructionMiniparserGrammar {
133
138
  }
134
139
  };
135
140
 
136
- module.exports = { name, dependencies, covers, grammar };
141
+ module.exports = { name, canonicalURL, dependencies, covers, grammar };