@bablr/boot 0.6.3 → 0.7.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,133 +1,68 @@
1
- const Spamex = require('./spamex.js');
2
- const CSTML = require('./cstml.js');
3
- const Regex = require('./regex.js');
4
- const { node } = require('@bablr/boot-helpers/symbols');
5
- const { buildCovers } = require('../utils.js');
1
+ import * as sym from '@bablr/agast-helpers/symbols';
2
+ import * as Spamex from './spamex.js';
3
+ import * as CSTML from './cstml.js';
4
+ import * as Regex from './regex.js';
5
+ import * as BaseJSON from './json.js';
6
6
 
7
7
  const _ = /\s+/y;
8
8
  const PN = 'Punctuator';
9
9
  const ID = 'Identifier';
10
10
  const KW = 'Keyword';
11
- const LIT = 'Literal';
12
-
13
- const name = 'Instruction';
14
-
15
- const canonicalURL = 'https://bablr.org/languages/core/en/bablr-vm-instruction';
16
-
17
- const dependencies = { Spamex, CSTML, Regex };
18
-
19
- const covers = buildCovers({
20
- [node]: ['Call', 'Punctuator', 'Property', 'Expression'],
21
- Expression: [
22
- 'Object',
23
- 'Array',
24
- 'Tuple',
25
- 'Identifier',
26
- 'CSTML:String',
27
- 'CSTML:Gap',
28
- 'Regex:Pattern',
29
- 'Boolean',
30
- 'Null',
31
- 'Spamex:Matcher',
32
- 'Separator',
33
- ],
34
- });
35
11
 
36
- const grammar = class InstructionMiniparserGrammar {
37
- // @Node
38
- Call(p) {
39
- p.eat(/[a-zA-Z]+/y, ID, { path: 'verb' });
40
- p.eatMatchTrivia(_);
41
- p.eatProduction('Tuple', { path: 'arguments' });
42
- }
12
+ export const name = 'Instruction';
43
13
 
44
- // @Cover
14
+ export const canonicalURL = 'https://bablr.org/languages/core/en/bablr-vm-instruction';
15
+
16
+ class JSONGrammar extends BaseJSON.grammar {
45
17
  Expression(p) {
46
- if (p.match('[')) {
47
- p.eatProduction('Array');
48
- } else if (p.match('{')) {
49
- p.eatProduction('Object');
50
- } else if (p.match('(')) {
51
- p.eatProduction('Tuple');
52
- } else if (p.match(/['"]/y)) {
53
- p.eatProduction('CSTML:String');
54
- } else if (p.match('<//>')) {
55
- p.eatProduction('CSTML:Gap');
56
- } else if (p.match('/')) {
57
- p.eatProduction('Regex:Pattern');
58
- } else if (p.match(/true|false/y)) {
59
- p.eatProduction('Boolean');
60
- } else if (p.match('null')) {
61
- p.eatProduction('Null');
62
- } else if (p.match(/[a-zA-Z]/y)) {
63
- p.eat(/[a-zA-Z]+/y, ID, p.m.attributes);
64
- } else if (p.match('<')) {
65
- p.eatProduction('Spamex:Matcher');
18
+ if (p.match(/(m|re|t)['"`]/y)) {
19
+ grammar.prototype.Expression.call(this, p);
20
+ } else {
21
+ super.Expression(p);
66
22
  }
67
23
  }
24
+ }
68
25
 
69
- // @Node
70
- Object(p) {
71
- p.eat('{', PN, { path: 'openToken', balanced: '}' });
72
-
73
- p.eatMatchTrivia(_);
74
-
75
- let first = true;
76
- let sep;
77
- while (first || (sep && (p.match(/./y) || p.atExpression))) {
78
- p.eatProduction('Property', { path: 'properties[]' });
79
- if (p.match(/\s*,?/y)) {
80
- sep = p.eatProduction('Separator', { path: 'separators[]' });
81
- }
82
- first = false;
83
- }
84
-
85
- p.eatMatchTrivia(_);
86
-
87
- p.eat('}', PN, { path: 'closeToken', balancer: true });
88
- }
26
+ const JSON = {
27
+ ...BaseJSON,
28
+ grammar: JSONGrammar,
29
+ };
89
30
 
90
- // @Node
91
- Property(p) {
92
- p.eat(/[a-zA-Z]+/y, LIT, { path: 'key' });
93
- p.eatMatchTrivia(_);
94
- p.eat(':', PN, { path: 'mapToken' });
95
- p.eatMatchTrivia(_);
96
- p.eatProduction('Expression', { path: 'value' });
97
- }
31
+ export const dependencies = { Spamex, CSTML, Regex, JSON };
32
+
33
+ export const covers = new Map([
34
+ [sym.node, new Set(['Call', 'Punctuator'])],
35
+ [
36
+ 'Expression',
37
+ new Set([
38
+ 'Object',
39
+ 'Array',
40
+ 'Number',
41
+ 'JSON:String',
42
+ 'CSTML:GapTag',
43
+ 'RegexString',
44
+ 'SpamexString',
45
+ 'CSTMLString',
46
+ 'Boolean',
47
+ 'Null',
48
+ ]),
49
+ ],
50
+ ]);
98
51
 
52
+ export const grammar = class InstructionMiniparserGrammar {
99
53
  // @Node
100
- Array(p) {
101
- p.eat('[', PN, { path: 'openToken', balanced: ']' });
102
-
54
+ Call(p) {
55
+ p.eat(/[a-zA-Z]+/y, ID, { path: 'verb' });
103
56
  p.eatMatchTrivia(_);
104
57
 
105
- let first = true;
106
- let sep;
107
- while (first || (sep && (p.match(/./y) || p.atExpression))) {
108
- p.eatProduction('Expression', { path: 'elements[]' });
109
- if (p.match(/\s*,?/y)) {
110
- sep = p.eatProduction('Separator', { path: 'separators[]' });
111
- }
112
-
113
- first = false;
114
- }
115
-
116
- p.eat(']', PN, { path: 'closeToken', balancer: true });
117
- }
118
-
119
- // @Node
120
- Tuple(p) {
121
58
  p.eat('(', PN, { path: 'openToken', balanced: ')' });
122
59
 
123
- let sep;
60
+ let sep = p.eatMatchTrivia(_);
124
61
 
125
62
  let i = 0;
126
63
  while (i === 0 || (sep && (p.match(/./y) || p.atExpression))) {
127
64
  p.eatProduction('Expression', { path: 'values[]' });
128
- if (p.match(/\s*,?/y)) {
129
- sep = p.eatProduction('Separator', { path: 'separators[]' });
130
- }
65
+ sep = p.eatMatchTrivia(_);
131
66
  i++;
132
67
  }
133
68
 
@@ -135,21 +70,51 @@ const grammar = class InstructionMiniparserGrammar {
135
70
  }
136
71
 
137
72
  // @Node
138
- Boolean(p) {
139
- p.eat(/true|false/y, KW, { path: 'sigilToken' });
73
+ SpamexString(p) {
74
+ p.eat('m', KW, { path: 'sigilToken' });
75
+ let quot = p.match(/['"`]/);
76
+ p.eat(quot, PN, { path: 'openToken', balanced: quot });
77
+ p.eatProduction('Spamex:Matcher', { path: 'content' });
78
+
79
+ p.eat(quot, PN, { path: 'closeToken', balancer: true });
140
80
  }
141
81
 
142
- //@Node
143
- Separator(p) {
144
- p.eatMatchTrivia(_);
145
- p.eatMatch(/,/y, PN, { path: 'separators[]' });
146
- p.eatMatchTrivia(_);
82
+ // @Node
83
+ CSTMLString(p) {
84
+ p.eat('t', KW, { path: 'sigilToken' });
85
+ let quot = p.match(/['"`]/);
86
+ p.eat(quot, PN, { path: 'openToken', balanced: quot });
87
+ p.eatProduction('CSTML:Tag', { path: 'content' });
88
+
89
+ p.eat(quot, PN, { path: 'closeToken', balancer: true });
147
90
  }
148
91
 
149
92
  // @Node
150
- Null(p) {
151
- p.eat('null', KW, { path: 'sigilToken' });
93
+ RegexString(p) {
94
+ p.eat('re', KW, { path: 'sigilToken' });
95
+ let quot = p.match(/['"`]/);
96
+ p.eat(quot, PN, { path: 'openToken', balanced: quot });
97
+ p.eatProduction('Regex:Pattern', { path: 'content' });
98
+
99
+ p.eat(quot, PN, { path: 'closeToken', balancer: true });
152
100
  }
153
- };
154
101
 
155
- module.exports = { name, canonicalURL, dependencies, covers, grammar };
102
+ // @Cover
103
+ Expression(p) {
104
+ if (p.match('[')) {
105
+ p.eatProduction('JSON:Array');
106
+ } else if (p.match('{')) {
107
+ p.eatProduction('JSON:Object');
108
+ } else if (p.match(/['"]/y)) {
109
+ p.eatProduction('JSON:String');
110
+ } else if (p.match(/re['"`]/y)) {
111
+ p.eatProduction('RegexString');
112
+ } else if (p.match(/m['"`]/y)) {
113
+ p.eatProduction('SpamexString');
114
+ } else if (p.match(/true|false/y)) {
115
+ p.eatProduction('JSON:Boolean');
116
+ } else if (p.match('null')) {
117
+ p.eatProduction('JSON:Null');
118
+ }
119
+ }
120
+ };
@@ -0,0 +1,241 @@
1
+ import objectEntries from 'iter-tools/methods/object-entries';
2
+ import * as sym from '@bablr/agast-helpers/symbols';
3
+ import * as Spamex from './spamex.js';
4
+ import * as CSTML from './cstml.js';
5
+ import * as Regex from './regex.js';
6
+
7
+ const _ = /\s+/y;
8
+ const PN = 'Punctuator';
9
+ const KW = 'Keyword';
10
+ const ID = 'Identifier';
11
+
12
+ export const name = 'JSON';
13
+
14
+ export const canonicalURL = 'https://bablr.org/languages/core/en/cstml-json';
15
+
16
+ export const dependencies = { Spamex, CSTML, Regex };
17
+
18
+ export const covers = new Map([
19
+ [
20
+ sym.node,
21
+ new Set([
22
+ 'Punctuator',
23
+ 'Property',
24
+ 'Object',
25
+ 'Array',
26
+ 'Boolean',
27
+ 'Null',
28
+ 'Number',
29
+ 'UnsignedInteger',
30
+ 'Infinity',
31
+ 'NaN',
32
+ 'Digit',
33
+ 'Integer',
34
+ 'String',
35
+ 'StringContent',
36
+ ]),
37
+ ],
38
+ ['Expression', new Set(['Object', 'Array', 'Boolean', 'Null', 'Number', 'String'])],
39
+ ['Number', new Set(['Integer', 'Infinity', 'NaN'])],
40
+ ]);
41
+
42
+ export const escapables = new Map(
43
+ objectEntries({
44
+ n: '\n',
45
+ r: '\r',
46
+ t: '\t',
47
+ 0: '\0',
48
+ }),
49
+ );
50
+
51
+ export const cookEscape = (escape, span) => {
52
+ let hexMatch;
53
+
54
+ if (!escape.startsWith('\\')) {
55
+ throw new Error('string escape must start with \\');
56
+ }
57
+
58
+ if ((hexMatch = /\\u([0-9a-f]{4})/iy.exec(escape))) {
59
+ //continue
60
+ } else if ((hexMatch = /\\u{([0-9a-f]+)}/iy.exec(escape))) {
61
+ //continue
62
+ }
63
+
64
+ if (hexMatch) {
65
+ return String.fromCodePoint(parseInt(hexMatch[1], 16));
66
+ }
67
+
68
+ const litPattern = span === 'Single' ? /\\([\\gnrt0'])/y : /\\([\\gnrt0"])/y;
69
+ const litMatch = litPattern.exec(escape);
70
+
71
+ if (litMatch) {
72
+ if (litMatch[1] === 'g') {
73
+ return null;
74
+ } else {
75
+ return escapables.get(litMatch[1]) || litMatch[1];
76
+ }
77
+ }
78
+
79
+ throw new Error('unable to cook string escape');
80
+ };
81
+
82
+ export const grammar = class JSONMiniparserGrammar {
83
+ // @Cover
84
+ Expression(p) {
85
+ if (p.match('[')) {
86
+ p.eatProduction('Array');
87
+ } else if (p.match('{')) {
88
+ p.eatProduction('Object');
89
+ } else if (p.match(/true|false/y)) {
90
+ p.eatProduction('Boolean');
91
+ } else if (p.match('null')) {
92
+ p.eatProduction('Null');
93
+ } else if (p.match(/[+-]?(?:\d|Infinity)|NaN/y)) {
94
+ p.eatProduction('Number');
95
+ } else if (p.match(/['"]/y)) {
96
+ p.eatProduction('String');
97
+ }
98
+ }
99
+
100
+ // @Node
101
+ Object(p) {
102
+ p.eat('{', PN, { path: 'openToken', balanced: '}' });
103
+
104
+ p.eatMatchTrivia(_);
105
+
106
+ let first = true;
107
+ let sep;
108
+ while (first || (sep && (p.match(/./y) || p.atExpression))) {
109
+ p.eatProduction('Property', { path: 'properties[]' });
110
+ if (p.match(/\s*,?/y)) {
111
+ sep = p.eatProduction('ListSeparator', { path: 'separators[]' });
112
+ }
113
+ first = false;
114
+ }
115
+
116
+ p.eatMatchTrivia(_);
117
+
118
+ p.eat('}', PN, { path: 'closeToken', balancer: true });
119
+ }
120
+
121
+ // @Node
122
+ Property(p) {
123
+ p.eat(/[a-zA-Z]+/y, ID, { path: 'key' });
124
+ p.eatMatchTrivia(_);
125
+ p.eat(':', PN, { path: 'mapToken' });
126
+ p.eatMatchTrivia(_);
127
+ p.eatProduction('Expression', { path: 'value' });
128
+ }
129
+
130
+ // @Node
131
+ Array(p) {
132
+ p.eat('[', PN, { path: 'openToken', balanced: ']' });
133
+
134
+ p.eatMatchTrivia(_);
135
+
136
+ let first = true;
137
+ let sep;
138
+ while (first || (sep && (p.match(/./y) || p.atExpression))) {
139
+ p.eatProduction('Expression', { path: 'elements[]' });
140
+ if (p.match(/\s*,?/y)) {
141
+ sep = p.eatProduction('ListSeparator', { path: 'separators[]' });
142
+ }
143
+
144
+ first = false;
145
+ }
146
+
147
+ p.eat(']', PN, { path: 'closeToken', balancer: true });
148
+ }
149
+
150
+ // @Node
151
+ Boolean(p) {
152
+ p.eat(/true|false/y, KW, { path: 'sigilToken' });
153
+ }
154
+
155
+ // @Node
156
+ Null(p) {
157
+ p.eat('null', KW, { path: 'sigilToken' });
158
+ }
159
+
160
+ ListSeparator(p) {
161
+ p.eatMatchTrivia(_);
162
+ p.eatMatch(/,/y, PN, { path: 'separators[]' });
163
+ p.eatMatchTrivia(_);
164
+ }
165
+
166
+ Number(p) {
167
+ if (p.match(/-?\d/y)) {
168
+ p.eatProduction('Integer');
169
+ } else if (p.match('NaN')) {
170
+ p.eatProduction('NaN');
171
+ } else {
172
+ p.eatProduction('Infinity');
173
+ }
174
+ }
175
+
176
+ // @Node
177
+ Integer(p) {
178
+ p.eatMatch('-', 'Punctuator', { path: 'negative' });
179
+ p.eatProduction('Digits', { path: 'digits[]' });
180
+ }
181
+
182
+ // @Node
183
+ UnsignedInteger(p) {
184
+ p.eatProduction('Digits', { path: 'digits[]' });
185
+ }
186
+
187
+ // @Node
188
+ Infinity(p) {
189
+ p.eatMatch(/[+-]/, 'Punctuator', { path: 'sign' });
190
+ p.eat('Infinity', 'Keyword', { path: 'sigilToken' });
191
+ }
192
+
193
+ // @Node
194
+ NaN(p) {
195
+ p.eat('NaN', 'Keyword', { path: 'sigilToken' });
196
+ }
197
+
198
+ Digits(p) {
199
+ while (p.match(/\d/y)) {
200
+ p.eatProduction('Digit');
201
+ }
202
+ }
203
+
204
+ // @Node
205
+ Digit(p) {
206
+ p.eatLiteral(/\d/y);
207
+ }
208
+
209
+ // @Node
210
+ String(p) {
211
+ const q = p.match(/['"]/y) || '"';
212
+
213
+ const span = q === '"' ? 'Double' : 'Single';
214
+
215
+ p.eat(q, PN, { path: 'openToken', startSpan: span, balanced: q });
216
+ while (p.match(/./sy) || p.atExpression) {
217
+ p.eatProduction('StringContent', { path: 'content' });
218
+ }
219
+ p.eat(q, PN, { path: 'closeToken', endSpan: span, balancer: true });
220
+ }
221
+
222
+ // @Node
223
+ StringContent(p) {
224
+ let esc, lit;
225
+ let i = 0;
226
+ do {
227
+ esc =
228
+ p.span.type === 'Single'
229
+ ? p.eatMatchEscape(/\\(u(\{\d{1,6}\}|\d{4})|[\\gnrt0'])/y)
230
+ : p.eatMatchEscape(/\\(u(\{\d{1,6}\}|\d{4})|[\\gnrt0"])/y);
231
+ lit =
232
+ p.span.type === 'Single'
233
+ ? p.eatMatchLiteral(/[^\r\n\0\\']+/y)
234
+ : p.eatMatchLiteral(/[^\r\n\0\\"]+/y);
235
+ i++;
236
+ } while (esc || lit);
237
+ if (i === 1 && !esc && !lit) {
238
+ throw new Error('Invalid string content');
239
+ }
240
+ }
241
+ };
@@ -1,51 +1,77 @@
1
- const when = require('iter-tools/methods/when');
2
- const { escapables } = require('./cstml.js');
3
- const { buildCovers } = require('../utils.js');
4
- const { node } = require('@bablr/boot-helpers/symbols');
5
-
6
- const name = 'Regex';
7
-
8
- const canonicalURL = 'https://bablr.org/languages/core/en/bablr-regex-pattern';
9
-
10
- const dependencies = {};
11
-
12
- const covers = buildCovers({
13
- [node]: [
14
- 'RegExpLiteral',
15
- 'Flags',
16
- 'Pattern',
17
- 'Alternative',
18
- 'Group',
19
- 'CapturingGroup',
20
- 'Assertion',
21
- 'Character',
22
- 'CharacterClass',
23
- 'CharacterClassRange',
24
- 'AnyCharacterSet',
25
- 'WordCharacterSet',
26
- 'SpaceCharacterSet',
27
- 'DigitCharacterSet',
28
- 'Quantifier',
29
- 'Punctuator',
30
- 'Keyword',
31
- 'Escape',
32
- 'Number',
33
- 'Gap',
1
+ import * as sym from '@bablr/agast-helpers/symbols';
2
+ import when from 'iter-tools/methods/when';
3
+ import { escapables } from './json.js';
4
+
5
+ export const name = 'Regex';
6
+
7
+ export const canonicalURL = 'https://bablr.org/languages/core/en/bablr-regex-pattern';
8
+
9
+ export const dependencies = {};
10
+
11
+ export const covers = new Map([
12
+ [
13
+ sym.node,
14
+ new Set([
15
+ 'RegExpLiteral',
16
+ 'Flags',
17
+ 'Pattern',
18
+ 'Alternative',
19
+ 'Group',
20
+ 'CapturingGroup',
21
+ 'StartOfInputAssertion',
22
+ 'EndOfInputAssertion',
23
+ 'WordBoundaryAssertion',
24
+ 'Character',
25
+ 'CharacterClass',
26
+ 'CharacterClassRange',
27
+ 'AnyCharacterSet',
28
+ 'WordCharacterSet',
29
+ 'SpaceCharacterSet',
30
+ 'DigitCharacterSet',
31
+ 'Quantifier',
32
+ 'Punctuator',
33
+ 'Keyword',
34
+ 'Escape',
35
+ 'Number',
36
+ 'Gap',
37
+ ]),
34
38
  ],
35
- Assertion: ['StartOfInputAssertion', 'EndOfInputAssertion', 'WordBoundaryAssertion'],
36
- Element: [
37
- 'CharacterClass',
38
- 'Group',
39
- 'CapturingGroup',
40
- 'Assertion',
39
+ ['Assertion', new Set(['StartOfInputAssertion', 'EndOfInputAssertion', 'WordBoundaryAssertion'])],
40
+ [
41
+ 'Element',
42
+ new Set([
43
+ 'CharacterClass',
44
+ 'Group',
45
+ 'CapturingGroup',
46
+ 'StartOfInputAssertion',
47
+ 'EndOfInputAssertion',
48
+ 'WordBoundaryAssertion',
49
+ 'AnyCharacterSet',
50
+ 'WordCharacterSet',
51
+ 'SpaceCharacterSet',
52
+ 'DigitCharacterSet',
53
+ 'Gap',
54
+ 'Character',
55
+ 'Quantifier',
56
+ ]),
57
+ ],
58
+ [
59
+ 'CharacterClassElement',
60
+ new Set([
61
+ 'CharacterClassRange',
62
+ 'AnyCharacterSet',
63
+ 'WordCharacterSet',
64
+ 'SpaceCharacterSet',
65
+ 'DigitCharacterSet',
66
+ 'Character',
67
+ 'Gap',
68
+ ]),
69
+ ],
70
+ [
41
71
  'CharacterSet',
42
- 'Gap',
43
- 'Character',
44
- 'Quantifier',
72
+ new Set(['AnyCharacterSet', 'WordCharacterSet', 'SpaceCharacterSet', 'DigitCharacterSet']),
45
73
  ],
46
- CharacterClassElement: ['CharacterClassRange', 'CharacterSet', 'Character', 'Gap'],
47
- CharacterSet: ['AnyCharacterSet', 'WordCharacterSet', 'SpaceCharacterSet', 'DigitCharacterSet'],
48
- });
74
+ ]);
49
75
 
50
76
  const flags = {
51
77
  global: 'g',
@@ -77,7 +103,7 @@ const getSpecialPattern = (span) => {
77
103
  }
78
104
  };
79
105
 
80
- const cookEscape = (escape, span) => {
106
+ export const cookEscape = (escape, span) => {
81
107
  let hexMatch;
82
108
 
83
109
  if (!escape.startsWith('\\')) {
@@ -111,7 +137,7 @@ const cookEscape = (escape, span) => {
111
137
  throw new Error('unable to cook regex escape');
112
138
  };
113
139
 
114
- const grammar = class RegexMiniparserGrammar {
140
+ export const grammar = class RegexMiniparserGrammar {
115
141
  // @Node
116
142
  Pattern(p) {
117
143
  p.eat('/', PN, { path: 'openToken', balanced: '/' });
@@ -145,12 +171,12 @@ const grammar = class RegexMiniparserGrammar {
145
171
  Alternatives(p) {
146
172
  do {
147
173
  p.eatProduction('Alternative');
148
- } while (p.eatMatch('|', PN, { path: 'separators[]' }));
174
+ } while (p.eatMatch('|', PN, { path: 'separatorTokens[]' }));
149
175
  }
150
176
 
151
177
  // @Node
152
178
  Alternative(p) {
153
- p.eatProduction('Elements', { path: 'elements[]' });
179
+ p.eatProduction('Elements', { path: 'elements[]+' });
154
180
  }
155
181
 
156
182
  Elements(p) {
@@ -383,7 +409,7 @@ const grammar = class RegexMiniparserGrammar {
383
409
 
384
410
  // @Node
385
411
  Quantifier(p) {
386
- p.eatHeldProduction('Element', { path: 'element' });
412
+ p.eatHeldProduction('Element', { path: 'element+' });
387
413
 
388
414
  let attrs;
389
415
 
@@ -415,5 +441,3 @@ const grammar = class RegexMiniparserGrammar {
415
441
  return { attrs };
416
442
  }
417
443
  };
418
-
419
- module.exports = { name, canonicalURL, dependencies, covers, grammar, cookEscape };