@bablr/helpers 0.23.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/decorators.js CHANGED
@@ -53,20 +53,9 @@ export const InjectFrom = (obj) => (_stub, context) => {
53
53
  return obj[context.name];
54
54
  };
55
55
 
56
- export const Node = (desc, context) => {
57
- return CoveredBy(sym.node)(desc, context);
58
- };
59
-
60
56
  export const Attributes = (attributes) => (desc, context) => {
61
57
  context.addInitializer(function () {
62
58
  this.attributes = this.attributes || new Map();
63
59
  this.attributes.set(context.name, attributes);
64
60
  });
65
61
  };
66
-
67
- export const UndefinedAttributes = (attributes) => (desc, context) => {
68
- context.addInitializer(function () {
69
- this.undefinedAttributes = this.undefinedAttributes || new Map();
70
- this.undefinedAttributes.set(context.name, attributes);
71
- });
72
- };
package/lib/grammar.js CHANGED
@@ -1,13 +1,35 @@
1
1
  import every from 'iter-tools-es/methods/every';
2
2
  import isString from 'iter-tools-es/methods/is-string';
3
- import { getOwnPropertySymbols, getPrototypeOf, objectEntries, objectValues } from './object.js';
4
- import { OpenNodeTag, CloseNodeTag, NullTag } from './symbols.js';
5
- import { buildEmbeddedObject } from '@bablr/agast-vm-helpers/builders';
3
+ import { getOwnPropertySymbols, getPrototypeOf, objectEntries } from './object.js';
4
+ import * as BTree from '@bablr/agast-helpers/btree';
5
+ import { OpenNodeTag, CloseNodeTag, NullTag, Matcher } from './symbols.js';
6
+ import {
7
+ buildCall,
8
+ buildEmbeddedMatcher,
9
+ buildEmbeddedObject,
10
+ } from '@bablr/agast-vm-helpers/builders';
11
+ import { freeze, isObject, isPlainObject } from '@bablr/agast-helpers/object';
12
+ import { StreamIterable, wait } from '@bablr/stream-iterator';
13
+ import { get } from '@bablr/agast-helpers/path';
14
+ import { buildBindingMatchers, buildBoundNodeMatcher, buildPropertyMatcher } from './builders.js';
15
+ import { reifyExpression } from '@bablr/agast-vm-helpers';
6
16
 
7
17
  export * from './decorators.js';
8
18
 
19
+ import { EmbeddedObject } from '@bablr/agast-vm-helpers/symbols';
20
+
21
+ export const normalizeProps = (value) => {
22
+ return isPlainObject(value) && value.type === EmbeddedObject && isPlainObject(value.value)
23
+ ? value.value
24
+ : value === undefined
25
+ ? {}
26
+ : { value: value?.type === EmbeddedObject ? value.value : value };
27
+ };
28
+
9
29
  const { getOwnPropertyNames, hasOwn } = Object;
10
30
 
31
+ const matchVerbs = new Set(['eat', 'match', 'eatMatch', 'shift', 'shiftMatch']);
32
+
11
33
  const { isArray } = Array;
12
34
  const isSymbol = (value) => typeof value === 'symbol';
13
35
  const isType = (value) => isString(value) || isSymbol(value);
@@ -62,39 +84,6 @@ export function* generateProductions(Grammar) {
62
84
  }
63
85
  }
64
86
 
65
- export const resolveLanguage = (context, language, path) => {
66
- const { languages } = context;
67
- if (isString(path)) {
68
- if (language.canonicalURL === path) {
69
- return language;
70
- } else {
71
- throw new Error('absolute path resolution not implemented');
72
- }
73
- }
74
-
75
- let l = language;
76
-
77
- if (!l) {
78
- throw new Error();
79
- }
80
-
81
- let segments = isString(path) ? [path] : isArray(path) ? path : null;
82
-
83
- if (path == null) {
84
- return language;
85
- } else {
86
- for (const segment of segments) {
87
- if (isString(l.dependencies[segment])) {
88
- l = languages.get(l.dependencies[segment]);
89
- } else {
90
- l = l.dependencies[segment];
91
- }
92
- }
93
- }
94
-
95
- return l;
96
- };
97
-
98
87
  export const explodeSubtypes = (aliases, exploded, types) => {
99
88
  for (const type of types) {
100
89
  const explodedTypes = aliases.get(type);
@@ -128,6 +117,10 @@ export const buildCovers = (rawAliases) => {
128
117
  return new Map(aliases);
129
118
  };
130
119
 
120
+ export const buildReturnValue = (shift, value) => {
121
+ return freeze({ shift, value });
122
+ };
123
+
131
124
  export const getProduction = (grammar, type) => {
132
125
  return getPrototypeOf(grammar)[type];
133
126
  };
@@ -136,16 +129,16 @@ const __buildDependentLanguages = (language, languages = new Map()) => {
136
129
  languages.set(language.canonicalURL, language);
137
130
 
138
131
  for (const dependentLanguage of Object.values(language.dependencies || {})) {
139
- if (isString(dependentLanguage)) continue;
132
+ if (!isObject(dependentLanguage)) throw new Error();
140
133
 
141
- const { canonicalURL } = dependentLanguage;
142
- if (languages.has(canonicalURL) && dependentLanguage !== languages.get(canonicalURL)) {
143
- throw new Error();
134
+ if (
135
+ languages.has(dependentLanguage.canonicalURL) &&
136
+ languages.get(dependentLanguage.canonicalURL) !== dependentLanguage
137
+ ) {
138
+ throw new Error('url clash');
144
139
  }
145
140
 
146
- if (!languages.has(dependentLanguage)) {
147
- __buildDependentLanguages(dependentLanguage, languages);
148
- }
141
+ __buildDependentLanguages(dependentLanguage, languages);
149
142
  }
150
143
 
151
144
  return languages;
@@ -204,6 +197,138 @@ const __buildCall = (verb, ...args) => {
204
197
  return { verb, arguments: args };
205
198
  };
206
199
 
200
+ export const getInstrMatcher = (instr) => {
201
+ if (matchVerbs.has(instr.verb) && instr.arguments[0].type === Matcher) {
202
+ return instr.arguments[0];
203
+ }
204
+ return null;
205
+ };
206
+
207
+ function* __wrapGenerator(generator, relativeMatcher) {
208
+ // Path.get quietly returns the first item when asked for an array...
209
+ let { bindingMatchers: relativeBindings } = reifyExpression(relativeMatcher.value);
210
+
211
+ if (!relativeBindings || !relativeBindings.length) {
212
+ return yield* generator;
213
+ }
214
+
215
+ let step;
216
+ let returnValue;
217
+ while (!(step = generator.next(returnValue)).done) {
218
+ if (step instanceof Promise) {
219
+ step = yield wait(step);
220
+ }
221
+ let instr = step.value;
222
+ let { verb, arguments: args } = instr;
223
+
224
+ let isMatcher =
225
+ matchVerbs.has(verb) &&
226
+ args[0].type === Matcher &&
227
+ args[0].value.value.name?.description === 'PropertyMatcher';
228
+
229
+ if (isMatcher) {
230
+ let { 0: matcher, 1: props, 2: options } = args;
231
+ let { nodeMatcher, bindingMatchers } = reifyExpression(matcher.value);
232
+
233
+ if ((nodeMatcher.flags.token && !nodeMatcher.name) || !relativeBindings.length) {
234
+ returnValue = yield instr;
235
+ continue;
236
+ }
237
+
238
+ let newMatcher = buildPropertyMatcher(
239
+ get('refMatcher', matcher.value),
240
+ buildBoundNodeMatcher(
241
+ [...relativeBindings, ...bindingMatchers],
242
+ get(['valueMatcher', 'nodeMatcher'], matcher.value),
243
+ ),
244
+ );
245
+
246
+ let extraArgs = [];
247
+ if (props !== undefined || options !== undefined) extraArgs.push(props);
248
+ if (options !== undefined) extraArgs.push(options);
249
+
250
+ let newInstr = buildCall(verb, buildEmbeddedMatcher(newMatcher), ...extraArgs);
251
+ returnValue = yield newInstr;
252
+ } else {
253
+ returnValue = yield instr;
254
+ }
255
+ }
256
+ }
257
+
258
+ export const wrapGenerator = (generator, matcher) => {
259
+ return new StreamIterable(__wrapGenerator(generator, matcher));
260
+ };
261
+
262
+ export function resolveLanguage(languages, path) {
263
+ let languageIdx = BTree.getSize(languages) - 1;
264
+ let language = BTree.getAt(languageIdx, languages);
265
+
266
+ // TODO there may be more than one binding!
267
+ let path_ = Array.isArray(path)
268
+ ? path
269
+ : reifyExpression(path.value).bindingMatchers.flatMap((m) => m.segments);
270
+
271
+ for (let { name, type } of path_) {
272
+ if (name) {
273
+ language = language.dependencies[name];
274
+
275
+ if (!language) return null;
276
+ } else if (type) {
277
+ if (type !== '..') throw new Error();
278
+ languageIdx--;
279
+ language = BTree.getAt(languageIdx, languages);
280
+ } else {
281
+ throw new Error();
282
+ }
283
+ }
284
+
285
+ return language;
286
+ }
287
+
288
+ export function* exec(getState, matcher, value, getGrammar = (g) => g) {
289
+ let matcher_ = reifyExpression(matcher.value);
290
+ let { nodeMatcher } = matcher_;
291
+ let state = getState();
292
+
293
+ let language = resolveLanguage(state.languages, matcher);
294
+
295
+ let { productionEnhancer } = language;
296
+
297
+ let { grammar } = language;
298
+ let grammar_ = getGrammar(grammar);
299
+
300
+ let args = freeze({
301
+ type: nodeMatcher.type,
302
+ name: nodeMatcher.name,
303
+ props: normalizeProps(value),
304
+ getState,
305
+ s: getState,
306
+ flags: nodeMatcher.flags,
307
+ isCover: false,
308
+ isCoverBoundary: false,
309
+ allowEmpty: true,
310
+ grammar,
311
+ matcher: matcher.value,
312
+ literalValue: null,
313
+ });
314
+
315
+ let production = grammar_.prototype[nodeMatcher.name];
316
+
317
+ let enhancedProduction = production;
318
+
319
+ if (productionEnhancer) {
320
+ enhancedProduction = productionEnhancer(enhancedProduction, nodeMatcher.name);
321
+ }
322
+
323
+ // if (rootProductionEnhancer && !m.depth) {
324
+ // enhancedProduction = rootProductionEnhancer(enhancedProduction, nodeMatcher.name);
325
+ // }
326
+
327
+ let generator = enhancedProduction(args);
328
+
329
+ yield* wrapGenerator(generator, matcher);
330
+ }
331
+
207
332
  export const eat = (matcher, value, options) => {
208
333
  return __buildCall('eat', matcher, value, options);
209
334
  };
@@ -220,6 +345,10 @@ export const guard = (matcher, value, options) => {
220
345
  return __buildCall('guard', matcher, value, options);
221
346
  };
222
347
 
348
+ export const shift = (matcher, value, options) => {
349
+ return __buildCall('shift', matcher, value, options);
350
+ };
351
+
223
352
  export const shiftMatch = (matcher, value, options) => {
224
353
  return __buildCall('shiftMatch', matcher, value, options);
225
354
  };
@@ -228,6 +357,10 @@ export const fail = () => {
228
357
  return __buildCall('fail');
229
358
  };
230
359
 
360
+ export const eatHeld = (matcher) => {
361
+ return __buildCall('eatHeld', matcher);
362
+ };
363
+
231
364
  export const defineAttribute = (key, value) => {
232
365
  return __buildCall('defineAttribute', key, value);
233
366
  };
@@ -236,4 +369,14 @@ export const write = (value) => {
236
369
  return __buildCall('write', value);
237
370
  };
238
371
 
372
+ export const startSpan = (name, guard) => {
373
+ return __buildCall('startSpan', name, guard);
374
+ };
375
+
376
+ export const endSpan = () => {
377
+ return __buildCall('endSpan');
378
+ };
379
+
239
380
  export const o = buildEmbeddedObject;
381
+
382
+ export const r = buildReturnValue;
@@ -1,85 +1,69 @@
1
- /* @macrome
2
- * @generatedby @bablr/macrome-generator-bablr
3
- * @generatedfrom ./productions.macro.js#b095c92e30507ebaab43cd0c2b1dcee55853950f
4
- * This file is autogenerated. Please do not edit it directly.
5
- * When editing run `npx macrome watch` then change the file this is generated from.
6
- */
7
- import { spam as m } from '@bablr/boot';
8
- import { getCooked } from '@bablr/agast-helpers/tree';
9
- import { eat, eatMatch } from './grammar.js';
10
- import { EmbeddedMatcher } from './symbols.js';
11
- import { getEmbeddedMatcher } from '@bablr/agast-vm-helpers/deembed';
12
- import { buildPropertyMatcher } from './builders.js';
1
+ import { get, Path } from '@bablr/agast-helpers/path';
2
+ import { eat, eatMatch, fail } from './grammar.js';
3
+ import { Matcher } from './symbols.js';
13
4
  import { buildEmbeddedMatcher } from '@bablr/agast-vm-helpers/builders';
14
- export function* List({
15
- props
16
- }) {
17
- const {
18
- element,
19
- separator,
20
- allowHoles = false,
21
- allowTrailingSeparator = true
22
- } = props;
23
- if (!['#', '@'].includes(getCooked(getEmbeddedMatcher(separator).properties.refMatcher?.node.properties.type?.node))) {
24
- yield eat(buildEmbeddedMatcher(buildPropertyMatcher(getEmbeddedMatcher(separator).properties.refMatcher?.node, null, m.ArrayNodeMatcher`[]`)));
5
+ import { printSource } from '@bablr/agast-helpers/tree';
6
+
7
+ const { isArray } = Array;
8
+
9
+ export function* List(args) {
10
+ let { props, matcher } = args.element ? { props: args } : args;
11
+
12
+ const { element, separator, allowHoles = false, allowTrailingSeparator = true } = props;
13
+ let refMatcher = matcher && get('refMatcher', matcher);
14
+
15
+ let matcher_ = isArray(element) ? element[0] : element;
16
+ let props_ = isArray(element) ? element[1] : undefined;
17
+ let options = isArray(element) ? element[2] : undefined;
18
+
19
+ if (refMatcher && !get('refMatcher', matcher_.value)) {
20
+ matcher_ = buildEmbeddedMatcher(Path.set(matcher_.value, 'refMatcher', refMatcher));
25
21
  }
26
- yield eat(buildEmbeddedMatcher(buildPropertyMatcher(getEmbeddedMatcher(Array.isArray(element) ? element[0] : element).properties.refMatcher?.node, null, m.ArrayNodeMatcher`[]`)));
27
- let sep,
28
- it,
29
- anySep = false;
22
+
23
+ let sep, it;
30
24
  for (;;) {
31
- it = yield eatMatch(...(Array.isArray(element) ? element : [element]));
25
+ it = yield eatMatch(matcher_, props_, options);
32
26
  if (it || allowTrailingSeparator) {
33
27
  sep = yield eatMatch(separator);
34
- anySep ||= sep;
35
28
  } else {
36
29
  sep = null;
37
30
  }
38
31
  if (!(sep || allowHoles)) break;
39
32
  }
40
33
  }
41
- export function* Any({
42
- props: {
43
- value: alternatives
44
- }
45
- }) {
34
+
35
+ export function* Any({ props: { value: alternatives } }) {
46
36
  for (const alternative of alternatives) {
47
- if (Array.isArray(alternative)) {
48
- if (yield eatMatch(...alternative)) break;
49
- } else if (alternative.type === EmbeddedMatcher) {
50
- if (yield eatMatch(alternative)) break;
37
+ if (isArray(alternative)) {
38
+ if (yield eatMatch(...alternative)) return;
39
+ } else if (alternative.type === Matcher) {
40
+ if (yield eatMatch(alternative)) return;
51
41
  } else {
52
42
  throw new Error();
53
43
  }
54
44
  }
45
+ yield fail();
55
46
  }
56
- export function* All({
57
- props: {
58
- value: matchers
59
- }
60
- }) {
47
+
48
+ export function* All({ props: { value: matchers } }) {
61
49
  for (const matcher of matchers) {
62
- if (Array.isArray(matcher)) {
50
+ if (isArray(matcher)) {
63
51
  yield eat(...matcher);
64
52
  } else {
65
53
  yield eat(matcher);
66
54
  }
67
55
  }
68
56
  }
69
- export function* Optional({
70
- props: {
71
- value: matcher
72
- }
73
- }) {
57
+
58
+ export function* Optional({ props: { value: matcher } }) {
74
59
  yield eatMatch(matcher);
75
60
  }
76
- export function* Literal({
77
- ctx,
78
- intrinsicValue
79
- }) {
80
- if (!intrinsicValue) throw new Error('Intrinsic productions must have value');
81
- yield eat(ctx.sourceTextFor(intrinsicValue.value));
61
+
62
+ export function* Literal({ literalValue }) {
63
+ if (!literalValue) throw new Error('Intrinsic productions must have value');
64
+
65
+ yield eat(printSource(literalValue.value));
82
66
  }
83
67
  export const Keyword = Literal;
84
- export const Punctuator = Literal;
85
- export const Space = Literal;
68
+
69
+ export const Space = Literal;
package/lib/shorthand.js CHANGED
@@ -1 +1 @@
1
- export { i, re, spam } from '@bablr/boot';
1
+ export { i, re, spam, t, cst } from '@bablr/boot';
package/lib/source.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import slice from 'iter-tools-es/methods/slice';
2
2
  import { streamFromTree } from '@bablr/agast-helpers/tree';
3
- import { StreamIterable, getStreamIterator } from '@bablr/agast-helpers/stream';
3
+ import { StreamIterable, getStreamIterator, wait } from '@bablr/agast-helpers/stream';
4
4
  import { ShiftTag, GapTag, LiteralTag } from './symbols.js';
5
5
 
6
6
  const escapables = {
@@ -15,7 +15,7 @@ function* __readFromStream(stream) {
15
15
  let step;
16
16
 
17
17
  for (;;) {
18
- step = yield iter.next();
18
+ step = yield wait(iter.next());
19
19
 
20
20
  if (step.done) break;
21
21
 
@@ -23,7 +23,10 @@ function* __readFromStream(stream) {
23
23
  }
24
24
 
25
25
  if (!step.done) {
26
- iter?.return();
26
+ step = iter?.return();
27
+ if (step instanceof Promise) {
28
+ step = yield wait(step);
29
+ }
27
30
  }
28
31
  }
29
32
 
@@ -42,7 +45,7 @@ function* __embeddedSourceFrom(iterable) {
42
45
  step = iter.next();
43
46
 
44
47
  if (step instanceof Promise) {
45
- step = yield step;
48
+ step = yield wait(step);
46
49
  }
47
50
 
48
51
  if (step.done) break;
@@ -86,7 +89,11 @@ function* __embeddedSourceFrom(iterable) {
86
89
  }
87
90
 
88
91
  if (!step.done) {
89
- iter?.return();
92
+ step = iter?.return();
93
+
94
+ if (step instanceof Promise) {
95
+ step = yield wait(step);
96
+ }
90
97
  }
91
98
  }
92
99
 
@@ -101,7 +108,7 @@ function* __printEmbeddedSource(chrs) {
101
108
  step = iter.next();
102
109
 
103
110
  if (step instanceof Promise) {
104
- step = yield step;
111
+ step = yield wait(step);
105
112
  }
106
113
 
107
114
  if (step.done) break;
@@ -142,7 +149,7 @@ function* __sourceFromTokenStream(tags) {
142
149
  step = iter.next();
143
150
 
144
151
  if (step instanceof Promise) {
145
- step = yield step;
152
+ step = yield wait(step);
146
153
  }
147
154
 
148
155
  if (step.done) break;
@@ -168,7 +175,7 @@ function* __sourceFromQuasis(quasis) {
168
175
  step = iter.next();
169
176
 
170
177
  if (step instanceof Promise) {
171
- step = yield step;
178
+ step = yield wait(step);
172
179
  }
173
180
 
174
181
  if (step.done) break;
@@ -192,7 +199,7 @@ export function* fillGapsWith(expressions, iterable) {
192
199
  let step = iter.next();
193
200
 
194
201
  if (step instanceof Promise) {
195
- step = yield step;
202
+ step = yield wait(step);
196
203
  }
197
204
 
198
205
  if (step.done) break;
@@ -231,7 +238,7 @@ function* __stripTrailingNewline(iterable) {
231
238
 
232
239
  for (;;) {
233
240
  if (step instanceof Promise) {
234
- step = yield step;
241
+ step = yield wait(step);
235
242
  }
236
243
 
237
244
  // TODO: handle \r\n line endings