@bablr/bablr-vm 0.20.0 → 0.21.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/node.js CHANGED
@@ -2,28 +2,24 @@ import {
2
2
  buildCloseNodeTag,
3
3
  buildGapTag,
4
4
  buildOpenNodeTag,
5
- buildReferenceTag,
6
5
  buildStubNode,
6
+ fragmentFlags,
7
7
  isNullNode,
8
- parseReference,
9
8
  } from '@bablr/agast-helpers/tree';
10
- import { getStreamIterator } from '@bablr/agast-helpers/stream';
11
- import { agast } from '@bablr/agast-vm';
12
- import * as sumtree from '@bablr/agast-helpers/sumtree';
13
- import { Pump } from './utils/pump.js';
14
- import { OpenNodeTag, ReferenceTag } from '@bablr/agast-helpers/symbols';
9
+ import { NullTag, OpenNodeTag, ReferenceTag } from '@bablr/agast-helpers/symbols';
15
10
  import {
11
+ buildFullPathSegment,
16
12
  buildFullRange,
17
- get,
18
- getProperties,
19
- getPropertyChildrenIndex,
13
+ buildPathSegment,
14
+ buildTypePathSegment,
20
15
  } from '@bablr/agast-helpers/path';
21
- import { isArray } from '@bablr/helpers/object';
16
+ import { hasOwn, isArray } from '@bablr/helpers/object';
22
17
 
23
18
  export const states = new WeakMap();
24
- export const internalStates = new WeakMap();
25
19
 
26
- const { hasOwn } = Object;
20
+ export const isFragmentNode = (node) => {
21
+ return node.type === null && node.sigilTag.type === OpenNodeTag;
22
+ };
27
23
 
28
24
  export const FragmentFacade = class BABLRFragmentFacade {
29
25
  static wrapNode(
@@ -74,12 +70,12 @@ export const FragmentFacade = class BABLRFragmentFacade {
74
70
  node,
75
71
  context,
76
72
  transparent = false,
77
- isFragmentNode = true,
73
+ isFragmentFacade = true,
78
74
  childrenIndexRange = null,
79
75
  dotPropertyReference = null,
80
76
  dotPropertyIndex = null,
81
77
  ) {
82
- if (!node) throw new Error();
78
+ if (!node || hasOwn(node, 'properties')) throw new Error();
83
79
 
84
80
  if (childrenIndexRange && (childrenIndexRange[0] == null || !childrenIndexRange[1] == null)) {
85
81
  throw new Error();
@@ -87,34 +83,35 @@ export const FragmentFacade = class BABLRFragmentFacade {
87
83
 
88
84
  if (!context) throw new Error();
89
85
 
90
- if (dotPropertyReference && !hasOwn(node.properties, dotPropertyReference.value.name))
91
- throw new Error();
86
+ if (dotPropertyReference && !node.properties.at(dotPropertyReference.name)) throw new Error();
92
87
 
93
- if (isArray(dotPropertyReference?.value.isArray && dotPropertyIndex == null)) throw new Error();
88
+ if (dotPropertyReference?.isArray && dotPropertyIndex == null) throw new Error();
94
89
 
95
90
  let resolvedNode = node;
96
91
  let fragmentNode = node;
97
92
 
98
93
  if (dotPropertyReference) {
99
- let { name, isArray, flags } = dotPropertyReference.value;
94
+ let { type, name } = dotPropertyReference;
100
95
 
101
- let dotReference = buildReferenceTag(name, isArray, flags, dotPropertyIndex);
102
-
103
- resolvedNode = get(dotReference, fragmentNode);
96
+ resolvedNode = fragmentNode.get(buildFullPathSegment(type, name, dotPropertyIndex));
104
97
  }
105
98
 
99
+ if (dotPropertyReference?.value) throw new Error();
100
+
106
101
  states.set(this, {
107
102
  context,
108
- openTag: buildOpenNodeTag(),
103
+ openTag: buildOpenNodeTag(fragmentFlags),
109
104
  closeTag: buildCloseNodeTag(),
110
105
  node: resolvedNode,
111
- fragmentNode: node,
106
+ fragmentNode,
112
107
  transparent,
113
- isFragmentNode,
108
+ isFragmentFacade,
114
109
  childrenIndexRange,
115
110
  dotPropertyReference,
116
111
  dotPropertyIndex,
117
112
  });
113
+
114
+ Object.freeze(this);
118
115
  }
119
116
 
120
117
  get dotPropertyName() {
@@ -127,7 +124,7 @@ export const FragmentFacade = class BABLRFragmentFacade {
127
124
 
128
125
  get isNull() {
129
126
  let { node } = states.get(this);
130
- return isNullNode(node);
127
+ return node.sigilTag.type === NullTag;
131
128
  }
132
129
 
133
130
  get children() {
@@ -141,7 +138,7 @@ export const FragmentFacade = class BABLRFragmentFacade {
141
138
  for (let i = childrenIndexRange[0]; i <= childrenIndexRange[1]; i++) {
142
139
  const interpolated = false; // TODO
143
140
  if (!interpolated || transparent) {
144
- yield sumtree.getAt(i, node.children);
141
+ yield node.children.at(i);
145
142
  }
146
143
  }
147
144
  },
@@ -156,7 +153,7 @@ export const FragmentFacade = class BABLRFragmentFacade {
156
153
  if (childrenIndexRange[0] > childrenIndexRange[1]) throw new Error();
157
154
 
158
155
  for (let i = childrenIndexRange[0]; i <= childrenIndexRange[1]; i++) {
159
- let tag = sumtree.getAt(i, node.children);
156
+ let tag = node.children.at(i);
160
157
  if (tag.type === ReferenceTag) {
161
158
  const { name, isArray } = tag.value;
162
159
  let resolvedTagName = name === '.' ? dotPropertyName : name;
@@ -171,41 +168,44 @@ export const FragmentFacade = class BABLRFragmentFacade {
171
168
  }
172
169
 
173
170
  get flags() {
174
- const { openTag } = this;
175
-
176
- return openTag.type === OpenNodeTag ? openTag.value.flags : null;
177
- }
178
-
179
- get language() {
180
- let { node, isFragmentNode } = states.get(this);
171
+ const { node } = states.get(this);
172
+ let openTag = node.sigilTag;
181
173
 
182
- return isFragmentNode ? null : node.language;
174
+ return openTag?.value.flags;
183
175
  }
184
176
 
185
177
  get type() {
186
- let { node, isFragmentNode } = states.get(this);
178
+ let { node } = states.get(this);
179
+ let openTag = node.sigilTag;
187
180
 
188
- return isFragmentNode ? null : node.type;
181
+ return openTag?.value.type;
189
182
  }
190
183
 
191
184
  get attributes() {
192
- let { node, isFragmentNode } = states.get(this);
185
+ let { node } = states.get(this);
186
+ let openTag = node.sigilTag;
193
187
 
194
- return isFragmentNode ? {} : node.attributes;
188
+ return openTag?.value.attributes;
195
189
  }
196
190
 
197
191
  get openTag() {
198
- const { node, openTag, isFragmentNode } = states.get(this);
192
+ const { node, openTag, isFragmentFacade } = states.get(this);
199
193
 
200
- return isFragmentNode ? openTag : sumtree.getAt(0, node.children);
194
+ return isFragmentNode(node)
195
+ ? node.children.at(1)
196
+ : isFragmentFacade
197
+ ? openTag
198
+ : node.children.at(0);
201
199
  }
202
200
 
203
201
  get closeTag() {
204
- const { node, closeTag, isFragmentNode } = states.get(this);
202
+ const { node, closeTag, isFragmentFacade } = states.get(this);
205
203
 
206
- return isFragmentNode
207
- ? closeTag
208
- : sumtree.getAt(sumtree.getSize(node.children) - 1, node.children);
204
+ return isFragmentFacade
205
+ ? isFragmentNode(node)
206
+ ? node.closeTag
207
+ : closeTag
208
+ : node.children.at(-1);
209
209
  }
210
210
 
211
211
  merge(targetFragment) {
@@ -213,8 +213,7 @@ export const FragmentFacade = class BABLRFragmentFacade {
213
213
  fragmentNode,
214
214
  context,
215
215
  transparent,
216
- isFragmentNode,
217
- childrenIndexRange,
216
+ isFragmentFacade,
218
217
  dotPropertyReference,
219
218
  dotPropertyIndex,
220
219
  } = states.get(this);
@@ -227,7 +226,7 @@ export const FragmentFacade = class BABLRFragmentFacade {
227
226
  fragmentNode,
228
227
  context,
229
228
  transparent,
230
- isFragmentNode,
229
+ isFragmentFacade,
231
230
  targetChildrenIndexRange,
232
231
  dotPropertyReference,
233
232
  dotPropertyIndex,
@@ -240,85 +239,87 @@ export const FragmentFacade = class BABLRFragmentFacade {
240
239
  get(path) {
241
240
  let {
242
241
  node: ownNode,
243
- fragmentNode,
244
242
  context,
245
243
  transparent,
246
- isFragmentNode,
244
+ isFragmentFacade,
247
245
  childrenIndexRange,
248
246
  dotPropertyReference: dotReference,
249
247
  dotPropertyIndex: dotIndex,
250
248
  } = states.get(this);
251
- const parsedRef = parseReference(path);
252
249
 
253
- let node = fragmentNode;
250
+ if (typeof path === 'string') {
251
+ path = [path];
252
+ }
254
253
 
255
- if (parsedRef.value.name !== '.') {
256
- if (dotReference) {
257
- fragmentNode = ownNode;
258
- dotReference = parsedRef;
254
+ if (!isArray(path)) throw new Error();
259
255
 
260
- isFragmentNode = false;
261
- } else {
262
- dotReference = parsedRef;
263
- dotIndex = parsedRef.value.index;
256
+ let node = ownNode;
257
+ let ref = dotReference;
258
+ let index = dotIndex;
259
+ let outerNode;
260
+
261
+ for (let i = 0; i < path.length; i++) {
262
+ let name = path[i];
263
+ let seg = typeof name === 'string' ? buildPathSegment(name) : name;
264
+ outerNode = node;
265
+
266
+ node = node.get(seg);
267
+ if (node?.sigilTag.type === NullTag) {
268
+ node = null;
264
269
  }
265
270
 
266
- let { name, isArray, flags } = parsedRef.value;
267
- let resolvedReference = buildReferenceTag(name, isArray, flags, dotIndex);
268
- let refIndex = getPropertyChildrenIndex(fragmentNode, resolvedReference);
269
- childrenIndexRange = [refIndex, refIndex + 1];
271
+ if (!node) break;
272
+ let refIndex = outerNode.getPropertyChildrenIndex(null, seg.name, seg.index);
273
+ childrenIndexRange = [refIndex, refIndex + 2];
270
274
 
271
- const binding = getProperties(parsedRef, fragmentNode.properties);
275
+ let boundRef = outerNode.properties.referenceAt(seg.name, seg.index);
272
276
 
273
- if (!binding) return null;
277
+ if (!node) return null;
274
278
 
275
- const ref = binding.reference;
279
+ ref = boundRef;
280
+ index = seg.index === -1 ? outerNode.properties.at(seg.name).size + seg.index : seg.index;
276
281
 
277
282
  node =
278
- (ref && ref.type === ReferenceTag && !ref.value.flags.hasGap) || transparent
279
- ? binding.node
283
+ (ref && ref.type === ReferenceTag && !ref.flags.hasGap) || transparent
284
+ ? node
280
285
  : buildStubNode(buildGapTag());
281
286
  }
282
287
 
283
- return isNullNode(node)
288
+ return !node || isNullNode(node)
284
289
  ? null
285
290
  : new FragmentFacade(
286
- fragmentNode,
291
+ outerNode,
287
292
  context,
288
293
  transparent,
289
- isFragmentNode,
294
+ isFragmentFacade,
290
295
  childrenIndexRange,
291
- dotReference,
292
- dotIndex,
296
+ ref,
297
+ index,
293
298
  );
294
299
  }
295
300
 
296
301
  has(path) {
297
- return hasOwn(states.get(this).node.properties, parseReference(path).value.name);
302
+ let node = states.get(this).node;
303
+ let path_ = path;
304
+ if (typeof path === 'string') {
305
+ path_ = [path];
306
+ }
307
+
308
+ if (!isArray(path_)) throw new Error();
309
+
310
+ for (let i = 0; i < path_.length; i++) {
311
+ if (!node.properties.has(path_[i])) return false;
312
+ if (i < path.length - 1) {
313
+ node = node.properties.at(path_[i]);
314
+ }
315
+ }
316
+
317
+ return true;
318
+ }
319
+
320
+ hasRoot() {
321
+ return this.type != null ? false : !!states.get(this).node.get([buildTypePathSegment('.')]);
298
322
  }
299
323
  };
300
324
 
301
325
  Object.seal(FragmentFacade);
302
-
303
- export const buildInternalState = () => {
304
- let instructionsPump = new Pump();
305
- let expressionsPump = new Pump();
306
- let agastState = null;
307
- let agast_ = getStreamIterator(
308
- agast(
309
- (ctx, s) => {
310
- agastState = s;
311
- return instructionsPump;
312
- },
313
- { expressions: expressionsPump },
314
- ),
315
- );
316
- let internalState = {
317
- instructionsPump,
318
- expressionsPump,
319
- agastState,
320
- agast: agast_,
321
- path: agastState.path,
322
- };
323
- return internalState;
324
- };
package/lib/source.js CHANGED
@@ -1,8 +1,14 @@
1
- import { WeakStackFrame } from '@bablr/weak-stack';
2
- import { maybeWait, getStreamIterator, emptyStreamIterator } from '@bablr/agast-helpers/stream';
1
+ import { maybeWait, getStreamIterator, StreamIterable } from '@bablr/agast-helpers/stream';
3
2
  import { facades, actuals } from './facades.js';
4
3
 
5
- // Queue item instances are shared between all forks.
4
+ function* __concat(...iterables) {
5
+ for (let iter of iterables) {
6
+ yield* getStreamIterator(iter);
7
+ }
8
+ }
9
+ const concat = (...iterables) => new StreamIterable(__concat(...iterables));
10
+
11
+ // Queue item instances are shared between all sources.
6
12
  class QueueItem {
7
13
  constructor(step) {
8
14
  this.step = step;
@@ -17,7 +23,7 @@ class Exchange {
17
23
  this.iterator = iterator;
18
24
  this.tail = new QueueItem(null);
19
25
  this.head = this.tail;
20
- this.forks = 0;
26
+ this.sources = 0;
21
27
  }
22
28
 
23
29
  static from(iterable) {
@@ -28,10 +34,19 @@ class Exchange {
28
34
  return this.head.step instanceof Promise;
29
35
  }
30
36
 
31
- allocateFork(fork) {
32
- const { head = this.tail, exchange = this, current } = fork || {};
33
- ++this.forks;
34
- return new Fork(head, exchange, current);
37
+ allocateSource(source) {
38
+ const { head = this.tail, exchange = this, index, holding } = source || {};
39
+ ++this.sources;
40
+ return new Source(head, exchange, index, holding);
41
+ }
42
+
43
+ releaseSource(source) {
44
+ --this.sources;
45
+ if (this.sources === 0) {
46
+ this.iterator.return?.();
47
+ }
48
+ source.exchange = null;
49
+ return { value: undefined, done: true };
35
50
  }
36
51
 
37
52
  advance() {
@@ -44,7 +59,6 @@ class Exchange {
44
59
  if (step instanceof Promise) {
45
60
  step = step.then((step) => {
46
61
  newItem.step = step;
47
- return step;
48
62
  });
49
63
  }
50
64
 
@@ -55,99 +69,47 @@ class Exchange {
55
69
 
56
70
  return newItem;
57
71
  }
72
+ }
58
73
 
59
- releaseFork(fork) {
60
- --this.forks;
61
- if (this.forks === 0) {
62
- this.iterator.return?.();
63
- }
64
- return { value: undefined, done: true };
74
+ let sources = new WeakMap();
75
+
76
+ class SourceIterator {
77
+ static from(source) {
78
+ const { exchange } = source;
79
+ return source.done
80
+ ? sources.get(source) ?? new SourceIterator(source)
81
+ : new SourceIterator(exchange.allocateSource(source));
65
82
  }
66
- }
67
83
 
68
- class ForkIterator {
69
- constructor(fork) {
70
- facades.set(fork.clone(), this);
84
+ constructor(source) {
85
+ sources.set(this, source);
71
86
  }
72
87
 
73
88
  next() {
74
- const fork = actuals.get(this);
75
- if (!fork.done) {
76
- const { head } = fork;
77
- fork.advance();
78
- return maybeWait(fork.head.step, () => head.step);
89
+ const source = sources.get(this);
90
+ if (!source.done) {
91
+ const { holding, head } = source;
92
+ source.advance();
93
+ return maybeWait(source.head.step, () => {
94
+ return holding ? { value: null, done: false } : head.step;
95
+ });
79
96
  } else {
80
97
  return { value: undefined, done: true };
81
98
  }
82
99
  }
83
100
 
84
101
  return() {
85
- actuals.get(this).return();
102
+ sources.get(this).release();
103
+
86
104
  return { value: undefined, done: true };
87
105
  }
88
106
 
89
- [Symbol.for('@@streamIterator')]() {
107
+ [Symbol.iterator]() {
90
108
  return this;
91
109
  }
92
- }
93
-
94
- class Fork {
95
- constructor(head, exchange, done = false) {
96
- this.head = head; // QueueItem
97
- this.exchange = exchange;
98
- this._done = done;
99
- }
100
-
101
- get done() {
102
- return this._done || this.head.step?.done;
103
- }
104
-
105
- get value() {
106
- return this.done ? { value: undefined, done: true } : this.head.step?.value;
107
- }
108
-
109
- advance() {
110
- const { exchange } = this;
111
-
112
- if (this.done) {
113
- throw new Error('cannot advance a fork that is done');
114
- } else {
115
- let { head } = this;
116
-
117
- let nextItem = this.head.next;
118
-
119
- if (!head.step?.done) {
120
- if (!nextItem) {
121
- nextItem = exchange.fetch();
122
- }
123
-
124
- this.head = nextItem;
125
- }
126
-
127
- return nextItem;
128
- }
129
- }
130
-
131
- return() {
132
- const { done, exchange } = this;
133
-
134
- if (!done) exchange.releaseFork(this);
135
-
136
- const step = { value: undefined, done: true };
137
-
138
- this._current = step;
139
-
140
- return step;
141
- }
142
-
143
- clone() {
144
- const { exchange } = this;
145
-
146
- return exchange.allocateFork(this);
147
- }
148
110
 
149
111
  [Symbol.for('@@streamIterator')]() {
150
- return new ForkIterator(this);
112
+ return this;
151
113
  }
152
114
  }
153
115
 
@@ -158,6 +120,8 @@ export const SourceFacade = class BABLRSourceFacade {
158
120
 
159
121
  constructor(source) {
160
122
  facades.set(source, this);
123
+
124
+ Object.freeze(this);
161
125
  }
162
126
 
163
127
  [Symbol.for('@@streamIterator')]() {
@@ -181,18 +145,16 @@ export const SourceFacade = class BABLRSourceFacade {
181
145
  }
182
146
  };
183
147
 
184
- export const Source = class BABLRSource extends WeakStackFrame {
148
+ export const Source = class BABLRSource {
185
149
  static from(iterable) {
186
150
  const exchange = Exchange.from(iterable);
187
- return Source.create(exchange.allocateFork(), exchange);
151
+ return exchange.allocateSource();
188
152
  }
189
153
 
190
- constructor(parent, fork, exchange, index = -1, holding = false) {
191
- super(parent);
154
+ constructor(head, exchange, index = -1, holding = false) {
155
+ if (!head || !exchange) throw new Error();
192
156
 
193
- if (!fork || !exchange) throw new Error();
194
-
195
- this.fork = fork;
157
+ this.head = head;
196
158
  this.exchange = exchange;
197
159
  this.index = index;
198
160
  this.holding = holding;
@@ -201,27 +163,17 @@ export const Source = class BABLRSource extends WeakStackFrame {
201
163
  }
202
164
 
203
165
  get value() {
204
- return this.holding ? null : this.fork.value;
166
+ return this.holding ? null : this.head.step?.value;
205
167
  }
206
168
 
207
169
  get done() {
208
- return this.fork.done;
170
+ return !this.exchange || this.head.step?.done;
209
171
  }
210
172
 
211
173
  get atGap() {
212
174
  return this.holding || (!this.done && this.value == null);
213
175
  }
214
176
 
215
- advance(n = 1) {
216
- return new Array(n).fill(null).reduce((acc) => {
217
- return maybeWait(acc, () => {
218
- this.fork.advance();
219
- this.index++;
220
- return this.fork.step;
221
- });
222
- }, this.fork.step);
223
- }
224
-
225
177
  shift() {
226
178
  this.holding = true;
227
179
  }
@@ -230,30 +182,78 @@ export const Source = class BABLRSource extends WeakStackFrame {
230
182
  this.holding = false;
231
183
  }
232
184
 
185
+ advance(n = 1) {
186
+ let { exchange } = this;
187
+
188
+ if (this.holding) {
189
+ this.holding = false;
190
+ n--;
191
+ }
192
+
193
+ return new Array(n).fill(null).reduce((acc) => {
194
+ return maybeWait(acc, () => {
195
+ if (this.done) {
196
+ throw new Error('cannot advance a source that is done');
197
+ } else {
198
+ let { head } = this;
199
+
200
+ let nextItem = head.next;
201
+
202
+ if (!this.done) {
203
+ if (!nextItem) {
204
+ nextItem = exchange.fetch();
205
+ }
206
+
207
+ this.head = head = nextItem;
208
+
209
+ if (head.step?.done) {
210
+ exchange.releaseSource(this);
211
+ }
212
+ }
213
+ }
214
+
215
+ this.index++;
216
+ return this.head.step;
217
+ });
218
+ }, this.head.step);
219
+ }
220
+
233
221
  branch() {
234
- const { fork, exchange, index, holding } = this;
235
- return this.push(exchange.allocateFork(fork), exchange, index, holding);
222
+ const { exchange } = this;
223
+
224
+ return exchange ? exchange.allocateSource(this) : this;
236
225
  }
237
226
 
238
227
  release() {
239
- this.fork.return();
228
+ const { exchange, head } = this;
229
+
230
+ if (exchange) exchange.releaseSource(this);
231
+
232
+ const step = { value: undefined, done: true };
233
+
234
+ this.head = { ...head, step };
235
+
236
+ return step;
240
237
  }
241
238
 
242
239
  accept(source) {
243
- this.release();
244
- this.fork = source.fork;
240
+ this.head = source.head;
245
241
  this.index = source.index;
246
242
  this.holding = source.holding;
243
+
244
+ source.release();
247
245
  }
248
246
 
249
247
  reject() {
250
248
  this.release();
251
249
  }
252
250
 
251
+ [Symbol.iterator]() {
252
+ return SourceIterator.from(this);
253
+ }
254
+
253
255
  [Symbol.for('@@streamIterator')]() {
254
- return (this.holding ? emptyStreamIterator() : this.fork.clone())[
255
- Symbol.for('@@streamIterator')
256
- ]();
256
+ return SourceIterator.from(this);
257
257
  }
258
258
 
259
259
  formatIndex() {