@bablr/helpers 0.24.0 → 0.25.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/trivia.js CHANGED
@@ -1,345 +1,211 @@
1
1
  /* global WeakSet */
2
- import { spam as m } from '@bablr/boot';
3
2
  import { Coroutine } from '@bablr/coroutine';
4
- import { eat, eatMatch, mapOwnProductions, mapProductions, o } from './grammar.js';
3
+ import { eat, eatMatch, mapProductions, o, r } from './grammar.js';
4
+ import { Matcher, Regex } from './symbols.js';
5
+ import { get, getCooked, getFlagsWithGap, nodeFlags } from '@bablr/agast-helpers/tree';
6
+ import {
7
+ buildTreeNodeMatcher,
8
+ buildBindingMatchers,
9
+ buildIdentifier,
10
+ buildNodeFlags,
11
+ buildTreeNodeMatcherOpen,
12
+ buildPropertyMatcher,
13
+ buildString,
14
+ buildBoundNodeMatcher,
15
+ } from './builders.js';
16
+ import { spam as m } from '@bablr/boot';
5
17
  import {
6
- Matcher,
7
- Regex,
8
- Node,
9
- OpenNodeTag,
10
- CloseNodeTag,
11
- ReferenceTag,
12
- Property,
13
- } from './symbols.js';
14
- import { buildIdentifier, buildString } from './builders.js';
15
- import { buildCall, buildEmbeddedInstruction } from '@bablr/agast-vm-helpers/builders';
16
- import { getEmbeddedInstruction } from '@bablr/agast-vm-helpers/deembed';
18
+ buildCall,
19
+ buildEmbeddedMatcher,
20
+ buildEmbeddedNode,
21
+ buildEmbeddedObject,
22
+ buildEmbeddedRegex,
23
+ } from '@bablr/agast-vm-helpers/builders';
17
24
  import { reifyExpression } from '@bablr/agast-vm-helpers';
18
- import { get, getCooked } from '@bablr/agast-helpers/tree';
19
- import { buildPathSegment } from '@bablr/agast-helpers/path';
20
-
21
- const lookbehind = (context, s) => {
22
- let token = s.resultPath;
23
- while (token && [OpenNodeTag, CloseNodeTag, ReferenceTag].includes(token.type)) {
24
- const prevToken = context.getPreviousTagPath(token);
25
- if (!prevToken) break;
26
- token = prevToken;
27
- }
28
- return token;
29
- };
30
-
31
- const matchedResults = new WeakSet();
32
-
33
- export const basicTriviaEnhancer = ({ triviaIsAllowed, triviaMatcher }, grammar) => {
34
- let Wrapper_ = 'Wrapper';
35
- let Literal_ = 'Literal';
36
-
37
- while (grammar.prototype[Wrapper_]) Wrapper_ += '_';
38
- while (grammar.prototype[Literal_]) Literal_ += '_';
39
-
40
- const resultGrammar = mapOwnProductions((production) => {
41
- return function* (props) {
42
- const co = new Coroutine(production(props));
43
- const { s, ctx, flags, isCover, isCovered } = props;
44
-
45
- co.advance();
46
-
47
- try {
48
- while (!co.done) {
49
- const instr = co.value;
50
- const { verb, arguments: args = [] } = instr;
51
- let returnValue = undefined;
52
-
53
- switch (verb) {
54
- case 'eat':
55
- case 'eatMatch':
56
- case 'match':
57
- case 'guard': {
58
- const { 0: matcher } = args;
59
-
60
- if (
61
- matcher &&
62
- !isCovered &&
63
- matcher.type === Matcher &&
64
- getCooked(get(['refMatcher', 'type', 'value'], matcher.value)) !== '#'
65
- ) {
66
- const previous = lookbehind(ctx, s);
67
- if (triviaIsAllowed(s) && (!previous || !matchedResults.has(previous))) {
68
- matchedResults.add(previous);
69
- yield eatMatch(triviaMatcher);
70
- matchedResults.add(s.resultPath);
71
- }
72
- }
73
-
74
- returnValue = returnValue || (yield instr);
75
- break;
76
- }
77
-
78
- default:
79
- returnValue = yield instr;
80
- break;
81
- }
82
-
83
- co.advance(returnValue);
84
- }
85
-
86
- if (co.value) {
87
- let realMatcher = reifyExpression(get('nodeMatcher', co.value.arguments[0].value));
88
- let { flags: matcherFlags } = realMatcher;
89
-
90
- let isNode = matcherFlags && !matcherFlags.fragment;
91
-
92
- if (
93
- !flags.token &&
94
- isNode &&
95
- !isCover &&
96
- (!s.depths.path || (co.value && ['shift', 'shiftMatch'].includes(co.value.verb)))
97
- ) {
98
- if (triviaIsAllowed(s)) {
99
- yield eatMatch(triviaMatcher);
100
- }
101
- }
102
-
103
- return co.value;
104
- } else {
105
- if (!s.depths.path && !flags.token && !isCovered) {
106
- if (triviaIsAllowed(s)) {
107
- yield eatMatch(triviaMatcher);
108
- }
109
- }
110
- }
111
- } catch (e) {
112
- co.throw(e);
113
- throw e;
114
- }
115
- };
116
- }, grammar);
117
-
118
- return class extends resultGrammar {
119
- *[Wrapper_]({ value: wrapped }) {
120
- for (const instr of wrapped) {
121
- yield getEmbeddedInstruction(instr);
122
- }
123
- }
124
-
125
- *[Literal_]({ value: matcher }) {
126
- yield eat(matcher);
127
- }
128
- };
129
- };
130
25
 
131
26
  export const triviaEnhancer = (
132
27
  { triviaIsAllowed, triviaIsRequired = () => false, triviaMatcher },
133
28
  grammar,
134
29
  ) => {
135
- if (!triviaMatcher) throw new Error();
136
-
137
- let Wrapper_ = 'Wrapper';
30
+ let Trivia_ = 'Trivia';
138
31
  let Literal_ = 'Literal';
139
32
 
140
- while (grammar.prototype[Wrapper_]) Wrapper_ += '_';
33
+ while (grammar.prototype[Trivia_]) Trivia_ += '_';
141
34
  while (grammar.prototype[Literal_]) Literal_ += '_';
142
35
 
143
- const resultGrammar = mapProductions((production) => {
144
- return function* (args) {
145
- const co = new Coroutine(production(args));
146
- const { ctx, s, isCovered, isCover, isCoverBoundary, flags, mergedReference } = args;
36
+ let tmi = eatMatch(triviaMatcher);
37
+ let literalMatcher = get(['nodeMatcher', 'open', 'literalValue'], triviaMatcher.value);
147
38
 
148
- co.advance();
39
+ let resultGrammar = mapProductions((production) => {
40
+ return function* (props) {
41
+ let co = new Coroutine(production(props));
42
+ let outerProps = props;
43
+ let { type, getState, s, flags, isCover, isCoverBoundary: thisIsCoverBoundary } = props;
44
+ let isRootFragment = type === Symbol.for('__') && !s().depths.path;
149
45
 
150
46
  try {
151
- while (!co.done) {
152
- const instr = co.value;
153
- const { verb, arguments: args = [] } = instr;
154
- let returnValue = undefined;
47
+ let returnValue = undefined;
48
+ do {
49
+ co.advance(returnValue);
50
+
51
+ let instr = co.done ? co.value?.shift : co.value;
52
+
53
+ if (co.done && !instr) break;
54
+
55
+ let { verb, arguments: args = [] } = instr;
155
56
 
156
57
  switch (verb) {
157
58
  case 'eat':
158
59
  case 'eatMatch':
60
+ case 'shift':
61
+ case 'shiftMatch':
159
62
  case 'match':
160
63
  case 'guard': {
161
- const { 0: matcher, 1: props, 2: options } = args;
162
-
163
- if (
164
- matcher.type === Matcher &&
165
- ['ArrayNodeMatcher', 'NullNodeMatcher'].includes(
166
- get('nodeMatcher', matcher.value).type.description,
167
- )
168
- ) {
169
- returnValue = yield instr;
170
- break;
171
- }
172
-
173
- if (
174
- matcher.type === Regex ||
175
- typeof matcher === 'string' ||
176
- (matcher.type === Node && matcher.value.flags.token)
177
- ) {
64
+ let { 0: matcher, 1: props, 2: options } = args;
65
+ let s = getState();
66
+
67
+ let isCoverBoundary = co.done
68
+ ? thisIsCoverBoundary
69
+ : isRootFragment ||
70
+ (reifyExpression(
71
+ get(['valueMatcher', 'nodeMatcher', 'open', 'type'], matcher.value),
72
+ ) !== '__' &&
73
+ !isCover);
74
+
75
+ if (matcher.type === Regex || typeof matcher === 'string') {
178
76
  if (triviaIsAllowed(s) && !flags.token) {
77
+ if (literalMatcher) {
78
+ let literalResult = yield buildCall(
79
+ 'match',
80
+ buildEmbeddedRegex(literalMatcher),
81
+ );
82
+ if (!literalResult) {
83
+ returnValue = yield instr;
84
+ break;
85
+ }
86
+ }
87
+
179
88
  let isString = typeof matcher === 'string';
180
- let wrappedMatcher = m`#: <*Literal ${
89
+ let wrappedMatcher = m`#: <*${buildIdentifier(Literal_)} ${
181
90
  isString ? buildString(matcher) : matcher.value
182
91
  } />`;
183
92
 
184
- let tmi = buildEmbeddedInstruction(eatMatch(triviaMatcher));
185
-
186
- let result = yield buildCall(verb, m`<__${buildIdentifier(Wrapper_)} />`, [
187
- tmi,
188
- buildEmbeddedInstruction(eat(wrappedMatcher, props, options)),
189
- ]);
190
-
191
- let prop = null;
192
-
193
- if (result) {
194
- for (let child of result.tagsInner) {
195
- if (child.type === Property) prop = child.value;
196
- }
197
- }
93
+ let result = yield buildCall(
94
+ verb,
95
+ m`<_${buildIdentifier(Trivia_)} />`,
96
+ o({
97
+ matcher: wrappedMatcher,
98
+ props,
99
+ options: o({ ...(options?.value ?? {}), allowEmpty: true }),
100
+ matchTrailing: false,
101
+ }),
102
+ );
198
103
 
199
- returnValue = prop?.node;
200
- } else {
201
- returnValue = yield instr;
104
+ returnValue = result?.value;
105
+ break;
202
106
  }
203
- break;
204
107
  }
205
108
 
206
- let { type: refType } = reifyExpression(get('refMatcher', matcher.value)) || {};
207
- let realMatcher = reifyExpression(get('nodeMatcher', matcher.value));
208
- let { flags: matcherFlags } = realMatcher;
209
-
210
- let isNode = matcherFlags && !matcherFlags.fragment;
211
- let isCoverBoundary = matcherFlags && (matcherFlags.cover || (isNode && !isCovered));
212
109
  if (
213
110
  matcher &&
214
- (matcher.type !== Matcher || refType !== '#') &&
215
- !s.holding &&
216
- !flags.token &&
217
- isCoverBoundary
111
+ !s.node.value.flags.token &&
112
+ isCoverBoundary &&
113
+ (matcher.type !== Matcher ||
114
+ getCooked(get(['refMatcher', 'type'], matcher.value)) !== '#')
218
115
  ) {
219
- if (triviaIsAllowed(s)) {
220
- let { nodeMatcher } = reifyExpression(matcher.value);
221
-
222
- let tmi = buildEmbeddedInstruction(
223
- triviaIsRequired(s, nodeMatcher) ? eat(triviaMatcher) : eatMatch(triviaMatcher),
224
- );
225
-
226
- let result = yield buildCall(
227
- verb,
228
- m`<__${buildIdentifier(Wrapper_)} />`,
229
- [tmi, buildEmbeddedInstruction(eat(matcher, props, options))],
230
- o({ bind: options?.value.bind ?? false }),
231
- );
232
- let trivialResult = result;
233
-
234
- if (result && !result.isNull) {
235
- if (isNode || isCoverBoundary) {
236
- let refMatcher = get('refMatcher', matcher.value);
237
-
238
- let name, isArray;
239
-
240
- if (!refMatcher) {
241
- ({ name, isArray } = mergedReference);
116
+ if (
117
+ triviaIsAllowed(s) &&
118
+ !(getState().holding || co.done) &&
119
+ get(['valueMatcher', 'nodeMatcher'], matcher.value).value.name?.description ===
120
+ 'TreeNodeMatcher'
121
+ ) {
122
+ // TODO this is a problem. What if there's trivia before?
123
+ if (literalMatcher) {
124
+ let literalResult = yield buildCall(
125
+ 'match',
126
+ buildEmbeddedRegex(literalMatcher),
127
+ );
128
+ if (!literalResult) {
129
+ if (co.done) {
130
+ return r(instr, co.value.value);
242
131
  } else {
243
- let name_ = get('name', refMatcher);
244
- let openIndexToken = get('openIndexToken', refMatcher);
245
- name = name_ && ctx.sourceTextFor(name_);
246
- isArray = !!openIndexToken;
247
- }
248
-
249
- if (name) {
250
- let pathSpec = name;
251
-
252
- if (isArray) {
253
- pathSpec = buildPathSegment(name, -1);
254
- }
255
-
256
- result = result.get([pathSpec]);
132
+ returnValue = yield instr;
257
133
  }
134
+ break;
258
135
  }
259
-
260
- result = result && result.merge(trivialResult);
261
136
  }
262
- returnValue = result;
263
- } else {
264
- returnValue = yield instr;
137
+
138
+ instr = buildCall(
139
+ verb,
140
+ buildEmbeddedMatcher(
141
+ buildPropertyMatcher(
142
+ get('refMatcher', matcher.value),
143
+ buildBoundNodeMatcher(
144
+ [],
145
+ buildTreeNodeMatcher(
146
+ buildTreeNodeMatcherOpen(
147
+ buildNodeFlags(getFlagsWithGap(nodeFlags, flags.hasGap)),
148
+ '_',
149
+ Trivia_,
150
+ ),
151
+ ),
152
+ ),
153
+ ),
154
+ ),
155
+ o({
156
+ matcher,
157
+ props,
158
+ matchTrailing: isRootFragment,
159
+ }),
160
+ ...(options ? [options] : []),
161
+ );
265
162
  }
163
+ }
164
+
165
+ // if (co.done) {
166
+ // // account for language shift due to binding
167
+ // let outerMatcher = reifyExpression(outerProps.matcher);
168
+ // if (outerMatcher.bindingMatchers.length) {
169
+ // instr = buildCall(
170
+ // verb,
171
+ // buildEmbeddedMatcher(
172
+ // buildPropertyMatcher(
173
+ // get('refMatcher', matcher.value),
174
+ // buildBoundNodeMatcher(
175
+ // buildBindingMatchers(outerMatcher.bindingMatchers),
176
+ // get('nodeMatcher', matcher.value),
177
+ // ),
178
+ // ),
179
+ // ),
180
+ // props,
181
+ // options,
182
+ // );
183
+ // }
184
+
185
+ // return r(instr, co.value.value);
186
+ // } else {
187
+ if (co.done) {
188
+ return r(instr, co.value.value);
266
189
  } else {
267
190
  returnValue = yield instr;
268
191
  }
269
192
  break;
193
+ // }
270
194
  }
271
195
 
272
196
  default:
273
- returnValue = yield instr;
197
+ if (co.done) {
198
+ return r(instr, co.value.value);
199
+ } else {
200
+ returnValue = yield instr;
201
+ }
274
202
  break;
275
203
  }
204
+ } while (!co.done);
276
205
 
277
- co.advance(returnValue);
278
- }
279
-
280
- if (!flags.token && !(isCovered || isCover)) {
281
- if (triviaIsAllowed(s)) {
282
- yield eatMatch(triviaMatcher);
283
- }
284
- } else if (co.value) {
285
- let matcher = co.value.arguments[0];
286
- let realMatcher = reifyExpression(get('nodeMatcher', matcher.value));
287
- let { flags: matcherFlags } = realMatcher;
288
-
289
- let isNode = matcherFlags && !matcherFlags.fragment;
290
-
291
- if (
292
- !flags.token &&
293
- isNode &&
294
- !isCover &&
295
- co.value &&
296
- ['shift', 'shiftMatch'].includes(co.value.verb)
297
- ) {
298
- if (triviaIsAllowed(s)) {
299
- let tmi = buildEmbeddedInstruction(
300
- triviaIsRequired() ? eat(triviaMatcher) : eatMatch(triviaMatcher),
301
- );
302
- let result = yield buildCall(co.value.verb, m`<__${buildIdentifier(Wrapper_)} />`, [
303
- tmi,
304
- buildEmbeddedInstruction(eat(co.value.arguments[0], co.value.arguments[1])),
305
- ]);
306
-
307
- let trivialResult = result;
308
-
309
- if (result && !result.isNull) {
310
- if (isNode || isCoverBoundary) {
311
- let refMatcher = get('refMatcher', matcher.value);
312
-
313
- let name, isArray;
314
-
315
- if (!refMatcher) {
316
- ({ name, isArray } = mergedReference);
317
- } else {
318
- let name = get('name', refMatcher);
319
- let openIndexToken = get('openIndexToken', refMatcher);
320
- name = name && ctx.sourceTextFor(name);
321
- isArray = !!openIndexToken;
322
- }
323
-
324
- if (name) {
325
- let pathSpec = name;
326
-
327
- if (isArray) {
328
- pathSpec = buildPathSegment(name, -1);
329
- }
330
-
331
- result = result.get([pathSpec]);
332
- }
333
- }
334
-
335
- result = result && result.merge(trivialResult);
336
- }
337
- return result;
338
- }
339
- }
206
+ if (co.value) {
207
+ throw new Error();
340
208
  }
341
-
342
- return co.value;
343
209
  } catch (e) {
344
210
  co.throw(e);
345
211
  throw e;
@@ -348,34 +214,33 @@ export const triviaEnhancer = (
348
214
  }, grammar);
349
215
 
350
216
  return class extends resultGrammar {
351
- constructor() {
352
- super();
353
-
354
- if (!this.emptyables) {
355
- this.emptyables = new Set();
356
- }
357
-
358
- if (!this.covers) {
359
- this.covers = new Map();
360
- }
361
-
362
- if (!this.covers.get(Symbol.for('@bablr/node'))) {
363
- this.covers.set(Symbol.for('@bablr/node'), new Set());
364
- }
365
-
366
- this.covers.get(Symbol.for('@bablr/node')).add(Literal_);
367
-
368
- this.emptyables.add(Wrapper_);
217
+ static get atrivial() {
218
+ return grammar;
369
219
  }
370
220
 
371
- *[Wrapper_]({ props: { value: wrapped } }) {
372
- for (const instr of wrapped) {
373
- yield getEmbeddedInstruction(instr);
221
+ *[Trivia_]({ props: { matchTrailing, matcher, props, options } }) {
222
+ yield tmi;
223
+ let returnValue = yield eat(
224
+ // TODO fixme binding tag
225
+ matcher.value.value.name === Symbol.for('PropertyMatcher')
226
+ ? buildEmbeddedMatcher(
227
+ buildPropertyMatcher(
228
+ null,
229
+ buildBoundNodeMatcher([], get(['valueMatcher', 'nodeMatcher'], matcher.value)),
230
+ ),
231
+ )
232
+ : matcher,
233
+ props,
234
+ buildEmbeddedObject({ ...(options?.value || {}), shift: false }),
235
+ );
236
+ if (matchTrailing || returnValue.value?.shift) {
237
+ yield tmi;
374
238
  }
239
+ return returnValue.value;
375
240
  }
376
241
 
377
242
  *[Literal_]({ literalValue }) {
378
- yield eat(literalValue);
243
+ return r(null, yield eat(buildEmbeddedNode(literalValue)));
379
244
  }
380
245
  };
381
246
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bablr/helpers",
3
3
  "description": "Command helpers for use in writing BABLR grammars",
4
- "version": "0.24.0",
4
+ "version": "0.25.0",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
7
  "files": [
@@ -23,32 +23,29 @@
23
23
  ".": "./lib/index.js"
24
24
  },
25
25
  "sideEffects": false,
26
- "scripts": {
27
- "build": "macrome build",
28
- "watch": "macrome watch",
29
- "clean": "macrome clean"
30
- },
31
26
  "dependencies": {
32
- "@bablr/language_enhancer-debug-log": "0.11.1",
33
- "@bablr/strategy_enhancer-debug-log": "0.10.0",
34
- "@bablr/agast-helpers": "0.9.0",
35
- "@bablr/agast-vm-helpers": "0.9.0",
36
- "@bablr/boot": "0.10.0",
27
+ "@bablr/language_enhancer-debug-log": "0.12.1",
28
+ "@bablr/strategy_enhancer-debug-log": "0.11.0",
29
+ "@bablr/stream-iterator": "2.0.0",
30
+ "@bablr/agast-helpers": "0.10.0",
31
+ "@bablr/agast-vm-helpers": "0.10.0",
32
+ "@bablr/boot": "0.11.0",
37
33
  "@bablr/coroutine": "0.1.0",
38
34
  "@iter-tools/imm-stack": "1.2.0",
39
35
  "iter-tools-es": "^7.5.3"
40
36
  },
41
37
  "devDependencies": {
42
38
  "@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#c97bfa4b3663f8378e9b3e42bb5a41e685406cf9",
43
- "@bablr/macrome": "0.1.3",
44
- "@bablr/macrome-generator-bablr": "0.3.2",
45
39
  "enhanced-resolve": "^5.12.0",
46
40
  "eslint": "^7.32.0",
47
41
  "eslint-import-resolver-enhanced-resolve": "^1.0.5",
48
42
  "eslint-plugin-import": "^2.27.5",
49
43
  "prettier": "^2.6.2"
50
44
  },
51
- "repository": "github:bablr-lang/helpers",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/bablr-lang/helpers.git"
48
+ },
52
49
  "homepage": "https://github.com/bablr-lang/helpers",
53
50
  "license": "MIT"
54
51
  }