@bablr/language-en-regex-vm-pattern 0.7.1 → 0.8.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.
Files changed (3) hide show
  1. package/lib/grammar.js +147 -10216
  2. package/lib/grammar.macro.js +121 -118
  3. package/package.json +15 -8
@@ -1,4 +1,4 @@
1
- import { i, re } from '@bablr/boot/shorthand.macro';
1
+ import { re, spam as m } from '@bablr/boot';
2
2
  import {
3
3
  Node,
4
4
  CoveredBy,
@@ -8,7 +8,16 @@ import {
8
8
  } from '@bablr/helpers/decorators';
9
9
  import objectEntries from 'iter-tools-es/methods/object-entries';
10
10
  import * as Shared from '@bablr/helpers/productions';
11
- import { buildString, buildBoolean, buildNumber, buildNullTag } from '@bablr/helpers/builders';
11
+ import {
12
+ eat,
13
+ eatMatch,
14
+ match,
15
+ holdForMatch,
16
+ guard,
17
+ bindAttribute,
18
+ fail,
19
+ } from '@bablr/helpers/grammar';
20
+ import { buildString, buildBoolean } from '@bablr/helpers/builders';
12
21
 
13
22
  export const canonicalURL = 'https://bablr.org/languages/core/en/bablr-regex-pattern';
14
23
 
@@ -66,17 +75,17 @@ const getSpecialPattern = (span) => {
66
75
  export const grammar = class RegexGrammar {
67
76
  @Node
68
77
  *Pattern() {
69
- yield i`eat(<*Punctuator '/' balanced='/' balancedSpan='Pattern' /> 'openToken')`;
70
- yield i`eat(<Alternatives />)`;
71
- yield i`eat(<*Punctuator '/' balancer /> 'closeToken')`;
72
- yield i`eat(<Flags /> 'flags$')`;
78
+ yield eat(m`openToken: <*Punctuator '/' { balanced: '/', balancedSpan: 'Pattern' } />`);
79
+ yield eat(m`<Alternatives />`);
80
+ yield eat(m`closeToken: <*Punctuator '/' { balancer: true } />`);
81
+ yield eat(m`flags$: <Flags />`);
73
82
  }
74
83
 
75
84
  @UnboundAttributes(Object.keys(flagCharacters))
76
85
  @AllowEmpty
77
86
  @Node
78
87
  *Flags({ ctx }) {
79
- const flags = yield i`match(/[gimsuy]+/)`;
88
+ const flags = yield match(re`/[gimsuy]+/`);
80
89
 
81
90
  const flagsStr = ctx.sourceTextFor(flags) || '';
82
91
 
@@ -84,115 +93,115 @@ export const grammar = class RegexGrammar {
84
93
 
85
94
  for (const { 0: name, 1: chr } of Object.entries(flagCharacters)) {
86
95
  if (flagsStr.includes(chr)) {
87
- yield i`bindAttribute(${buildString(name)} true)`;
96
+ yield bindAttribute(name, true);
88
97
  } else {
89
- yield i`bindAttribute(${buildString(name)} false)`;
98
+ yield bindAttribute(name, false);
90
99
  }
91
100
  }
92
101
 
93
102
  for (const flagChr of flagsStr) {
94
- yield i`eat(<*Keyword ${buildString(flagChr)} /> 'tokens[]')`;
103
+ yield eat(m`tokens[]: <*Keyword ${buildString(flagChr)} />`);
95
104
  }
96
105
  }
97
106
 
98
107
  @AllowEmpty
99
108
  *Alternatives() {
100
109
  do {
101
- yield i`eat(<Alternative /> 'alternatives[]$')`;
102
- } while (yield i`eatMatch(<*Punctuator '|' /> 'separators[]')`);
110
+ yield eat(m`alternatives[]$: <Alternative />`);
111
+ } while (yield eatMatch(m`separatorTokens[]: <*Punctuator '|' />`));
103
112
  }
104
113
 
105
114
  @AllowEmpty
106
115
  @Node
107
116
  *Alternative() {
108
- yield i`eat(<Elements /> 'elements[]$')`;
117
+ yield eat(m`elements[]$: <Elements />`);
109
118
  }
110
119
 
111
120
  @AllowEmpty
112
121
  *Elements() {
113
- yield i`eat([])`;
114
- while (yield i`match(/[^|]/)`) {
115
- yield i`eat(<+Element />)`;
122
+ yield eat(m`.[]: []`);
123
+ while (yield match(re`/[^|]/`)) {
124
+ yield eat(m`.[]+: <Element />`);
116
125
  }
117
126
  }
118
127
 
119
128
  *Element() {
120
- yield i`guard(<*Keyword /[*+?]/ />)`;
121
-
122
- yield i`eat(<Any /> null [
123
- <+CharacterClass '[' />
124
- <+Group '(?:' />
125
- <+Assertion /[$^]|\\b/i />
126
- <+Gap '\\g' />
127
- <+CharacterSet /\.|\\[dswp]/i />
128
- <*+Character />
129
- ])`;
130
-
131
- if (yield i`match(/[*+?{]/)`) {
132
- return i`holdForMatch(<Quantifier />)`;
129
+ yield guard(m`<*Keyword /[*+?]/ />`);
130
+
131
+ yield eat(m`<Any />`, [
132
+ m`<CharacterClass '[' />`,
133
+ m`<Group '(?:' />`,
134
+ m`<Assertion /[$^]|\\b/i />`,
135
+ m`<Gap '\\g' />`,
136
+ m`<CharacterSet /\.|\\[dswp]/i />`,
137
+ m`<*Character />`,
138
+ ]);
139
+
140
+ if (yield match(re`/[*+?{]/`)) {
141
+ return holdForMatch(m`<Quantifier />`);
133
142
  }
134
143
  }
135
144
 
136
145
  @CoveredBy('Element')
137
146
  @Node
138
147
  *Group() {
139
- yield i`eat(<*Punctuator '(?:' balanced=')' /> 'openToken')`;
140
- yield i`eat(<Alternatives />)`;
141
- yield i`eat(<*Punctuator ')' balancer /> 'closeToken')`;
148
+ yield eat(m`openToken: <*Punctuator '(?:' { balanced: ')' } />`);
149
+ yield eat(m`<Alternatives />`);
150
+ yield eat(m`closeToken: <*Punctuator ')' { balancer: true } />`);
142
151
  }
143
152
 
144
153
  @Node
145
154
  *CapturingGroup() {
146
- yield i`eat(<*Punctuator '(' balanced=')' /> 'openToken')`;
147
- yield i`eat(<Alternatives />)`;
148
- yield i`eat(<*Punctuator ')' balancer /> 'closeToken')`;
155
+ yield eat(m`openToken: <*Punctuator '(' { balanced: ')' } />`);
156
+ yield eat(m`<Alternatives />`);
157
+ yield eat(m`closeToken: <*Punctuator ')' { balancer: true } />`);
149
158
  }
150
159
 
151
160
  @CoveredBy('Element')
152
161
  *Assertion() {
153
- yield i`eat(<Any /> null [
154
- <*StartOfInputAssertion '^' />
155
- <*EndOfInputAssertion '$' />
156
- <*@WordBoundaryAssertion /\\b/i />
157
- ])`;
162
+ yield eat(m`<Any />`, [
163
+ m`<*StartOfInputAssertion '^' />`,
164
+ m`<*EndOfInputAssertion '$' />`,
165
+ m`<*WordBoundaryAssertion /\\b/i />`,
166
+ ]);
158
167
  }
159
168
 
160
169
  @CoveredBy('Assertion')
161
170
  @Node
162
171
  *StartOfInputAssertion() {
163
- yield i`eat(<*Keyword '^' /> 'sigilToken')`;
172
+ yield eat(m`sigilToken: <*Keyword '^' />`);
164
173
  }
165
174
 
166
175
  @CoveredBy('Assertion')
167
176
  @Node
168
177
  *EndOfInputAssertion() {
169
- yield i`eatMatch(<*Keyword '$' /> 'sigilToken')`;
178
+ yield eatMatch(m`sigilToken: <*Keyword '$' />`);
170
179
  }
171
180
 
172
181
  @UnboundAttributes(['negate'])
173
182
  @CoveredBy('Assertion')
174
183
  @Node
175
184
  *WordBoundaryAssertion({ ctx }) {
176
- yield i`eatMatch(<*Punctuator '\\' /> 'escapeToken')`;
177
- const m = yield i`eat(<*Keyword /b/i /> 'value')`;
178
- yield i`bindAttribute('negate' ${buildBoolean(ctx.sourceTextFor(m) === 'B')})`;
185
+ yield eatMatch(m`escapeToken: <*Punctuator '\\' />`);
186
+ const m_ = yield eat(m`value: <*Keyword /b/i />`);
187
+ yield bindAttribute('negate', buildBoolean(ctx.sourceTextFor(m_) === 'B'));
179
188
  }
180
189
 
181
190
  @CoveredBy('Assertion')
182
191
  @Node
183
192
  *Gap() {
184
- yield i`eatMatch(<*Punctuator '\\' /> 'escapeToken')`;
185
- yield i`eat(<*Keyword 'g' /> 'value')`;
193
+ yield eatMatch(m`escapeToken: <*Punctuator '\\' />`);
194
+ yield eat(m`value: <*Keyword 'g' />`);
186
195
  }
187
196
 
188
197
  @CoveredBy('Element')
189
198
  @CoveredBy('CharacterClassElement')
190
199
  @Node
191
200
  *Character() {
192
- if (yield i`match('\\')`) {
193
- yield i`eat(<@EscapeSequence /> null)`;
201
+ if (yield match('\\')) {
202
+ yield eat(m`@: <EscapeSequence />`);
194
203
  } else {
195
- yield i`eat(/[^\r\n\t]/)`;
204
+ yield eat(re`/[^\r\n\t]/`);
196
205
  }
197
206
  }
198
207
 
@@ -200,96 +209,92 @@ export const grammar = class RegexGrammar {
200
209
  @CoveredBy('Element')
201
210
  @Node
202
211
  *CharacterClass() {
203
- yield i`eat(<*Punctuator '[' balancedSpan='CharacterClass' balanced=']' /> 'openToken')`;
212
+ yield eat(m`openToken: <*Punctuator '[' { balancedSpan: 'CharacterClass', balanced: ']' } />`);
204
213
 
205
- let neg = yield i`eatMatch(<*Keyword '^' /> 'negateToken')`;
214
+ let negate = yield eatMatch(m`negateToken: <*Keyword '^' />`);
206
215
 
207
- yield i`bindAttribute('negate' ${buildBoolean(neg)})`;
216
+ yield bindAttribute('negate', !!negate);
208
217
 
209
- while (yield i`match(/./s)`) {
210
- yield i`eat(<+CharacterClassElement /> 'elements[]$')`;
218
+ while (yield match(re`/./s`)) {
219
+ yield eat(m`elements[]+$: <CharacterClassElement />`);
211
220
  }
212
221
 
213
- yield i`eat(<*Punctuator ']' balancer /> 'closeToken')`;
222
+ yield eat(m`closeToken: <*Punctuator ']' { balancer: true } />`);
214
223
  }
215
224
 
216
225
  *CharacterClassElement() {
217
- yield i`eat(<Any /> null [
218
- <CharacterSet /\\[dswp]/i />
219
- <Gap '\\g' />
220
- <*+Character />
221
- ])`;
222
-
223
- if (yield i`match('-')`) {
224
- return i`holdForMatch(<+CharacterClassRange />)`;
226
+ yield eat(m`<Any />`, [m`<CharacterSet /\\[dswp]/i />`, m`<Gap '\\g' />`, m`<*Character />`]);
227
+
228
+ if (yield match('-')) {
229
+ return holdForMatch(m`<CharacterClassRange />`);
225
230
  }
226
231
  }
227
232
 
228
233
  @CoveredBy('CharacterClassElement')
229
234
  @Node
230
235
  *CharacterClassRange() {
231
- yield i`eat(<*+Character /> 'min$')`;
232
- yield i`eat(<*Punctuator '-' /> 'sigilToken')`;
233
- yield i`eat(<*+Character /> 'max$')`;
236
+ yield eat(m`min+$: <*Character />`);
237
+ yield eat(m`sigilToken: <*Punctuator '-' />`);
238
+ yield eat(m`max+$: <*Character />`);
234
239
  }
235
240
 
236
241
  @CoveredBy('Element')
237
242
  *CharacterSet() {
238
- yield i`eat(<Any /> null [
239
- <+AnyCharacterSet '.' />
240
- <+DigitCharacterSet /\\[dD]/ />
241
- <+SpaceCharacterSet /\\[sS]/ />
242
- <+WordCharacterSet /\\[wW]/ />
243
- ])`;
243
+ yield eat(m`<Any />`, [
244
+ m`<AnyCharacterSet '.' />`,
245
+ m`<DigitCharacterSet /\\[dD]/ />`,
246
+ m`<SpaceCharacterSet /\\[sS]/ />`,
247
+ m`<WordCharacterSet /\\[wW]/ />`,
248
+ ]);
244
249
  }
245
250
 
246
251
  @CoveredBy('CharacterSet')
247
252
  @Node
248
253
  *AnyCharacterSet() {
249
- yield i`eat(<*Keyword '.' /> 'sigilToken')`;
254
+ yield eat(m`sigilToken: <*Keyword '.' />`);
250
255
  }
251
256
 
252
257
  @UnboundAttributes(['negate'])
253
258
  @CoveredBy('CharacterSet')
254
259
  @Node
255
260
  *DigitCharacterSet({ ctx }) {
256
- yield i`eat(<*Punctuator '\\' /> 'escapeToken')`;
261
+ yield eat(m`escapeToken: <*Punctuator '\\' />`);
257
262
 
258
- let code = yield i`eat(<*Keyword /[dD]/ /> 'value')`;
263
+ let code = yield eat(m`value: <*Keyword /[dD]/ />`);
259
264
 
260
- yield i`bindAttribute('negate' ${buildBoolean(ctx.sourceTextFor(code) === 'D')})`;
265
+ yield bindAttribute('negate', buildBoolean(ctx.sourceTextFor(code) === 'D'));
261
266
  }
262
267
 
263
268
  @UnboundAttributes(['negate'])
264
269
  @CoveredBy('CharacterSet')
265
270
  @Node
266
271
  *SpaceCharacterSet({ ctx }) {
267
- yield i`eat(<*Punctuator '\\' /> 'escapeToken')`;
272
+ yield eat(m`escapeToken: <*Punctuator '\\' />`);
268
273
 
269
- let code = yield i`eat(<*Keyword /[sS]/ /> 'value')`;
274
+ let code = yield eat(m`value: <*Keyword /[sS]/ />`);
270
275
 
271
- yield i`bindAttribute('negate' ${buildBoolean(ctx.sourceTextFor(code) === 'S')})`;
276
+ yield bindAttribute('negate', buildBoolean(ctx.sourceTextFor(code) === 'S'));
272
277
  }
273
278
 
274
279
  @UnboundAttributes(['negate'])
275
280
  @CoveredBy('CharacterSet')
276
281
  @Node
277
282
  *WordCharacterSet({ ctx }) {
278
- yield i`eat(<*Punctuator '\\' /> 'escapeToken')`;
283
+ yield eat(m`escapeToken: <*Punctuator '\\' />`);
279
284
 
280
- let code = yield i`eat(<*Keyword /[wW]/ /> 'value')`;
285
+ let code = yield eat(m`value: <*Keyword /[wW]/ />`);
281
286
 
282
- yield i`bindAttribute('negate' ${buildBoolean(ctx.sourceTextFor(code) === 'W')})`;
287
+ yield bindAttribute('negate', ctx.sourceTextFor(code) === 'W');
283
288
  }
284
289
 
285
290
  @UnboundAttributes(['min', 'max'])
286
291
  @Node
287
292
  *Quantifier({ ctx }) {
288
- yield i`eat(<+Element /> 'element$')`;
293
+ yield eat(m`element+$: <Element />`);
289
294
 
290
295
  let attrs, sigil;
291
296
 
292
- if ((sigil = yield i`eatMatch(<*Keyword /[*+?]/ /> 'sigilToken')`)) {
297
+ if ((sigil = yield eatMatch(m`sigilToken: <*Keyword /[*+?]/ />`))) {
293
298
  switch (ctx.sourceTextFor(sigil)) {
294
299
  case '*':
295
300
  attrs = { min: 0, max: Infinity };
@@ -301,12 +306,12 @@ export const grammar = class RegexGrammar {
301
306
  attrs = { min: 0, max: 1 };
302
307
  break;
303
308
  }
304
- } else if (yield i`eat(<*Punctuator '{' balanced='}' /> 'openToken')`) {
309
+ } else if (yield eat(m`openToken: <*Punctuator '{' { balanced: '}' } />`)) {
305
310
  let max;
306
- let min = yield i`eat(<*UnsignedInteger /> 'min$')`;
311
+ let min = yield eat(m`min$: <*UnsignedInteger />`);
307
312
 
308
- if (yield i`eatMatch(<*Punctuator ',' /> 'separator')`) {
309
- max = yield i`eatMatch(<*UnsignedInteger /> 'max$')`;
313
+ if (yield eatMatch(m`separator: <*Punctuator ',' />`)) {
314
+ max = yield eatMatch(m`max$: <*UnsignedInteger />`);
310
315
  }
311
316
 
312
317
  min = min && ctx.sourceTextFor(min);
@@ -317,65 +322,63 @@ export const grammar = class RegexGrammar {
317
322
 
318
323
  attrs = { min, max };
319
324
 
320
- yield i`eat(<*Punctuator '}' balancer /> 'closeToken')`;
325
+ yield eat(m`closeToken: <*Punctuator '}' { balancer: true } />`);
321
326
  }
322
327
 
323
- yield i`bindAttribute('min' ${attrs.min ? buildNumber(attrs.min) : buildNullTag()})`;
324
- yield i`bindAttribute('max' ${attrs.max ? buildNumber(attrs.max) : buildNullTag()})`;
328
+ yield bindAttribute('min', attrs.min);
329
+ yield bindAttribute('max', attrs.max);
325
330
  }
326
331
 
327
332
  @Node
328
333
  *UnsignedInteger() {
329
- yield i`eat(/\d+/)`;
334
+ yield eat(re`/\d+/`);
330
335
  }
331
336
 
332
337
  @Node
333
338
  *EscapeSequence({ state, ctx, value: props }) {
334
339
  const parentSpan = state.span;
335
340
 
336
- yield i`eat(<*Punctuator '\\' openSpan='Escape' /> 'escape')`;
341
+ yield eat(m`escape: <*Punctuator '\\' { openSpan: 'Escape' } />`);
337
342
 
338
- let match;
343
+ let m_;
339
344
 
340
- if ((match = yield i`match(/[\\/nrt0]/)`)) {
341
- const match_ = ctx.sourceTextFor(match);
342
- yield i`eat(<*Keyword ${buildString(match_)} closeSpan='Escape' /> 'code')`;
343
- } else if (
344
- (match = yield i`match(${getSpecialPattern(parentSpan, ctx.reifyExpression(props))})`)
345
- ) {
346
- const match_ = ctx.sourceTextFor(match);
347
- yield i`eat(<*Keyword ${buildString(match_)} closeSpan='Escape' /> 'code')`;
348
- } else if (yield i`match(/[ux]/)`) {
349
- yield i`eat(<EscapeCode closeSpan='Escape' /> 'code')`;
345
+ if ((m_ = yield match(re`/[\\/nrt0]/`))) {
346
+ const match_ = ctx.sourceTextFor(m_);
347
+ yield eat(m`code: <*Keyword ${buildString(match_)} { closeSpan: 'Escape' } />`);
348
+ } else if ((m_ = yield match(getSpecialPattern(parentSpan)))) {
349
+ const match_ = ctx.sourceTextFor(m_);
350
+ yield eat(m`code: <*Keyword ${buildString(match_)} { closeSpan: 'Escape' } />`);
351
+ } else if (yield match(re`/[ux]/`)) {
352
+ yield eat(m`code: <EscapeCode { closeSpan: 'Escape' } />`);
350
353
  } else {
351
- yield i`fail()`;
354
+ yield fail();
352
355
  }
353
356
  }
354
357
 
355
358
  @Node
356
359
  *EscapeCode() {
357
- if (yield i`eatMatch(<*Keyword 'u' /> 'type')`) {
358
- if (yield i`eatMatch(<*Punctuator '{' /> 'openToken')`) {
359
- yield i`eatMatch(<*UnsignedInteger /> 'value$')`;
360
- yield i`eat(<*Punctuator '}' /> 'closeToken')`;
360
+ if (yield eatMatch(m`type: <*Keyword 'u' />`)) {
361
+ if (yield eatMatch(m`openToken: <*Punctuator '{' />`)) {
362
+ yield eatMatch(m`value$: <*UnsignedInteger />`);
363
+ yield eat(m`closeToken: <*Punctuator '}' />`);
361
364
  } else {
362
- yield i`eat(<*UnsignedInteger /\d{4}/ /> 'value$')`;
363
- yield i`eat(null 'closeToken')`;
365
+ yield eat(m`value$: <*UnsignedInteger /\d{4}/ />`);
366
+ yield eat(m`closeToken: null`);
364
367
  }
365
- } else if (yield i`eatMatch(<*Keyword 'x' /> 'type')`) {
366
- yield i`eat(null 'openToken')`;
367
- yield i`eat(<*UnsignedInteger /\d{2}/ /> 'value$')`;
368
- yield i`eat(null 'closeToken')`;
368
+ } else if (yield eatMatch(m`type: <*Keyword 'x' />`)) {
369
+ yield eat(m`openToken: null`);
370
+ yield eat(m`value$: <*UnsignedInteger /\d{2}/ />`);
371
+ yield eat(m`closeToken: null`);
369
372
  }
370
373
  }
371
374
 
372
375
  *Digits() {
373
- while (yield i`eatMatch(<*Digit />)`);
376
+ while (yield eatMatch(m`<*Digit />`));
374
377
  }
375
378
 
376
379
  @Node
377
380
  *Digit() {
378
- yield i`eat(/\d/)`;
381
+ yield eat(re`/\d/`);
379
382
  }
380
383
 
381
384
  @InjectFrom(Shared)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bablr/language-en-regex-vm-pattern",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "A BABLR language for nonbacktracking JS-style regexes",
5
5
  "engines": {
6
6
  "node": ">=12.0.0"
@@ -10,7 +10,9 @@
10
10
  ".": "./lib/grammar.js",
11
11
  "./package.json": "./package.json"
12
12
  },
13
- "files": ["lib/**/*.js"],
13
+ "files": [
14
+ "lib/**/*.js"
15
+ ],
14
16
  "sideEffects": false,
15
17
  "scripts": {
16
18
  "build": "macrome build",
@@ -20,13 +22,13 @@
20
22
  },
21
23
  "dependencies": {
22
24
  "@babel/runtime": "^7.23.2",
23
- "@bablr/helpers": "^0.20.0",
24
- "@bablr/agast-helpers": "^0.5.0",
25
- "@bablr/agast-vm-helpers": "^0.5.0",
26
- "iter-tools-es": "^7.5.3"
25
+ "@bablr/helpers": "0.21.1",
26
+ "@bablr/agast-helpers": "0.6.0",
27
+ "@bablr/agast-vm-helpers": "0.6.0",
28
+ "iter-tools-es": "7.5.3"
27
29
  },
28
30
  "devDependencies": {
29
- "@bablr/boot": "^0.6.0",
31
+ "@bablr/boot": "^0.7.0",
30
32
  "@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#49f5952efed27f94ee9b94340eb1563c440bf64e",
31
33
  "@bablr/macrome": "^0.1.3",
32
34
  "@bablr/macrome-generator-bablr": "^0.3.2",
@@ -40,7 +42,12 @@
40
42
  "mocha": "^10.4.0",
41
43
  "prettier": "^2.0.5"
42
44
  },
43
- "keywords": ["bablr-language", "grammar", "english", "regex"],
45
+ "keywords": [
46
+ "bablr-language",
47
+ "grammar",
48
+ "english",
49
+ "regex"
50
+ ],
44
51
  "repository": "git@github.com:bablr-lang/language-en-regex-vm-pattern.git",
45
52
  "homepage": "https://github.com/bablr-lang/language-en-regex-vm-pattern",
46
53
  "author": "Conrad Buck <conartist6@gmail.com>",