@bablr/bablr-vm 0.18.1 → 0.19.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/state.js CHANGED
@@ -1,11 +1,30 @@
1
1
  import emptyStack from '@iter-tools/imm-stack';
2
2
  import { WeakStackFrame } from '@bablr/weak-stack';
3
3
  import { getCooked } from '@bablr/agast-helpers/stream';
4
+ import { reifyExpression } from '@bablr/agast-vm-helpers';
5
+ import {
6
+ EmbeddedMatcher,
7
+ EmbeddedNode,
8
+ EmbeddedRegex,
9
+ GapTag,
10
+ ReferenceTag,
11
+ ShiftTag,
12
+ } from '@bablr/agast-helpers/symbols';
13
+ import * as btree from '@bablr/agast-helpers/btree';
14
+ import {
15
+ buildCall,
16
+ buildEmbeddedRegex,
17
+ buildEmbeddedTag,
18
+ buildGapTag,
19
+ buildReferenceTag,
20
+ buildStubNode,
21
+ } from '@bablr/agast-helpers/tree';
22
+ import { get, Path, PathResolver, TagPath, updatePath } from '@bablr/agast-helpers/path';
4
23
  import { match, guardWithPattern } from './utils/pattern.js';
5
24
  import { facades, actuals } from './facades.js';
6
- import { reifyExpression } from '@bablr/agast-vm-helpers';
7
- import { EmbeddedNode, GapTag } from '@bablr/agast-helpers/symbols';
8
- import { NodeFacade } from './node.js';
25
+ import { buildInternalState, FragmentFacade, internalStates } from './node.js';
26
+
27
+ export const nodeStates = new WeakMap();
9
28
 
10
29
  export const StateFacade = class BABLRStateFacade {
11
30
  constructor(state) {
@@ -24,8 +43,16 @@ export const StateFacade = class BABLRStateFacade {
24
43
  return actuals.get(this).span.name;
25
44
  }
26
45
 
27
- get result() {
28
- return actuals.get(this).result;
46
+ get resultPath() {
47
+ return actuals.get(this).resultPath;
48
+ }
49
+
50
+ get reference() {
51
+ return actuals.get(this).reference;
52
+ }
53
+
54
+ get referencePath() {
55
+ return actuals.get(this).referencePath;
29
56
  }
30
57
 
31
58
  get holding() {
@@ -36,12 +63,17 @@ export const StateFacade = class BABLRStateFacade {
36
63
  return actuals.get(this).path;
37
64
  }
38
65
 
66
+ get depths() {
67
+ const { path, result } = actuals.get(this).depths;
68
+ return { path, result };
69
+ }
70
+
39
71
  get node() {
40
- return NodeFacade.wrap(actuals.get(this).node, this.ctx);
72
+ return FragmentFacade.wrap(actuals.get(this).node, this.ctx);
41
73
  }
42
74
 
43
75
  get parentNode() {
44
- return NodeFacade.wrap(actuals.get(this).parentNode, this.ctx);
76
+ return FragmentFacade.wrap(actuals.get(this).parentNode, this.ctx);
45
77
  }
46
78
 
47
79
  get source() {
@@ -56,44 +88,57 @@ export const StateFacade = class BABLRStateFacade {
56
88
  return actuals.get(this).status;
57
89
  }
58
90
 
59
- nodeForPath(path) {
60
- return NodeFacade.wrap(actuals.get(this).nodeForPath(path), this.ctx);
61
- }
62
-
63
- pathForTag(tag) {
64
- return actuals.get(this).pathForTag(tag);
91
+ get parent() {
92
+ return facades.get(actuals.get(this).parent);
65
93
  }
66
94
 
67
- nodeForTag(tag) {
68
- return NodeFacade.wrap(actuals.get(this).nodeForTag(tag), this.ctx);
95
+ nodeForPath(path) {
96
+ return actuals.get(this).nodeForPath(path);
69
97
  }
70
98
  };
71
99
 
72
100
  export const State = class BABLRState extends WeakStackFrame {
73
101
  constructor(
102
+ parent,
74
103
  source,
75
- agast,
76
104
  context,
105
+ expressions = emptyStack,
77
106
  balanced = emptyStack,
78
107
  spans = emptyStack.push({ name: 'Bare' }),
108
+ referencePath = null,
109
+ resultPath = null,
110
+ depths = { path: -1, result: -1, emitted: -1 },
111
+ held = null,
112
+ node = null,
79
113
  ) {
80
- super();
114
+ super(parent);
81
115
 
82
- if (!source || !agast) throw new Error('invalid args to State');
116
+ if (!source) throw new Error('invalid args to State');
83
117
 
84
118
  this.source = source;
85
- this.agast = agast;
86
119
  this.context = context;
120
+ this.expressions = expressions;
87
121
  this.balanced = balanced;
88
122
  this.spans = spans;
123
+ this.referencePath = referencePath;
124
+ this.resultPath = resultPath;
125
+ this.depths = depths;
126
+ this.held = held;
127
+ this.node = node;
89
128
 
90
129
  this.status = 'active';
91
130
 
131
+ this.emitted = null;
132
+
92
133
  new StateFacade(this);
93
134
  }
94
135
 
95
- static from(source, agast, context) {
96
- return State.create(source, agast, context);
136
+ static from(source, context, expressions = []) {
137
+ return State.create(source, context, emptyStack.push(...expressions));
138
+ }
139
+
140
+ get unboundAttributes() {
141
+ return nodeStates.get(this.node).unboundAttributes;
97
142
  }
98
143
 
99
144
  get guardedSource() {
@@ -108,23 +153,19 @@ export const State = class BABLRState extends WeakStackFrame {
108
153
  }
109
154
 
110
155
  get path() {
111
- return this.agast.path;
112
- }
113
-
114
- get node() {
115
- return this.agast.node;
156
+ throw new Error('not implemented');
116
157
  }
117
158
 
118
159
  get parentNode() {
119
- return this.agast.parentNode;
160
+ throw new Error('not implemented');
120
161
  }
121
162
 
122
163
  get holding() {
123
- return this.agast.holding;
164
+ return !!this.held;
124
165
  }
125
166
 
126
- get result() {
127
- return this.agast.result;
167
+ get reference() {
168
+ return this.referencePath?.tag;
128
169
  }
129
170
 
130
171
  get isGap() {
@@ -135,50 +176,224 @@ export const State = class BABLRState extends WeakStackFrame {
135
176
  return !!this.parent;
136
177
  }
137
178
 
138
- nodeForPath(path) {
139
- return this.agast.nodeForPath(path);
140
- }
179
+ advance(tag) {
180
+ const { path, instructionsPump, expressionsPump, agast } = internalStates.get(this.node);
141
181
 
142
- pathForTag(tag) {
143
- return this.agast.pathForTag(tag);
144
- }
182
+ if (tag.type === GapTag) {
183
+ expressionsPump.queue(
184
+ this.expressions.size ? this.expressions.value : buildStubNode(buildGapTag()),
185
+ );
186
+ this.expressions = this.expressions.pop();
187
+ }
188
+ instructionsPump.queue(buildCall('advance', buildEmbeddedTag(tag)));
189
+ agast.next();
145
190
 
146
- nodeForTag(tag) {
147
- return this.agast.nodeForTag(tag);
191
+ this.resultPath = TagPath.from(path, -1);
148
192
  }
149
193
 
150
194
  guardedMatch(pattern) {
151
- let { span, spans, source, node } = this;
195
+ let { span, source } = this;
152
196
  let { guard } = span;
153
197
 
154
- if (pattern.type === EmbeddedNode) {
155
- pattern = reifyExpression(pattern.value);
198
+ let pattern_ = pattern;
199
+ if (pattern.type === EmbeddedMatcher) {
200
+ pattern_ = reifyExpression(pattern.value).nodeMatcher;
201
+ } else if (pattern.type === EmbeddedRegex) {
202
+ pattern_ = pattern.value;
203
+ } else if (typeof pattern !== 'string') {
204
+ throw new Error();
156
205
  }
157
206
 
158
207
  if (
159
208
  span.type === 'Lexical' &&
160
- (node.flags.token
161
- ? node.attributes.balancer || node.attributes.balanced
162
- : pattern.attributes?.balancer)
209
+ pattern.type === EmbeddedMatcher &&
210
+ (pattern_.flags.token
211
+ ? pattern_.attributes.balancer || pattern_.attributes.balanced
212
+ : pattern_.attributes?.balancer)
163
213
  ) {
164
214
  // also check that the open node starts a lexical span?
165
215
  guard = null;
166
216
  }
167
217
 
168
- if (pattern?.intrinsicValue) {
218
+ if (pattern_?.intrinsicValue) {
169
219
  // if (pattern.type === OpenNodeTag) {
170
220
 
171
221
  // // TODO differntiate better between self-closing tags and matchers
172
222
  // pattern = pattern.value;
173
223
  // }
174
224
 
175
- pattern = pattern.intrinsicValue || getCooked(pattern.children);
225
+ pattern_ = pattern_.intrinsicValue || getCooked(pattern_.children);
226
+
227
+ if (pattern_.type === Symbol.for('String')) {
228
+ pattern_ = reifyExpression(pattern_);
229
+ }
230
+ }
231
+
232
+ return match(pattern_, guard ? guardWithPattern(guard, source) : source);
233
+ }
234
+
235
+ match(pattern) {
236
+ return match(pattern, this.source);
237
+ }
176
238
 
177
- if (pattern.type === 'String') {
178
- pattern = reifyExpression(pattern);
239
+ branch() {
240
+ const baseState = this;
241
+ let {
242
+ source,
243
+ context,
244
+ balanced,
245
+ spans,
246
+ resultPath,
247
+ depths,
248
+ referencePath,
249
+ held,
250
+ node,
251
+ expressions,
252
+ } = baseState;
253
+
254
+ let resolver = new PathResolver();
255
+
256
+ const internalState = buildInternalState();
257
+
258
+ for (let tag of btree.traverse(node.children)) {
259
+ if (tag.type === GapTag) {
260
+ let { name, isArray, flags } = resolver.reference.value;
261
+ const resolvedPath = buildReferenceTag(
262
+ name,
263
+ isArray,
264
+ flags,
265
+ Number.isFinite(resolver.counters[name]) ? resolver.counters[name] : null,
266
+ );
267
+ const expr = get(resolvedPath, node);
268
+ internalState.expressionsPump.queue(expr);
269
+ } else if (tag.type === EmbeddedNode) {
270
+ internalState.expressionsPump.queue(tag.value);
271
+ tag = buildGapTag();
179
272
  }
273
+
274
+ resolver.advance(tag);
275
+
276
+ internalState.instructionsPump.queue(buildCall('advance', buildEmbeddedTag(tag)));
277
+ internalState.agast.next();
278
+ }
279
+
280
+ const newNode = internalState.agastState.node;
281
+ const nodeState = nodeStates.get(node);
282
+ let newResultPath;
283
+
284
+ if (resultPath.path.node === node) {
285
+ newResultPath = TagPath.from(Path.from(newNode), resultPath.childrenIndex);
286
+ } else {
287
+ newResultPath = resultPath;
180
288
  }
181
289
 
182
- return match(pattern, guard ? guardWithPattern(guard, source) : source);
290
+ nodeStates.set(newNode, { ...nodeState });
291
+ internalStates.set(newNode, internalState);
292
+
293
+ const child = this.push(
294
+ source.branch(),
295
+ context,
296
+ expressions,
297
+ balanced,
298
+ spans,
299
+ referencePath,
300
+ newResultPath,
301
+ depths,
302
+ held,
303
+ newNode,
304
+ );
305
+
306
+ return child;
307
+ }
308
+
309
+ accept() {
310
+ const accepted = this;
311
+
312
+ this.status = 'accepted';
313
+
314
+ const { parent } = this;
315
+
316
+ if (!parent) {
317
+ throw new Error('accepted the root state');
318
+ }
319
+
320
+ parent.spans = accepted.spans;
321
+ parent.balanced = accepted.balanced;
322
+ parent.referencePath = accepted.referencePath;
323
+ parent.held = accepted.held;
324
+ parent.depths = accepted.depths;
325
+ parent.expressions = accepted.expressions;
326
+ // do I need this condition?
327
+ if (parent.depths.path === accepted.depths.path) {
328
+ const parentChildren = parent.node.children;
329
+ parent.node.children = parentChildren;
330
+
331
+ const internalState = internalStates.get(parent.node);
332
+ const { path: parentPath } = internalState;
333
+ // FIXME
334
+ // some `properties` are not copied
335
+ for (let i = btree.getSum(parentChildren); i < btree.getSum(accepted.node.children); i++) {
336
+ let tag = btree.getAt(i, accepted.node.children);
337
+
338
+ if (tag.type === GapTag) {
339
+ let referenceIdx = i;
340
+ let reference;
341
+ let isShift = btree.getAt(i - 1, accepted.node.children).type === ShiftTag;
342
+
343
+ while (
344
+ (reference = btree.getAt(--referenceIdx, accepted.node.children)).type !== ReferenceTag
345
+ );
346
+
347
+ let { name, isArray, flags } = reference.value;
348
+ const resolvedPath = buildReferenceTag(
349
+ name,
350
+ isArray,
351
+ flags,
352
+ isArray ? btree.getSum(parentPath.node.properties[name]) - (isShift ? 1 : 0) : null,
353
+ );
354
+ const expr = get(resolvedPath, accepted.node);
355
+ internalState.expressionsPump.queue(expr);
356
+ } else if (tag.type === EmbeddedNode) {
357
+ internalState.expressionsPump.queue(tag.value);
358
+ tag = buildGapTag();
359
+ }
360
+
361
+ internalState.instructionsPump.queue(buildCall('advance', buildEmbeddedTag(tag)));
362
+ internalState.agast.next();
363
+
364
+ updatePath(parentPath, tag);
365
+ }
366
+ }
367
+
368
+ if (parent.depths.result === accepted.depths.result + 1) {
369
+ parent.resultPath = new TagPath(parent.resultPath.path, accepted.resultPath.childrenIndex);
370
+ } else {
371
+ parent.resultPath = accepted.resultPath;
372
+ }
373
+
374
+ nodeStates.set(parent.node, nodeStates.get(accepted.node));
375
+
376
+ parent.source.accept(accepted.source);
377
+
378
+ return parent;
379
+ }
380
+
381
+ reject() {
382
+ const rejectedState = this;
383
+ const { parent } = rejectedState;
384
+
385
+ if (this.status === 'rejected') {
386
+ return parent;
387
+ }
388
+
389
+ if (this.status !== 'active') throw new Error();
390
+
391
+ this.status = 'rejected';
392
+
393
+ if (!parent) throw new Error('rejected root state');
394
+
395
+ rejectedState.source.reject();
396
+
397
+ return parent;
183
398
  }
184
399
  };
@@ -1,9 +1,16 @@
1
1
  import isString from 'iter-tools-es/methods/is-string';
2
2
  import isEmpty from 'iter-tools-es/methods/is-empty';
3
3
  import { generateMatches } from '@bablr/regex-vm';
4
- import { getStreamIterator, maybeWait } from '@bablr/agast-helpers/stream';
5
- import * as t from '@bablr/agast-helpers/shorthand';
4
+ import { getStreamIterator, maybeWait, printType } from '@bablr/agast-helpers/stream';
6
5
  import * as l from '@bablr/agast-vm-helpers/languages';
6
+ import {
7
+ buildAlternative,
8
+ buildAlternatives,
9
+ buildElements,
10
+ buildPattern,
11
+ buildToken,
12
+ } from '@bablr/helpers/builders';
13
+ import { buildEmbeddedRegex } from '@bablr/agast-helpers/builders';
7
14
 
8
15
  export const assertValidRegex = (expr) => {
9
16
  const { flags } = expr;
@@ -16,25 +23,21 @@ export const assertValidRegex = (expr) => {
16
23
  };
17
24
 
18
25
  const buildStringRegex = (str) => {
19
- return t.node(l.Regex, 'Pattern', [t.ref`open`, t.ref`alternatives[]`, t.ref`close`], {
20
- open: t.s_node(l.Regex, 'Punctuator', '/'),
21
- alternatives: [
22
- t.node(l.Regex, 'Alternative', [t.ref`elements[]`], {
23
- elements: [...str].map((chr) => t.s_node(l.Regex, 'Character', chr)),
24
- }),
25
- ],
26
- close: t.s_node(l.Regex, 'Punctuator', '/'),
27
- });
26
+ return buildPattern(
27
+ buildAlternatives([
28
+ buildAlternative(buildElements([...str].map((chr) => buildToken(l.Regex, 'Character', chr)))),
29
+ ]),
30
+ );
28
31
  };
29
32
 
30
33
  export const match = (pattern, source) => {
31
34
  const pattern_ = isString(pattern) ? buildStringRegex(pattern) : pattern;
32
35
 
33
- if (pattern_.type !== 'Pattern') throw new Error();
36
+ if (printType(pattern_.type) !== 'Pattern') throw new Error();
34
37
 
35
38
  assertValidRegex(pattern_);
36
39
 
37
- const iter = getStreamIterator(generateMatches(pattern_, source));
40
+ const iter = getStreamIterator(generateMatches(buildEmbeddedRegex(pattern_), source));
38
41
 
39
42
  const step = iter.next();
40
43
 
@@ -0,0 +1,20 @@
1
+ export class Pump {
2
+ constructor() {
3
+ this.held = [];
4
+ }
5
+
6
+ queue(value) {
7
+ this.held.push(value);
8
+ }
9
+
10
+ next() {
11
+ const held = this.held[0];
12
+ if (!held) throw new Error();
13
+ this.held.shift();
14
+ return { done: false, value: held };
15
+ }
16
+
17
+ [Symbol.iterator]() {
18
+ return this;
19
+ }
20
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bablr/bablr-vm",
3
3
  "description": "A VM for parsing using BABLR languages",
4
- "version": "0.18.1",
4
+ "version": "0.19.0",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
7
  "files": [
@@ -12,14 +12,15 @@
12
12
  },
13
13
  "sideEffects": false,
14
14
  "dependencies": {
15
- "@bablr/agast-helpers": "^0.5.0",
16
- "@bablr/agast-vm-helpers": "^0.5.0",
15
+ "@bablr/agast-helpers": "0.6.0",
16
+ "@bablr/agast-vm": "0.7.0",
17
+ "@bablr/agast-vm-helpers": "0.6.0",
17
18
  "@bablr/coroutine": "0.1.0",
18
- "@bablr/helpers": "^0.20.0",
19
- "@bablr/regex-vm": "^0.9.0",
19
+ "@bablr/helpers": "0.21.1",
20
+ "@bablr/regex-vm": "0.10.0",
20
21
  "@bablr/weak-stack": "0.1.0",
21
22
  "@iter-tools/imm-stack": "1.1.0",
22
- "iter-tools-es": "^7.5.3"
23
+ "iter-tools-es": "7.5.3"
23
24
  },
24
25
  "devDependencies": {
25
26
  "@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#d834ccc52795d6c3b96ecc6c419960fceed221a6",