@bablr/agast-vm-helpers 0.6.1 → 0.7.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.
@@ -0,0 +1,69 @@
1
+ import {
2
+ EmbeddedNode,
3
+ EmbeddedMatcher,
4
+ EmbeddedRegex,
5
+ EmbeddedTag,
6
+ EmbeddedObject,
7
+ EmbeddedInstruction,
8
+ } from './symbols.js';
9
+
10
+ const isObject = (val) => val !== null && typeof val === 'object';
11
+
12
+ const { freeze } = Object;
13
+
14
+ export const buildCall = (verb, ...args) => {
15
+ return freeze({ verb, arguments: freeze(args) });
16
+ };
17
+
18
+ export const buildEmbeddedObject = (obj) => {
19
+ if (!isObject(obj)) throw new Error();
20
+ return freeze({ type: EmbeddedObject, value: freeze(obj) });
21
+ };
22
+
23
+ export const buildEmbeddedMatcher = (matcher) => {
24
+ if (!isObject(matcher)) throw new Error();
25
+ return freeze({ type: EmbeddedMatcher, value: matcher });
26
+ };
27
+
28
+ export const buildEmbeddedInstruction = (instr) => {
29
+ if (!isObject(instr)) throw new Error();
30
+ return freeze({ type: EmbeddedInstruction, value: instr });
31
+ };
32
+
33
+ export const buildEmbeddedRegex = (re) => {
34
+ if (!isObject(re)) throw new Error();
35
+ return freeze({ type: EmbeddedRegex, value: re });
36
+ };
37
+
38
+ export const buildEmbeddedTag = (tag) => {
39
+ if (!isObject(tag)) throw new Error();
40
+ return freeze({ type: EmbeddedTag, value: tag });
41
+ };
42
+
43
+ export const buildEffect = (value) => {
44
+ return freeze({ type: 'Effect', value });
45
+ };
46
+
47
+ export const buildWriteEffect = (text, options = {}) => {
48
+ return buildEffect(
49
+ freeze({
50
+ verb: 'write',
51
+ value: buildEmbeddedObject(freeze({ text, options: buildEmbeddedObject(freeze(options)) })),
52
+ }),
53
+ );
54
+ };
55
+
56
+ export const buildAnsiPushEffect = (spans = '') => {
57
+ return buildEffect(
58
+ freeze({
59
+ verb: 'ansi-push',
60
+ value: buildEmbeddedObject(
61
+ freeze({ spans: spans === '' ? freeze([]) : freeze(spans.split(' ')) }),
62
+ ),
63
+ }),
64
+ );
65
+ };
66
+
67
+ export const buildAnsiPopEffect = () => {
68
+ return buildEffect(freeze({ verb: 'ansi-pop', value: undefined }));
69
+ };
package/lib/deembed.js CHANGED
@@ -4,7 +4,8 @@ import {
4
4
  EmbeddedTag,
5
5
  EmbeddedMatcher,
6
6
  EmbeddedRegex,
7
- } from '@bablr/agast-helpers/symbols';
7
+ EmbeddedInstruction,
8
+ } from './symbols.js';
8
9
 
9
10
  export const getEmbeddedObject = (expr) => {
10
11
  if (!expr) return expr;
@@ -24,6 +25,12 @@ export const getEmbeddedMatcher = (expr) => {
24
25
  return expr.value;
25
26
  };
26
27
 
28
+ export const getEmbeddedInstruction = (expr) => {
29
+ if (!expr) return expr;
30
+ if (expr.type !== EmbeddedInstruction) throw new Error();
31
+ return expr.value;
32
+ };
33
+
27
34
  export const getEmbeddedRegex = (expr) => {
28
35
  if (!expr) return expr;
29
36
  if (expr.type !== EmbeddedRegex) throw new Error();
package/lib/index.js CHANGED
@@ -5,7 +5,6 @@ import {
5
5
  nodeFlags,
6
6
  printType,
7
7
  buildGapTag,
8
- buildEmbeddedMatcher,
9
8
  isNullNode,
10
9
  buildNullTag,
11
10
  buildStubNode,
@@ -13,12 +12,12 @@ import {
13
12
  buildReferenceTag,
14
13
  } from '@bablr/agast-helpers/tree';
15
14
  import * as btree from '@bablr/agast-helpers/btree';
15
+ import * as sumtree from '@bablr/agast-helpers/sumtree';
16
16
  import {
17
17
  buildCloseNodeTag,
18
18
  buildLiteralTag,
19
19
  buildDoctypeTag,
20
20
  referenceFlags,
21
- buildEmbeddedRegex,
22
21
  } from '@bablr/agast-helpers/builders';
23
22
  import {
24
23
  DoctypeTag,
@@ -27,9 +26,10 @@ import {
27
26
  ShiftTag,
28
27
  GapTag,
29
28
  NullTag,
30
- ArrayInitializerTag,
29
+ InitializerTag,
31
30
  LiteralTag,
32
31
  } from '@bablr/agast-helpers/symbols';
32
+ import { buildEmbeddedMatcher, buildEmbeddedRegex } from './builders.js';
33
33
 
34
34
  const { freeze } = Object;
35
35
 
@@ -59,11 +59,13 @@ export const shouldBranch = (effects) => {
59
59
  };
60
60
 
61
61
  export const reifyNodeFlags = (flags) => {
62
- let { tokenToken, hasGapToken } = flags.properties;
62
+ let { tokenToken, hasGapToken, fragmentToken, coverFragmentToken } = flags.properties;
63
63
 
64
64
  return {
65
65
  token: !!(tokenToken && reifyExpression(tokenToken.node)),
66
66
  hasGap: !!(hasGapToken && reifyExpression(hasGapToken.node)),
67
+ fragment: !!(fragmentToken && reifyExpression(fragmentToken.node)),
68
+ cover: !!(coverFragmentToken && reifyExpression(coverFragmentToken.node)),
67
69
  };
68
70
  };
69
71
 
@@ -115,20 +117,20 @@ export const buildFragmentChildren = (node) => {
115
117
  open = reifyExpression(open.node);
116
118
  close = reifyExpression(close.node);
117
119
 
118
- built = btree.push(built, open);
120
+ built = sumtree.push(built, open);
119
121
 
120
- for (const child of btree.traverse(children)) {
122
+ for (const child of sumtree.traverse(children)) {
121
123
  if (child.node.type !== Symbol.for('Property')) throw new Error('umimplemented');
122
124
 
123
125
  let { reference } = child.node.properties;
124
126
 
125
127
  reference = reifyExpression(reference.node);
126
128
 
127
- built = btree.push(built, reference);
128
- built = btree.push(built, buildGapTag());
129
+ built = sumtree.push(built, reference);
130
+ built = sumtree.push(built, buildGapTag());
129
131
  }
130
132
 
131
- built = btree.push(built, close);
133
+ built = sumtree.push(built, close);
132
134
 
133
135
  return built;
134
136
  };
@@ -144,25 +146,25 @@ export const buildChildren = (node) => {
144
146
  close = reifyExpression(close?.node);
145
147
 
146
148
  if (selfClosing) {
147
- built = btree.push(built, open);
149
+ built = sumtree.push(built, open);
148
150
  if (!isNull(intrinsicValue?.node)) {
149
- built = btree.push(built, buildLiteralTag(intrinsicValue.node));
151
+ built = sumtree.push(built, buildLiteralTag(intrinsicValue.node));
150
152
  }
151
- built = btree.push(built, buildCloseNodeTag());
153
+ built = sumtree.push(built, buildCloseNodeTag());
152
154
  } else {
153
- built = btree.push(built, open);
154
- for (const child of btree.traverse(children)) {
155
+ built = sumtree.push(built, open);
156
+ for (const child of sumtree.traverse(children)) {
155
157
  if (child.node.type !== Symbol.for('Property')) throw new Error('umimplemented');
156
158
 
157
159
  let { reference } = child.node.properties;
158
160
 
159
161
  reference = reifyExpression(reference.node);
160
162
 
161
- built = btree.push(built, reference);
162
- built = btree.push(built, buildGapTag());
163
+ built = sumtree.push(built, reference);
164
+ built = sumtree.push(built, buildGapTag());
163
165
  }
164
166
 
165
- built = btree.push(built, close);
167
+ built = sumtree.push(built, close);
166
168
  }
167
169
 
168
170
  return built;
@@ -193,7 +195,7 @@ export const reifyExpression = (node) => {
193
195
  flags: nodeFlags,
194
196
  language: attributes.bablrLanguage,
195
197
  type: null,
196
- children: btree.addAt(
198
+ children: sumtree.addAt(
197
199
  0,
198
200
  buildFragmentChildren(node.properties.tree.node),
199
201
  buildDoctypeTag(attributes),
@@ -269,7 +271,7 @@ export const reifyExpression = (node) => {
269
271
  }
270
272
 
271
273
  case 'Identifier': {
272
- return getCooked(node);
274
+ return getCooked(node.properties.content.node);
273
275
  }
274
276
 
275
277
  case 'IdentifierPath': {
@@ -310,8 +312,8 @@ export const reifyExpression = (node) => {
310
312
  case 'GapTag':
311
313
  return { type: GapTag, value: undefined };
312
314
 
313
- case 'ArrayInitializerTag':
314
- return { type: ArrayInitializerTag, value: undefined };
315
+ case 'InitializerTag':
316
+ return { type: InitializerTag, value: undefined };
315
317
 
316
318
  case 'NullTag':
317
319
  return { type: NullTag, value: undefined };
@@ -355,7 +357,7 @@ export const reifyExpression = (node) => {
355
357
  type =
356
358
  type.node.type === Symbol.for('String')
357
359
  ? getCooked(type.node.properties.content.node)
358
- : getCooked(type.node);
360
+ : reifyExpression(type.node);
359
361
  attributes = attributes ? reifyExpression(attributes.node) : {};
360
362
  intrinsicValue = intrinsicValue && reifyExpression(intrinsicValue.node);
361
363
 
@@ -394,11 +396,7 @@ export const reifyExpression = (node) => {
394
396
  case 'ReferenceMatcher': {
395
397
  let { name, openIndexToken, flags } = node.properties;
396
398
 
397
- name =
398
- name &&
399
- (name.node.type === Symbol.for('Identifier')
400
- ? reifyExpression(name.node)
401
- : getCooked(name.node));
399
+ name = name && reifyExpression(name.node);
402
400
  let isArray = !isNull(openIndexToken?.node);
403
401
  flags = (flags && reifyReferenceFlags(flags?.node)) || referenceFlags;
404
402
 
@@ -443,10 +441,13 @@ export const reifyExpression = (node) => {
443
441
  return [...btree.traverse(elements)].map((el) => reifyExpression(el.node));
444
442
  }
445
443
 
446
- case 'LiteralTag':
447
- case 'Identifier':
444
+ case 'Punctuator':
445
+ case 'Keyword':
448
446
  return getCooked(node);
449
447
 
448
+ case 'Identifier':
449
+ return getCooked(node.properties.content.node);
450
+
450
451
  case 'Boolean': {
451
452
  // prettier-ignore
452
453
  switch (getCooked(node.properties.sigilToken.node)) {
@@ -459,6 +460,12 @@ export const reifyExpression = (node) => {
459
460
  case 'Null':
460
461
  return null;
461
462
 
463
+ case 'NotANumber':
464
+ return NaN;
465
+
466
+ case 'Undefined':
467
+ return undefined;
468
+
462
469
  default:
463
470
  throw new Error('bad expression');
464
471
  }
package/lib/print.js ADDED
@@ -0,0 +1,66 @@
1
+ import {
2
+ printSource,
3
+ printTag,
4
+ printExpression as printExpression_,
5
+ } from '@bablr/agast-helpers/tree';
6
+ import {
7
+ EmbeddedNode,
8
+ EmbeddedTag,
9
+ EmbeddedObject,
10
+ EmbeddedMatcher,
11
+ EmbeddedRegex,
12
+ EmbeddedInstruction,
13
+ } from './symbols.js';
14
+
15
+ export const printEmbedded = (value) => {
16
+ switch (value.type) {
17
+ case EmbeddedTag:
18
+ return `t\`${printTag(value.value)}\``;
19
+
20
+ case EmbeddedMatcher:
21
+ return `m\`${printSource(value.value)}\``;
22
+
23
+ case EmbeddedRegex:
24
+ return `re\`${printSource(value.value)}\``;
25
+
26
+ case EmbeddedInstruction:
27
+ return `i\`${printCall(value.value)}\``;
28
+
29
+ case EmbeddedObject: {
30
+ return printObject(value.value);
31
+ }
32
+
33
+ case EmbeddedNode: {
34
+ return printSource(value.value);
35
+ }
36
+
37
+ default:
38
+ throw new Error();
39
+ }
40
+ };
41
+
42
+ export const printObject = (obj) => {
43
+ let entries = Object.entries(obj);
44
+ return entries.length
45
+ ? `{ ${entries.map(([k, v]) => `${k}: ${printExpression(v)}`).join(', ')} }`
46
+ : '{}';
47
+ };
48
+
49
+ export const printArray = (arr) => `[${arr.map((v) => printExpression(v)).join(', ')}]`;
50
+
51
+ export const printCall = (call) => {
52
+ let { verb, arguments: args } = call;
53
+ return `${verb}${`(${args.map((v) => printExpression(v)).join(', ')})`}`;
54
+ };
55
+
56
+ export const printExpression = (expr) => {
57
+ if (['string', 'symbol', 'boolean', 'number'].includes(typeof expr) || expr == null) {
58
+ return printExpression_(expr);
59
+ } else if (Array.isArray(expr)) {
60
+ return printArray(expr);
61
+ } else if (typeof expr === 'object') {
62
+ return printEmbedded(expr);
63
+ } else {
64
+ throw new Error();
65
+ }
66
+ };
package/lib/stream.js ADDED
@@ -0,0 +1,231 @@
1
+ import { Coroutine } from '@bablr/coroutine';
2
+ import {
3
+ InitializerTag,
4
+ CloseNodeTag,
5
+ EmbeddedObject,
6
+ GapTag,
7
+ NullTag,
8
+ OpenNodeTag,
9
+ ReferenceTag,
10
+ TokenGroup,
11
+ } from './symbols.js';
12
+ import {
13
+ getStreamIterator,
14
+ prettyGroupTags,
15
+ printSelfClosingNodeTag,
16
+ printTag,
17
+ StreamIterable,
18
+ } from '@bablr/agast-helpers/stream';
19
+ import { buildWriteEffect } from './builders.js';
20
+ import { getCooked } from '@bablr/agast-helpers/tree';
21
+
22
+ const getEmbeddedObject = (obj) => {
23
+ if (obj.type !== EmbeddedObject) throw new Error();
24
+ return obj.value;
25
+ };
26
+
27
+ function* __generateStandardOutput(tags) {
28
+ const co = new Coroutine(getStreamIterator(tags));
29
+
30
+ for (;;) {
31
+ co.advance();
32
+
33
+ if (co.current instanceof Promise) {
34
+ co.current = yield co.current;
35
+ }
36
+ if (co.done) break;
37
+
38
+ const tag = co.value;
39
+
40
+ if (tag.type === 'Effect') {
41
+ const effect = tag.value;
42
+ if (effect.verb === 'write') {
43
+ const writeEffect = getEmbeddedObject(effect.value);
44
+ if (writeEffect.stream == null || writeEffect.stream === 1) {
45
+ yield* writeEffect.text;
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ export const generateStandardOutput = (tags) => new StreamIterable(__generateStandardOutput(tags));
53
+
54
+ function* __generateAllOutput(tags) {
55
+ const co = new Coroutine(getStreamIterator(tags));
56
+
57
+ let currentStream = null;
58
+
59
+ for (;;) {
60
+ co.advance();
61
+
62
+ if (co.current instanceof Promise) {
63
+ co.current = yield co.current;
64
+ }
65
+ if (co.done) break;
66
+
67
+ const tag = co.value;
68
+
69
+ if (tag.type === 'Effect') {
70
+ const effect = tag.value;
71
+ if (effect.verb === 'write') {
72
+ const writeEffect = getEmbeddedObject(effect.value);
73
+ const prevStream = currentStream;
74
+ currentStream = getEmbeddedObject(writeEffect.options).stream || 1;
75
+ if (
76
+ prevStream &&
77
+ (prevStream !== currentStream || currentStream === 2) &&
78
+ !writeEffect.text.startsWith('\n')
79
+ ) {
80
+ yield* '\n';
81
+ }
82
+ yield* writeEffect.text;
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ export const generateAllOutput = (tags) => new StreamIterable(__generateAllOutput(tags));
89
+
90
+ function* __writeCSTMLStrategy(tags) {
91
+ if (!tags) {
92
+ yield buildWriteEffect('<//>');
93
+ return;
94
+ }
95
+
96
+ let prevTag = null;
97
+
98
+ const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
99
+
100
+ for (;;) {
101
+ co.advance();
102
+
103
+ if (co.current instanceof Promise) {
104
+ co.current = yield co.current;
105
+ }
106
+ if (co.done) break;
107
+
108
+ const tag = co.value;
109
+
110
+ if (tag.type === ReferenceTag && prevTag.type === NullTag) {
111
+ yield buildWriteEffect(' ');
112
+ }
113
+
114
+ if (tag.type === 'Effect') {
115
+ yield tag;
116
+
117
+ continue;
118
+ }
119
+
120
+ if (tag.type === TokenGroup) {
121
+ const intrinsicValue = getCooked(tag.value);
122
+ yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
123
+ } else {
124
+ yield buildWriteEffect(printTag(tag));
125
+ }
126
+
127
+ prevTag = tag;
128
+ }
129
+
130
+ yield buildWriteEffect('\n');
131
+ }
132
+
133
+ export const writeCSTMLStrategy = (tags, options = {}) =>
134
+ new StreamIterable(__writeCSTMLStrategy(tags, options));
135
+
136
+ function* __writePrettyCSTMLStrategy(tags, options) {
137
+ let { indent = ' ', emitEffects = false, inline: inlineOption = true } = options;
138
+
139
+ if (!tags) {
140
+ yield buildWriteEffect('<//>');
141
+ return;
142
+ }
143
+
144
+ const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
145
+ let indentLevel = 0;
146
+ let first = true;
147
+ let inline = false;
148
+ let ref = null;
149
+
150
+ for (;;) {
151
+ co.advance();
152
+
153
+ if (co.done) break;
154
+
155
+ if (co.current instanceof Promise) {
156
+ co.current = yield co.current;
157
+ }
158
+
159
+ const tag = co.value;
160
+
161
+ if (tag.type === 'Effect') {
162
+ const effect = tag.value;
163
+ if (emitEffects && effect.verb === 'write') {
164
+ const writeEffect = getEmbeddedObject(effect.value);
165
+ yield buildWriteEffect(
166
+ (first ? '' : '\n') + writeEffect.text,
167
+ getEmbeddedObject(writeEffect.options),
168
+ );
169
+
170
+ inline = false;
171
+ first = false;
172
+ } else {
173
+ yield tag;
174
+ }
175
+ continue;
176
+ }
177
+
178
+ inline =
179
+ inlineOption &&
180
+ inline &&
181
+ ref &&
182
+ (tag.type === NullTag ||
183
+ tag.type === GapTag ||
184
+ tag.type === InitializerTag ||
185
+ tag.type === TokenGroup);
186
+
187
+ if (!first && !inline) {
188
+ yield buildWriteEffect('\n');
189
+ }
190
+
191
+ if (tag.type === CloseNodeTag) {
192
+ ref = null;
193
+ if (indentLevel === 0) {
194
+ throw new Error('imbalanced tag stack');
195
+ }
196
+
197
+ indentLevel--;
198
+ }
199
+
200
+ if (!inline) {
201
+ yield buildWriteEffect(indent.repeat(indentLevel));
202
+ } else {
203
+ yield buildWriteEffect(' ');
204
+ }
205
+
206
+ if (tag.type === TokenGroup) {
207
+ ref = null;
208
+ const intrinsicValue = tag.value[0].value.flags.token ? getCooked(tag.value) : null;
209
+ yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
210
+ } else {
211
+ yield buildWriteEffect(printTag(tag));
212
+ }
213
+
214
+ if (tag.type === ReferenceTag) {
215
+ inline = true;
216
+ ref = tag;
217
+ }
218
+
219
+ if (tag.type === OpenNodeTag) {
220
+ indentLevel++;
221
+ }
222
+
223
+ first = false;
224
+ }
225
+
226
+ yield buildWriteEffect('\n');
227
+ }
228
+
229
+ export const writePrettyCSTMLStrategy = (tags, options = {}) => {
230
+ return new StreamIterable(__writePrettyCSTMLStrategy(tags, options));
231
+ };
package/lib/symbols.js ADDED
@@ -0,0 +1,10 @@
1
+ export * from '@bablr/agast-helpers/symbols';
2
+
3
+ export const node = Symbol.for('@bablr/node');
4
+ export const fragment = Symbol.for('@bablr/fragment');
5
+
6
+ export const EmbeddedInstruction = Symbol.for('EmbeddedInstruction');
7
+ export const EmbeddedMatcher = Symbol.for('EmbeddedMatcher');
8
+ export const EmbeddedRegex = Symbol.for('EmbeddedRegex');
9
+ export const EmbeddedTag = Symbol.for('EmbeddedTag');
10
+ export const EmbeddedObject = Symbol.for('EmbeddedObject');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bablr/agast-vm-helpers",
3
3
  "description": "Helper functions for working with the BABLR VM",
4
- "version": "0.6.1",
4
+ "version": "0.7.0",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
7
  "files": [
@@ -15,11 +15,15 @@
15
15
  "./facades": "./lib/facades.js",
16
16
  "./internal-builders": "./lib/internal-builders.js",
17
17
  "./iterable": "./lib/iterable.js",
18
- "./languages": "./lib/languages.js"
18
+ "./languages": "./lib/languages.js",
19
+ "./print": "./lib/print.js",
20
+ "./stream": "./lib/stream.js",
21
+ "./symbols": "./lib/symbols.js"
19
22
  },
20
23
  "sideEffects": false,
21
24
  "dependencies": {
22
- "@bablr/agast-helpers": "0.6.1"
25
+ "@bablr/agast-helpers": "0.7.0",
26
+ "@bablr/coroutine": "0.1.0"
23
27
  },
24
28
  "devDependencies": {
25
29
  "@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#49f5952efed27f94ee9b94340eb1563c440bf64e",