@bablr/agast-vm 0.1.2 → 0.1.3

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/README.md CHANGED
@@ -2,19 +2,11 @@
2
2
 
3
3
  The agAST VM provides consistency guarantees when with CSTML documents to parse or transform code. It has no language-specific functionality of any kind. Instead it acts as a streaming traversal engine for CSTML.
4
4
 
5
- ## Why
6
-
7
- The goal of this project is to transition editors towards being a lot more like web browsers. You can have many of them, and they can be written in a variety of languages (though many share internals). You can even have a terminal browser like Lynx that does presentation very differently, yet it is still possible (if not trivial) to write a website once that will run on all (er, most) web browsers.
8
-
9
- If the parallel is not immediately obvious, try thinking about it this way: a webapp is really more or less a set of automated tools for editing a DOM tree. As programmers we have all these amazing web libraries and frameworks that can exist because at the end of the day everything comes down to editing a shared DOM tree. There's a great explanation of those dynamics here: https://glazkov.com/2024/01/02/the-protocol-force/
10
-
11
- If a code-DOM existed and was shared by all IDEs there would spring up a rich ecosystem of tools for accomplishing many kinds of tree alterations. For example it could become common for library authors to publish codemods that could help any project upgrade past breaking changes in its APIs!
12
-
13
5
  ## API
14
6
 
15
- The VM responds to several instructions, but its primary API is `advance(token)`, where `token` may be a `StartFragmentTag`, `EndFragmentTag`, `StartNodeTag`, `EndNodeTag`, `Literal`, `Reference`, or `Gap`.
7
+ The VM responds to several instructions, but its primary API is `advance(token)`, where `token` may be a `OpenFragmentTag`, `CloseFragmentTag`, `OpenNodeTag`, `CloseNodeTag`, `Literal`, `Reference`, or `Gap`.
16
8
 
17
- The VM requires the basic invariants of CSTML to be followed, for example that `Reference` must be followed by either a `StartNodeTag` or a `Gap`. In fact, `agast-vm` is the reference implementation of these invariants.
9
+ The VM requires the basic invariants of CSTML to be followed, for example that `Reference` must be followed by either a `OpenNodeTag` or a `Gap`. In fact, `agast-vm` is the reference implementation of these invariants.
18
10
 
19
11
  The VM supports `branch()`, `accept()`, and `reject()` instructions, which allow a series of instructions to have their effects applied or discarded together in a kind of transaction.
20
12
 
@@ -23,25 +15,20 @@ Finally the VM supports `bindAttribute(key, value)`. A node's attributes start u
23
15
  Here are the basic types used by the VM:
24
16
 
25
17
  ```ts
26
- type Token = StartFragmentTag | EndFragmentTag | StartNodeTag | EndNodeTag | Literal | Reference | Gap;
18
+ type Token = OpenFragmentTag | CloseFragmentTag | OpenNodeTag | CloseNodeTag | Literal | Reference | Gap;
27
19
 
28
- type StartFragmentTag {
29
- type: 'StartFragmentTag',
30
- value: {
31
- flags: {
32
- trivia: boolean
33
- },
34
- language: string,
35
- }
20
+ type OpenFragmentTag {
21
+ type: 'OpenFragmentTag',
22
+ value: null
36
23
  }
37
24
 
38
- type EndFragmentTag {
39
- type: 'EndFragmentTag',
25
+ type CloseFragmentTag {
26
+ type: 'CloseFragmentTag',
40
27
  value: null
41
28
  }
42
29
 
43
- type StartNodeTag {
44
- type: 'StartNodeTag',
30
+ type OpenNodeTag {
31
+ type: 'OpenNodeTag',
45
32
  value: {
46
33
  flags: {
47
34
  token: boolean,
@@ -54,9 +41,12 @@ type StartNodeTag {
54
41
  }
55
42
  }
56
43
 
57
- type EndNodeTag {
58
- type: 'EndNodeTag',
59
- value: null
44
+ type CloseNodeTag {
45
+ type: 'CloseNodeTag',
46
+ value: {
47
+ language: string,
48
+ type: string,
49
+ }
60
50
  }
61
51
 
62
52
  type Literal {
package/lib/evaluate.js CHANGED
@@ -19,7 +19,7 @@ import { facades } from './facades.js';
19
19
 
20
20
  export const evaluate = (ctx, strategy) => new StreamIterable(__evaluate(ctx, strategy));
21
21
 
22
- const __evaluate = function* agastStrategy(ctx, strategy) {
22
+ const __evaluate = function* agast(ctx, strategy) {
23
23
  let s = State.from(ctx);
24
24
 
25
25
  const co = new Coroutine(getStreamIterator(strategy(facades.get(ctx), facades.get(s))));
@@ -66,7 +66,7 @@ const __evaluate = function* agastStrategy(ctx, strategy) {
66
66
  }
67
67
 
68
68
  case 'advance': {
69
- const { arguments: { 0: terminal } = [] } = instr;
69
+ const { arguments: { 0: terminal, 1: options } = [] } = instr;
70
70
 
71
71
  switch (terminal?.type || 'Null') {
72
72
  case 'DoctypeTag': {
@@ -163,7 +163,7 @@ const __evaluate = function* agastStrategy(ctx, strategy) {
163
163
  case 'OpenFragmentTag': {
164
164
  const openTag = buildFragmentOpenTag();
165
165
 
166
- s.node = Node.from(s.path, openTag);
166
+ s.node = Node.from(openTag);
167
167
 
168
168
  yield* s.emit(openTag);
169
169
 
@@ -182,16 +182,9 @@ const __evaluate = function* agastStrategy(ctx, strategy) {
182
182
 
183
183
  case 'OpenNodeTag': {
184
184
  const { flags, language, type, intrinsicValue, attributes } = terminal.value;
185
+ const { unboundAttributes } = options || {};
185
186
  const reference = s.result;
186
- const boundAttributes = Object.entries(attributes).filter((a) => a[1].type !== 'Gap');
187
- const unboundAttributes = Object.entries(attributes).filter((a) => a[1].type === 'Gap');
188
- const openTag = buildNodeOpenTag(
189
- flags,
190
- language,
191
- type,
192
- intrinsicValue,
193
- Object.fromEntries(boundAttributes),
194
- );
187
+ const openTag = buildNodeOpenTag(flags, language, type, intrinsicValue, attributes);
195
188
 
196
189
  if (!flags.trivia && !flags.escape && s.path.depth > 1) {
197
190
  if (reference.type !== 'Reference' && reference.type !== 'OpenFragmentTag') {
@@ -199,29 +192,25 @@ const __evaluate = function* agastStrategy(ctx, strategy) {
199
192
  }
200
193
  }
201
194
 
202
- if (intrinsicValue && !flags.token) {
203
- throw new Error('intrinsic nodes must occur inside tokens');
204
- }
195
+ const { flags: openFlags } = openTag.value;
205
196
 
206
- if (!flags.expression) {
207
- const { flags } = openTag.value;
197
+ if (!(openFlags.trivia || openFlags.escape) && !s.path.depth) {
198
+ const tag = buildReference('root', false);
199
+ s.path = s.path.push(ctx, tag);
200
+ s.node.resolver.consume(tag);
201
+ }
208
202
 
209
- if (!(flags.trivia || flags.escape) && !s.path.depth) {
210
- const tag = buildReference('root', false);
211
- s.path = s.path.push(ctx, tag);
212
- s.node.resolver.consume(tag);
213
- }
214
- } else if (!intrinsicValue) {
203
+ if (flags.expression && !flags.intrinsic) {
215
204
  s.expressionDepth++;
216
205
  }
217
206
 
218
- const newNode = s.node.push(s.path, openTag);
207
+ const newNode = s.node.push(openTag);
219
208
 
220
- newNode.unboundAttributes = new Set(unboundAttributes.map((e) => e[0]));
209
+ newNode.unboundAttributes = new Set(unboundAttributes);
221
210
 
222
211
  ctx.tagNodes.set(openTag, newNode);
223
212
 
224
- if (!flags.intrinsic) {
213
+ if (!intrinsicValue) {
225
214
  s.node = newNode;
226
215
  } else {
227
216
  s.path = s.path.parent;
@@ -267,6 +256,7 @@ const __evaluate = function* agastStrategy(ctx, strategy) {
267
256
  if (!flags.escape && !flags.trivia) {
268
257
  const { name: refName, isArray } = s.path.reference.value;
269
258
 
259
+ // is this right?
270
260
  if (s.path.depth > 2) {
271
261
  const { properties } = s.node.parent;
272
262
 
package/lib/path.js CHANGED
@@ -97,12 +97,11 @@ export const PathFacade = class AgastPathFacade {
97
97
  };
98
98
 
99
99
  export const Node = class AgastNode extends WeakStackFrame {
100
- static from(path, openTag) {
101
- return AgastNode.create(path, openTag);
100
+ static from(openTag) {
101
+ return AgastNode.create(openTag);
102
102
  }
103
103
 
104
104
  constructor(
105
- path,
106
105
  openTag,
107
106
  closeTag = null,
108
107
  properties = new Map(),
@@ -111,7 +110,6 @@ export const Node = class AgastNode extends WeakStackFrame {
111
110
  ) {
112
111
  super();
113
112
 
114
- this.path = path;
115
113
  this.openTag = openTag;
116
114
  this.closeTag = closeTag;
117
115
  this.properties = properties;
@@ -119,10 +117,6 @@ export const Node = class AgastNode extends WeakStackFrame {
119
117
  this.unboundAttributes = unboundAttributes;
120
118
  }
121
119
 
122
- get context() {
123
- return this.path.context;
124
- }
125
-
126
120
  get language() {
127
121
  return this.openTag.value?.language;
128
122
  }
@@ -140,10 +134,9 @@ export const Node = class AgastNode extends WeakStackFrame {
140
134
  }
141
135
 
142
136
  branch() {
143
- const { path, openTag, closeTag, properties, resolver, unboundAttributes } = this;
137
+ const { openTag, closeTag, properties, resolver, unboundAttributes } = this;
144
138
 
145
- return this.push(
146
- path,
139
+ return this.replace(
147
140
  openTag,
148
141
  closeTag,
149
142
  new Map(properties), // there is probably a better way
package/lib/state.js CHANGED
@@ -182,6 +182,7 @@ export const State = class AgastState extends WeakStackFrame {
182
182
 
183
183
  parent.result = this.result;
184
184
  parent.held = this.held;
185
+ parent.path = this.path;
185
186
  parent.expressionDepth = this.expressionDepth;
186
187
 
187
188
  return parent;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bablr/agast-vm",
3
3
  "description": "A VM providing DOM-like guarantees about agAST trees",
4
- "version": "0.1.2",
4
+ "version": "0.1.3",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
7
  "files": [
@@ -15,8 +15,8 @@
15
15
  "test": "node ./test/runner.js"
16
16
  },
17
17
  "dependencies": {
18
- "@bablr/agast-helpers": "0.1.1",
19
- "@bablr/agast-vm-helpers": "0.1.1",
18
+ "@bablr/agast-helpers": "0.1.5",
19
+ "@bablr/agast-vm-helpers": "0.1.3",
20
20
  "@bablr/coroutine": "0.1.0",
21
21
  "@bablr/weak-stack": "0.1.0"
22
22
  },