@bablr/helpers 0.1.8 → 0.15.1

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
@@ -1,8 +1,51 @@
1
- export * from '@bablr/boot-helpers/decorators';
1
+ import * as sym from './symbols.js';
2
2
 
3
- export const UnboundAttributes = (attributes) => (desc, context) => {
3
+ export const AllowEmpty = (desc, context) => {
4
4
  context.addInitializer(function () {
5
- this.unboundAttributes = this.unboundAttributes || new Map();
6
- this.unboundAttributes.set(context.name, attributes);
5
+ let emptyables = this.emptyables;
6
+
7
+ if (!emptyables) {
8
+ emptyables = this.emptyables = new Set();
9
+ }
10
+
11
+ emptyables.add(context.name);
12
+ });
13
+ };
14
+
15
+ export const CoveredBy = (type) => (desc, context) => {
16
+ context.addInitializer(function () {
17
+ let covers = this.covers;
18
+
19
+ if (!covers) {
20
+ covers = this.covers = new Map();
21
+ }
22
+
23
+ let coveredTypes = covers.get(type);
24
+
25
+ if (!coveredTypes) {
26
+ coveredTypes = new Set();
27
+ covers.set(type, coveredTypes);
28
+ }
29
+
30
+ coveredTypes.add(context.name);
31
+ });
32
+ };
33
+
34
+ export const InjectFrom = (obj) => (_stub, context) => {
35
+ if (!Object.hasOwn(obj, context.name)) {
36
+ throw new Error('Bad injection');
37
+ }
38
+
39
+ return obj[context.name];
40
+ };
41
+
42
+ export const Node = (desc, context) => {
43
+ return CoveredBy(sym.node)(desc, context);
44
+ };
45
+
46
+ export const Attributes = (attributes) => (desc, context) => {
47
+ context.addInitializer(function () {
48
+ this.attributes = this.attributes || new Map();
49
+ this.attributes.set(context.name, attributes);
7
50
  });
8
51
  };
package/lib/enhancers.js CHANGED
@@ -1,3 +1,11 @@
1
+ import { Coroutine } from '@bablr/coroutine';
2
+ import {
3
+ enhanceStrategyBuilderWithDebugLogging as logStrategy,
4
+ enhanceStrategyBuilderWithEmittedLogging as logEmitted,
5
+ } from '@bablr/strategy_enhancer-debug-log';
6
+ import { enhanceProductionWithDebugLogging as createProductionLogger } from '@bablr/language_enhancer-debug-log';
7
+ import { getPrototypeOf } from './object.js';
8
+
1
9
  const { getOwnPropertyNames, hasOwn } = Object;
2
10
 
3
11
  export const memoize = (original) => {
@@ -46,8 +54,45 @@ export const mapProductions = (fn, Grammar) => {
46
54
  mapped[key] = fn(prototype[key], key);
47
55
  }
48
56
  }
49
- ({ prototype } = prototype);
57
+ prototype = getPrototypeOf(prototype);
50
58
  }
51
59
 
52
60
  return MappedGrammar;
53
61
  };
62
+
63
+ export const buildDebugEnhancers = (log) => {
64
+ return {
65
+ agastStrategy: (strategy) => logEmitted(strategy, '>>> ', log),
66
+ bablrStrategy: (strategy) => logStrategy(strategy, ' ', log),
67
+ bablrProduction: createProductionLogger(' ', log),
68
+ };
69
+ };
70
+
71
+ export const debugEnhancers = buildDebugEnhancers();
72
+
73
+ export const stateEnhancer = (hooks, grammar) => {
74
+ let state;
75
+ return mapProductions((production) => {
76
+ return function* (props) {
77
+ let prevState = props.state;
78
+
79
+ if (!state) {
80
+ hooks.buildState?.(props.state);
81
+ } else if (props.state !== state) {
82
+ hooks.branchState?.(state, props.state);
83
+ }
84
+
85
+ state = props.state;
86
+
87
+ try {
88
+ yield* production(props);
89
+
90
+ hooks.acceptState?.(props.state, state);
91
+ } catch (e) {
92
+ hooks.rejectState?.(props.state);
93
+ }
94
+
95
+ state = prevState;
96
+ };
97
+ }, grammar);
98
+ };
package/lib/grammar.js CHANGED
@@ -6,6 +6,28 @@ const { isArray } = Array;
6
6
  const isSymbol = (value) => typeof value === 'symbol';
7
7
  const isType = (value) => isString(value) || isSymbol(value);
8
8
 
9
+ export const resolveLanguage = (language, path) => {
10
+ let l = language;
11
+
12
+ if (!l) {
13
+ throw new Error();
14
+ }
15
+
16
+ if (path == null) {
17
+ return language;
18
+ } else if (isString(path)) {
19
+ l = l.dependencies[path];
20
+ } else if (isArray(path)) {
21
+ for (const segment of path) {
22
+ l = l.dependencies[segment];
23
+ }
24
+ } else {
25
+ throw new Error();
26
+ }
27
+
28
+ return l;
29
+ };
30
+
9
31
  export const explodeSubtypes = (aliases, exploded, types) => {
10
32
  for (const type of types) {
11
33
  const explodedTypes = aliases.get(type);
@@ -43,65 +65,52 @@ export const getProduction = (grammar, type) => {
43
65
  return getPrototypeOf(grammar)[type];
44
66
  };
45
67
 
46
- export class Coroutine {
47
- static from(grammar, type, props) {
48
- const production = grammar.get(type);
49
-
50
- if (!production) throw new Error(`Unknown production of {type: ${type}}`);
68
+ const __buildDependentLanguages = (language, languages = new Map()) => {
69
+ for (const dependentLanguage of Object.values(language.dependencies || {})) {
70
+ const { canonicalURL } = dependentLanguage;
71
+ if (languages.has(canonicalURL) && dependentLanguage !== languages.get(canonicalURL)) {
72
+ throw new Error();
73
+ }
51
74
 
52
- return new Coroutine(production.match(props));
75
+ if (!languages.has(dependentLanguage)) {
76
+ __buildDependentLanguages(dependentLanguage, languages);
77
+ }
53
78
  }
79
+ languages.set(language.canonicalURL, language);
54
80
 
55
- constructor(generator) {
56
- this.generator = generator;
57
- }
81
+ return languages;
82
+ };
58
83
 
59
- get value() {
60
- return this.current.value;
61
- }
84
+ export const buildDependentLanguages = (language, transformLanguage = (l) => l) => {
85
+ return __buildDependentLanguages(language);
86
+ };
62
87
 
63
- get done() {
64
- return this.current?.done;
65
- }
88
+ const arrayLast = (arr) => arr[arr.length - 1];
66
89
 
67
- advance(value) {
68
- if (this.done) {
69
- throw new Error('Cannot advance a coroutine that is done');
70
- }
71
- this.current = this.generator.next(value);
72
- return this;
73
- }
90
+ export function* zipLanguages(tokens, rootLanguage) {
91
+ const languages = [rootLanguage];
74
92
 
75
- return(value) {
76
- if (!this.done) {
77
- this.current = this.generator.return(value);
78
- } else {
79
- return this.current;
80
- }
81
- }
93
+ for (const token of tokens) {
94
+ switch (token.type) {
95
+ case 'OpenNodeTag': {
96
+ if (token.value.language) {
97
+ const dependentLanguage = languages.dependencies[token.value.language];
82
98
 
83
- throw(value) {
84
- if (!this.done) {
85
- this.current = { value: undefined, done: true };
99
+ if (!dependentLanguage) throw new Error('language was not a dependency');
86
100
 
87
- let caught = false;
88
- try {
89
- this.generator.throw(value);
90
- } catch (e) {
91
- caught = true;
101
+ languages.push(dependentLanguage);
102
+ }
103
+ break;
92
104
  }
93
- if (!caught) {
94
- throw new Error('Generator attempted to yield a command after failing');
105
+
106
+ case 'CloseNodeTag': {
107
+ if (token.value.language !== arrayLast(languages).canonicalURL) {
108
+ languages.pop();
109
+ }
110
+ break;
95
111
  }
96
- } else {
97
- throw value;
98
112
  }
99
- }
100
113
 
101
- finalize() {
102
- // ensures failures can be logged!
103
- if (!this.done) {
104
- this.return();
105
- }
114
+ yield [token, arrayLast(languages)];
106
115
  }
107
116
  }
@@ -1,58 +1,109 @@
1
- import { i } from '@bablr/boot/shorthand.macro';
2
-
3
- export function* List(props, s, ctx) {
1
+ /* @macrome
2
+ * @generatedby @bablr/macrome-generator-bablr
3
+ * @generatedfrom ./productions.macro.js#f1d7575719a2690bc530f7c4dd1607fd8a4bdc7d
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 { interpolateString as _interpolateString } from "@bablr/agast-helpers/template";
8
+ import { interpolateArrayChildren as _interpolateArrayChildren } from "@bablr/agast-helpers/template";
9
+ import { interpolateArray as _interpolateArray } from "@bablr/agast-helpers/template";
10
+ import * as _l from "@bablr/agast-vm-helpers/languages";
11
+ import * as _t from "@bablr/agast-helpers/shorthand";
12
+ export function* List({
13
+ value: props,
14
+ ctx
15
+ }) {
4
16
  const {
5
17
  element,
6
18
  separator,
7
19
  allowHoles = false,
8
- allowTrailingSeparator = true,
20
+ allowTrailingSeparator = true
9
21
  } = ctx.unbox(props);
10
-
11
22
  let sep, it;
12
23
  for (;;) {
13
- it = yield i`eatMatch(${element} 'elements[]')`;
24
+ it = yield _t.node(_l.Instruction, "Call", [_t.ref`verb`, _t.ref`arguments`], {
25
+ verb: _t.s_node(_l.Instruction, "Identifier", "eatMatch"),
26
+ arguments: _t.node(_l.Instruction, "Tuple", [_t.ref`open`, ..._interpolateArrayChildren(element, _t.ref`values[]`, _t.embedded(_t.t_node(_l.Comment, null, [_t.embedded(_t.t_node('Space', 'Space', [_t.lit(' ')]))]))), _t.ref`close`], {
27
+ open: _t.s_i_node(_l.Instruction, "Punctuator", "("),
28
+ values: [..._interpolateArray(element)],
29
+ close: _t.s_i_node(_l.Instruction, "Punctuator", ")")
30
+ }, {})
31
+ }, {});
14
32
  if (it || allowTrailingSeparator) {
15
- sep = yield i`eatMatch(${separator} 'separators[]')`;
33
+ sep = yield _t.node(_l.Instruction, "Call", [_t.ref`verb`, _t.ref`arguments`], {
34
+ verb: _t.s_node(_l.Instruction, "Identifier", "eatMatch"),
35
+ arguments: _t.node(_l.Instruction, "Tuple", [_t.ref`open`, ..._interpolateArrayChildren(separator, _t.ref`values[]`, _t.embedded(_t.t_node(_l.Comment, null, [_t.embedded(_t.t_node('Space', 'Space', [_t.lit(' ')]))]))), _t.embedded(_t.s_t_node(_l.Space, "Space", [_t.lit(" ")], {}, {})), _t.ref`values[]`, _t.ref`close`], {
36
+ open: _t.s_i_node(_l.Instruction, "Punctuator", "("),
37
+ values: [..._interpolateArray(separator), _t.node(_l.CSTML, "String", [_t.ref`open`, _t.ref`content`, _t.ref`close`], {
38
+ open: _t.s_i_node(_l.CSTML, "Punctuator", "'"),
39
+ content: _t.node(_l.CSTML, "Content", [_t.lit("separators[]")], {}, {}),
40
+ close: _t.s_i_node(_l.CSTML, "Punctuator", "'")
41
+ }, {})],
42
+ close: _t.s_i_node(_l.Instruction, "Punctuator", ")")
43
+ }, {})
44
+ }, {});
16
45
  }
17
46
  if (!(sep || allowHoles)) break;
18
47
  }
19
48
  }
20
-
21
- export function* Any(matchers, s, ctx) {
49
+ export function* Any({
50
+ value: matchers,
51
+ ctx
52
+ }) {
22
53
  for (const matcher of ctx.unbox(matchers)) {
23
- if (yield i`eatMatch(${matcher})`) break;
54
+ if (yield _t.node(_l.Instruction, "Call", [_t.ref`verb`, _t.ref`arguments`], {
55
+ verb: _t.s_node(_l.Instruction, "Identifier", "eatMatch"),
56
+ arguments: _t.node(_l.Instruction, "Tuple", [_t.ref`open`, ..._interpolateArrayChildren(matcher, _t.ref`values[]`, _t.embedded(_t.t_node(_l.Comment, null, [_t.embedded(_t.t_node('Space', 'Space', [_t.lit(' ')]))]))), _t.ref`close`], {
57
+ open: _t.s_i_node(_l.Instruction, "Punctuator", "("),
58
+ values: [..._interpolateArray(matcher)],
59
+ close: _t.s_i_node(_l.Instruction, "Punctuator", ")")
60
+ }, {})
61
+ }, {})) break;
24
62
  }
25
63
  }
26
-
27
- export function* All(matchers, s, ctx) {
64
+ export function* All({
65
+ value: matchers,
66
+ ctx
67
+ }) {
28
68
  for (const matcher of ctx.unbox(matchers)) {
29
- yield i`eat(${matcher})`;
69
+ yield _t.node(_l.Instruction, "Call", [_t.ref`verb`, _t.ref`arguments`], {
70
+ verb: _t.s_node(_l.Instruction, "Identifier", "eat"),
71
+ arguments: _t.node(_l.Instruction, "Tuple", [_t.ref`open`, ..._interpolateArrayChildren(matcher, _t.ref`values[]`, _t.embedded(_t.t_node(_l.Comment, null, [_t.embedded(_t.t_node('Space', 'Space', [_t.lit(' ')]))]))), _t.ref`close`], {
72
+ open: _t.s_i_node(_l.Instruction, "Punctuator", "("),
73
+ values: [..._interpolateArray(matcher)],
74
+ close: _t.s_i_node(_l.Instruction, "Punctuator", ")")
75
+ }, {})
76
+ }, {});
30
77
  }
31
78
  }
32
-
33
- export function* Match(cases, s, ctx) {
34
- for (const case_ of ctx.unbox(cases)) {
35
- const { 0: matcher, 1: guard } = ctx.unbox(case_);
36
- if (yield i`match(${guard})`) {
37
- yield i`eat(${matcher})`;
38
- break;
39
- }
40
- }
79
+ export function* Punctuator({
80
+ intrinsicValue
81
+ }) {
82
+ if (!intrinsicValue) throw new Error('Intrinsic productions must have value');
83
+ yield _t.node(_l.Instruction, "Call", [_t.ref`verb`, _t.ref`arguments`], {
84
+ verb: _t.s_node(_l.Instruction, "Identifier", "eat"),
85
+ arguments: _t.node(_l.Instruction, "Tuple", [_t.ref`open`, ..._interpolateArrayChildren(intrinsicValue, _t.ref`values[]`, _t.embedded(_t.t_node(_l.Comment, null, [_t.embedded(_t.t_node('Space', 'Space', [_t.lit(' ')]))]))), _t.ref`close`], {
86
+ open: _t.s_i_node(_l.Instruction, "Punctuator", "("),
87
+ values: [..._interpolateArray(intrinsicValue)],
88
+ close: _t.s_i_node(_l.Instruction, "Punctuator", ")")
89
+ }, {})
90
+ }, {});
41
91
  }
42
-
43
- export function* Punctuator(obj, s, ctx) {
44
- const { value, attrs } = ctx.unbox(obj);
45
- yield i`eat(${value})`;
46
-
47
- return { attrs };
48
- }
49
-
50
92
  export const Keyword = Punctuator;
51
-
52
- export function* Optional(matchers, s, ctx) {
93
+ export function* Optional({
94
+ value: matchers,
95
+ ctx
96
+ }) {
53
97
  const matchers_ = ctx.unbox(matchers);
54
98
  if (matchers_.length > 1) {
55
99
  throw new Error('Optional only allows one matcher');
56
100
  }
57
- yield i`eatMatch(${matchers_[0]})`;
58
- }
101
+ yield _t.node(_l.Instruction, "Call", [_t.ref`verb`, _t.ref`arguments`], {
102
+ verb: _t.s_node(_l.Instruction, "Identifier", "eatMatch"),
103
+ arguments: _t.node(_l.Instruction, "Tuple", [_t.ref`open`, ..._interpolateArrayChildren(matchers_[0], _t.ref`values[]`, _t.embedded(_t.t_node(_l.Comment, null, [_t.embedded(_t.t_node('Space', 'Space', [_t.lit(' ')]))]))), _t.ref`close`], {
104
+ open: _t.s_i_node(_l.Instruction, "Punctuator", "("),
105
+ values: [..._interpolateArray(matchers_[0])],
106
+ close: _t.s_i_node(_l.Instruction, "Punctuator", ")")
107
+ }, {})
108
+ }, {});
109
+ }
@@ -0,0 +1,47 @@
1
+ import { i } from '@bablr/boot/shorthand.macro';
2
+
3
+ export function* List({ value: props, ctx }) {
4
+ const {
5
+ element,
6
+ separator,
7
+ allowHoles = false,
8
+ allowTrailingSeparator = true,
9
+ } = ctx.unbox(props);
10
+
11
+ let sep, it;
12
+ for (;;) {
13
+ it = yield i`eatMatch(${element})`;
14
+ if (it || allowTrailingSeparator) {
15
+ sep = yield i`eatMatch(${separator} 'separators[]')`;
16
+ }
17
+ if (!(sep || allowHoles)) break;
18
+ }
19
+ }
20
+
21
+ export function* Any({ value: matchers, ctx }) {
22
+ for (const matcher of ctx.unbox(matchers)) {
23
+ if (yield i`eatMatch(${matcher})`) break;
24
+ }
25
+ }
26
+
27
+ export function* All({ value: matchers, ctx }) {
28
+ for (const matcher of ctx.unbox(matchers)) {
29
+ yield i`eat(${matcher})`;
30
+ }
31
+ }
32
+
33
+ export function* Punctuator({ intrinsicValue }) {
34
+ if (!intrinsicValue) throw new Error('Intrinsic productions must have value');
35
+
36
+ yield i`eat(${intrinsicValue})`;
37
+ }
38
+
39
+ export const Keyword = Punctuator;
40
+
41
+ export function* Optional({ value: matchers, ctx }) {
42
+ const matchers_ = ctx.unbox(matchers);
43
+ if (matchers_.length > 1) {
44
+ throw new Error('Optional only allows one matcher');
45
+ }
46
+ yield i`eatMatch(${matchers_[0]})`;
47
+ }
package/lib/source.js ADDED
@@ -0,0 +1,173 @@
1
+ import slice from 'iter-tools-es/methods/slice';
2
+ import { streamFromTree } from '@bablr/agast-helpers/tree';
3
+ import { StreamIterable, getStreamIterator } from '@bablr/agast-helpers/stream';
4
+
5
+ const escapables = {
6
+ n: '\n',
7
+ r: '\r',
8
+ t: '\t',
9
+ 0: '\0',
10
+ };
11
+
12
+ function* __readStreamAsStreamIterator(stream) {
13
+ let iter = stream[Symbol.asyncIterator]();
14
+ let step;
15
+
16
+ for (;;) {
17
+ step = yield iter.next();
18
+
19
+ if (step.done) break;
20
+
21
+ yield* step.value;
22
+ }
23
+
24
+ if (!step.done) {
25
+ iter?.return();
26
+ }
27
+ }
28
+
29
+ const readStreamAsStreamIterator = (stream) =>
30
+ new StreamIterable(__readStreamAsStreamIterator(stream));
31
+
32
+ const gapStr = '<//>';
33
+
34
+ function* __sourceFromReadStream(stream) {
35
+ let iter = getStreamIterator(readStreamAsStreamIterator(stream));
36
+ let step;
37
+ let escape = false;
38
+ let gapMatchIdx = 0;
39
+ let quote = null;
40
+
41
+ for (;;) {
42
+ step = iter.next();
43
+
44
+ if (step instanceof Promise) {
45
+ step = yield step;
46
+ }
47
+
48
+ if (step.done) break;
49
+
50
+ const chr = step.value;
51
+
52
+ if (escape) {
53
+ if (chr === "'" || chr === '"' || chr === '\\') {
54
+ yield chr;
55
+ } else if (escapables[chr]) {
56
+ yield escapables[chr];
57
+ } else {
58
+ throw new Error();
59
+ }
60
+ escape = false;
61
+ } else {
62
+ if (!quote && chr === gapStr[gapMatchIdx]) {
63
+ gapMatchIdx++;
64
+ if (gapMatchIdx === gapStr.length) {
65
+ yield null;
66
+ gapMatchIdx = 0;
67
+ }
68
+ } else {
69
+ if (gapMatchIdx > 0) {
70
+ throw new Error();
71
+ }
72
+
73
+ if (!quote && (chr === '"' || chr === "'")) {
74
+ quote = chr;
75
+ } else if (quote && chr === quote) {
76
+ quote = null;
77
+ } else if (quote && chr === '\\') {
78
+ escape = true;
79
+ } else if (quote) {
80
+ yield chr;
81
+ } else if (!/[\s]/.test(chr)) {
82
+ throw new Error('unkown syntax');
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ if (!step.done) {
89
+ iter?.return();
90
+ }
91
+ }
92
+
93
+ export const sourceFromReadStream = (stream) => new StreamIterable(__sourceFromReadStream(stream));
94
+
95
+ function* __sourceFromTokenStream(terminals) {
96
+ let iter = getStreamIterator(terminals);
97
+ let step;
98
+
99
+ for (;;) {
100
+ step = iter.next();
101
+
102
+ if (step instanceof Promise) {
103
+ yield step;
104
+ }
105
+
106
+ if (step.done) break;
107
+
108
+ const terminal = step.value;
109
+
110
+ if (terminal.type === 'Literal') {
111
+ yield* terminal.value;
112
+ } else if (terminal.type === 'Gap') {
113
+ yield null;
114
+ }
115
+ }
116
+ }
117
+
118
+ export const sourceFromTokenStream = (terminals) =>
119
+ new StreamIterable(__sourceFromTokenStream(terminals));
120
+
121
+ function* __sourceFromQuasis(quasis) {
122
+ let first = true;
123
+ let iter = getStreamIterator(quasis) || quasis[Symbol.iterator]();
124
+ let step;
125
+
126
+ for (;;) {
127
+ step = iter.next();
128
+
129
+ if (step instanceof Promise) {
130
+ step = yield step;
131
+ }
132
+
133
+ if (step.done) break;
134
+
135
+ const quasi = step.value;
136
+
137
+ if (!first) yield null;
138
+ yield* quasi;
139
+ first = false;
140
+ }
141
+ }
142
+
143
+ export const sourceFromQuasis = (quasis) => new StreamIterable(__sourceFromQuasis(quasis));
144
+
145
+ export function* fillGapsWith(expressions, stream) {
146
+ let exprIdx = 0;
147
+ let iter = getStreamIterator(stream);
148
+ let step;
149
+
150
+ for (;;) {
151
+ let step = iter.next();
152
+
153
+ if (step instanceof Promise) {
154
+ step = yield step;
155
+ }
156
+
157
+ if (step.done) break;
158
+
159
+ const token = step.value;
160
+
161
+ if (token.type === 'Gap') {
162
+ if (exprIdx >= expressions.length) throw new Error('not enough gaps for expressions');
163
+ yield* slice(2, -1, streamFromTree(expressions[exprIdx]));
164
+ exprIdx++;
165
+ } else {
166
+ yield token;
167
+ }
168
+ }
169
+
170
+ if (exprIdx !== expressions.length) {
171
+ throw new Error('too many expressions for gaps');
172
+ }
173
+ }
package/lib/trivia.js CHANGED
@@ -1,10 +1,14 @@
1
- import { getCooked } from './token.js';
1
+ import { Coroutine } from '@bablr/coroutine';
2
+ import { reifyExpression } from '@bablr/agast-vm-helpers';
2
3
  import { mapProductions } from './enhancers.js';
3
- import { Coroutine } from './grammar.js';
4
4
 
5
5
  const lookbehind = (context, s) => {
6
6
  let token = s.result;
7
- while (['OpenNode', 'CloseNode', 'OpenFragment', 'ReferenceTag'].includes(token.type)) {
7
+ while (
8
+ token &&
9
+ ['OpenNodeTag', 'CloseNodeTag', 'OpenFragmentTag', 'Reference'].includes(token.type) &&
10
+ (token.type !== 'OpenNodeTag' || !token.value.intrinsicValue)
11
+ ) {
8
12
  const prevToken = context.getPreviousTerminal(token);
9
13
  if (!prevToken) break;
10
14
  token = prevToken;
@@ -17,62 +21,50 @@ const matchedResults = new WeakSet();
17
21
 
18
22
  export const triviaEnhancer = ({ triviaIsAllowed, eatMatchTrivia }, grammar) => {
19
23
  return mapProductions((production) => {
20
- return function* (props, s, ctx, ...args) {
21
- const co = new Coroutine(production(props, s, ctx, ...args));
24
+ return function* (props) {
25
+ const co = new Coroutine(production(props));
26
+ const { s, ctx } = props;
22
27
 
23
28
  co.advance();
24
29
 
25
- let isTerminal = false;
26
-
27
30
  try {
28
31
  while (!co.done) {
29
- const instr = co.value;
32
+ const sourceInstr = co.value;
33
+ const instr = reifyExpression(sourceInstr);
34
+ const { verb, arguments: { 0: matcher } = [] } = instr;
30
35
  let returnValue = undefined;
31
36
 
32
- const {
33
- verb: verbToken,
34
- verbSuffix: verbSuffixToken,
35
- arguments: {
36
- properties: { values: { 0: matcher } = [] },
37
- },
38
- } = instr.properties;
39
- const verb = getCooked(verbToken);
40
- const verbSuffix = verbSuffixToken && getCooked(verbSuffixToken);
41
-
42
37
  switch (verb) {
43
38
  case 'eat':
44
39
  case 'eatMatch':
45
40
  case 'match':
46
41
  case 'guard': {
47
- if (verbSuffix !== '#') {
42
+ if (matcher && matcher.type && !matcher.flags.trivia) {
48
43
  const previous = lookbehind(ctx, s);
49
- if (triviaIsAllowed(s) && !matchedResults.has(previous)) {
44
+ if (triviaIsAllowed(s) && (!previous || !matchedResults.has(previous))) {
50
45
  matchedResults.add(previous);
51
- yield eatMatchTrivia;
46
+ yield* eatMatchTrivia();
52
47
  matchedResults.add(s.result);
53
48
  }
54
- if (['String', 'Pattern'].includes(matcher.type)) {
55
- isTerminal = true;
56
- }
57
49
  }
58
50
 
59
- returnValue = returnValue || (yield instr);
51
+ returnValue = returnValue || (yield sourceInstr);
60
52
  break;
61
53
  }
62
54
 
63
55
  default:
64
- returnValue = yield instr;
56
+ returnValue = yield sourceInstr;
65
57
  break;
66
58
  }
67
59
 
68
60
  co.advance(returnValue);
69
61
  }
70
62
 
71
- if (!isTerminal) {
63
+ if (!s.node?.flags.token) {
72
64
  const previous = lookbehind(ctx, s);
73
65
  if (triviaIsAllowed(s) && !matchedResults.has(previous)) {
74
66
  matchedResults.add(previous);
75
- yield eatMatchTrivia;
67
+ yield* eatMatchTrivia();
76
68
  matchedResults.add(s.result);
77
69
  }
78
70
  }
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.1.8",
4
+ "version": "0.15.1",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
7
  "files": [
@@ -13,20 +13,24 @@
13
13
  "./grammar": "./lib/grammar.js",
14
14
  "./object": "./lib/object.js",
15
15
  "./path": "./lib/path.js",
16
- "./productions": "./lib/productions.generated.js",
16
+ "./productions": "./lib/productions.js",
17
17
  "./shorthand": "./lib/shorthand.js",
18
+ "./source": "./lib/source.js",
18
19
  "./symbols": "./lib/symbols.js",
19
- "./token": "./lib/token.js",
20
20
  "./trivia": "./lib/trivia.js"
21
21
  },
22
22
  "sideEffects": false,
23
23
  "scripts": {
24
- "generate": "babel lib/productions.js --out-file lib/productions.generated.js",
25
- "clean": "rm lib/productions.generated.js"
24
+ "build": "macrome build",
25
+ "watch": "macrome watch",
26
+ "clean": "macrome clean"
26
27
  },
27
28
  "dependencies": {
28
- "@bablr/boot": "0.1.9",
29
- "@bablr/boot-helpers": "0.1.3",
29
+ "@bablr/language_enhancer-debug-log": "0.2.1",
30
+ "@bablr/strategy_enhancer-debug-log": "0.1.1",
31
+ "@bablr/agast-helpers": "0.1.1",
32
+ "@bablr/agast-vm-helpers": "0.1.1",
33
+ "@bablr/coroutine": "0.1.0",
30
34
  "iter-tools-es": "^7.5.3"
31
35
  },
32
36
  "devDependencies": {
@@ -36,7 +40,10 @@
36
40
  "@babel/plugin-transform-modules-commonjs": "^7.23.0",
37
41
  "@babel/plugin-transform-runtime": "^7.22.15",
38
42
  "@babel/runtime": "^7.23.2",
39
- "@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#c040bc95f2dddac160628e4757a412016044b3ef",
43
+ "@bablr/boot": "0.2.1",
44
+ "@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#d834ccc52795d6c3b96ecc6c419960fceed221a6",
45
+ "@bablr/macrome": "0.1.0",
46
+ "@bablr/macrome-generator-bablr": "0.2.0",
40
47
  "babel-plugin-macros": "3.1.0",
41
48
  "enhanced-resolve": "^5.12.0",
42
49
  "eslint": "^7.32.0",
package/lib/path.js DELETED
@@ -1 +0,0 @@
1
- export * from '@bablr/boot-helpers/path';
@@ -1,123 +0,0 @@
1
- import { interpolateString as _interpolateString } from "@bablr/boot-helpers/template";
2
- import { interpolateArray as _interpolateArray } from "@bablr/boot-helpers/template";
3
- import * as _t from "@bablr/boot-helpers/types";
4
- export function* List(props, s, ctx) {
5
- const {
6
- element,
7
- separator,
8
- allowHoles = false,
9
- allowTrailingSeparator = true
10
- } = ctx.unbox(props);
11
- let sep, it;
12
- for (;;) {
13
- it = yield _t.node("Instruction", "Call", [_t.ref`verb`, _t.ref`arguments`], {
14
- verb: _t.node("Instruction", "Identifier", [_t.lit`eatMatch`], {}, {}),
15
- arguments: _t.node("Instruction", "Tuple", [_t.ref`open`, _t.ref`values[]`, _t.trivia` `, _t.ref`values[]`, _t.ref`close`], {
16
- open: _t.node("Instruction", "Punctuator", [_t.lit`(`], {}, {}),
17
- values: [..._interpolateArray(element), _t.node("String", "String", [_t.ref`open`, _t.ref`content`, _t.ref`close`], {
18
- open: _t.node("String", "Punctuator", [_t.lit`'`], {}, {}),
19
- content: _t.node("String", "Content", [_t.lit`elements[]`], {}, {}),
20
- close: _t.node("String", "Punctuator", [_t.lit`'`], {}, {})
21
- }, {})],
22
- close: _t.node("Instruction", "Punctuator", [_t.lit`)`], {}, {})
23
- }, {})
24
- }, {});
25
- if (it || allowTrailingSeparator) {
26
- sep = yield _t.node("Instruction", "Call", [_t.ref`verb`, _t.ref`arguments`], {
27
- verb: _t.node("Instruction", "Identifier", [_t.lit`eatMatch`], {}, {}),
28
- arguments: _t.node("Instruction", "Tuple", [_t.ref`open`, _t.ref`values[]`, _t.trivia` `, _t.ref`values[]`, _t.ref`close`], {
29
- open: _t.node("Instruction", "Punctuator", [_t.lit`(`], {}, {}),
30
- values: [..._interpolateArray(separator), _t.node("String", "String", [_t.ref`open`, _t.ref`content`, _t.ref`close`], {
31
- open: _t.node("String", "Punctuator", [_t.lit`'`], {}, {}),
32
- content: _t.node("String", "Content", [_t.lit`separators[]`], {}, {}),
33
- close: _t.node("String", "Punctuator", [_t.lit`'`], {}, {})
34
- }, {})],
35
- close: _t.node("Instruction", "Punctuator", [_t.lit`)`], {}, {})
36
- }, {})
37
- }, {});
38
- }
39
- if (!(sep || allowHoles)) break;
40
- }
41
- }
42
- export function* Any(matchers, s, ctx) {
43
- for (const matcher of ctx.unbox(matchers)) {
44
- if (yield _t.node("Instruction", "Call", [_t.ref`verb`, _t.ref`arguments`], {
45
- verb: _t.node("Instruction", "Identifier", [_t.lit`eatMatch`], {}, {}),
46
- arguments: _t.node("Instruction", "Tuple", [_t.ref`open`, _t.ref`values[]`, _t.ref`close`], {
47
- open: _t.node("Instruction", "Punctuator", [_t.lit`(`], {}, {}),
48
- values: [..._interpolateArray(matcher)],
49
- close: _t.node("Instruction", "Punctuator", [_t.lit`)`], {}, {})
50
- }, {})
51
- }, {})) break;
52
- }
53
- }
54
- export function* All(matchers, s, ctx) {
55
- for (const matcher of ctx.unbox(matchers)) {
56
- yield _t.node("Instruction", "Call", [_t.ref`verb`, _t.ref`arguments`], {
57
- verb: _t.node("Instruction", "Identifier", [_t.lit`eat`], {}, {}),
58
- arguments: _t.node("Instruction", "Tuple", [_t.ref`open`, _t.ref`values[]`, _t.ref`close`], {
59
- open: _t.node("Instruction", "Punctuator", [_t.lit`(`], {}, {}),
60
- values: [..._interpolateArray(matcher)],
61
- close: _t.node("Instruction", "Punctuator", [_t.lit`)`], {}, {})
62
- }, {})
63
- }, {});
64
- }
65
- }
66
- export function* Match(cases, s, ctx) {
67
- for (const case_ of ctx.unbox(cases)) {
68
- const {
69
- 0: matcher,
70
- 1: guard
71
- } = ctx.unbox(case_);
72
- if (yield _t.node("Instruction", "Call", [_t.ref`verb`, _t.ref`arguments`], {
73
- verb: _t.node("Instruction", "Identifier", [_t.lit`match`], {}, {}),
74
- arguments: _t.node("Instruction", "Tuple", [_t.ref`open`, _t.ref`values[]`, _t.ref`close`], {
75
- open: _t.node("Instruction", "Punctuator", [_t.lit`(`], {}, {}),
76
- values: [..._interpolateArray(guard)],
77
- close: _t.node("Instruction", "Punctuator", [_t.lit`)`], {}, {})
78
- }, {})
79
- }, {})) {
80
- yield _t.node("Instruction", "Call", [_t.ref`verb`, _t.ref`arguments`], {
81
- verb: _t.node("Instruction", "Identifier", [_t.lit`eat`], {}, {}),
82
- arguments: _t.node("Instruction", "Tuple", [_t.ref`open`, _t.ref`values[]`, _t.ref`close`], {
83
- open: _t.node("Instruction", "Punctuator", [_t.lit`(`], {}, {}),
84
- values: [..._interpolateArray(matcher)],
85
- close: _t.node("Instruction", "Punctuator", [_t.lit`)`], {}, {})
86
- }, {})
87
- }, {});
88
- break;
89
- }
90
- }
91
- }
92
- export function* Punctuator(obj, s, ctx) {
93
- const {
94
- value,
95
- attrs
96
- } = ctx.unbox(obj);
97
- yield _t.node("Instruction", "Call", [_t.ref`verb`, _t.ref`arguments`], {
98
- verb: _t.node("Instruction", "Identifier", [_t.lit`eat`], {}, {}),
99
- arguments: _t.node("Instruction", "Tuple", [_t.ref`open`, _t.ref`values[]`, _t.ref`close`], {
100
- open: _t.node("Instruction", "Punctuator", [_t.lit`(`], {}, {}),
101
- values: [..._interpolateArray(value)],
102
- close: _t.node("Instruction", "Punctuator", [_t.lit`)`], {}, {})
103
- }, {})
104
- }, {});
105
- return {
106
- attrs
107
- };
108
- }
109
- export const Keyword = Punctuator;
110
- export function* Optional(matchers, s, ctx) {
111
- const matchers_ = ctx.unbox(matchers);
112
- if (matchers_.length > 1) {
113
- throw new Error('Optional only allows one matcher');
114
- }
115
- yield _t.node("Instruction", "Call", [_t.ref`verb`, _t.ref`arguments`], {
116
- verb: _t.node("Instruction", "Identifier", [_t.lit`eatMatch`], {}, {}),
117
- arguments: _t.node("Instruction", "Tuple", [_t.ref`open`, _t.ref`values[]`, _t.ref`close`], {
118
- open: _t.node("Instruction", "Punctuator", [_t.lit`(`], {}, {}),
119
- values: [..._interpolateArray(matchers_[0])],
120
- close: _t.node("Instruction", "Punctuator", [_t.lit`)`], {}, {})
121
- }, {})
122
- }, {});
123
- }
package/lib/token.js DELETED
@@ -1,23 +0,0 @@
1
- export const getCooked = (token) => {
2
- return token.children
3
- .map((child) => {
4
- if (child.type === 'Escape') {
5
- return child.value.cooked;
6
- } else if (child.type === 'Literal') {
7
- return child.value;
8
- } else throw new Error();
9
- })
10
- .join('');
11
- };
12
-
13
- export const getRaw = (token) => {
14
- return token.children
15
- .map((child) => {
16
- if (child.type === 'Escape') {
17
- return child.value.raw;
18
- } else if (child.type === 'Literal') {
19
- return child.value;
20
- } else throw new Error();
21
- })
22
- .join('');
23
- };