@bablr/boot 0.4.0 → 0.6.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/builders.js +122 -55
- package/lib/index.js +131 -39
- package/lib/index.mjs +15 -1
- package/lib/languages/cstml.js +54 -40
- package/lib/languages/instruction.js +1 -1
- package/lib/languages/regex.js +3 -3
- package/lib/languages/spamex.js +41 -8
- package/lib/match.js +1 -1
- package/lib/miniparser.js +18 -6
- package/lib/print.js +196 -128
- package/package.json +3 -2
- package/shorthand.macro.js +108 -69
- package/lib/symbols.js +0 -4
package/lib/print.js
CHANGED
|
@@ -1,169 +1,228 @@
|
|
|
1
1
|
const emptyStack = require('@iter-tools/imm-stack');
|
|
2
|
+
const sym = require('@bablr/boot-helpers/symbols');
|
|
3
|
+
const {
|
|
4
|
+
DoctypeTag,
|
|
5
|
+
OpenNodeTag,
|
|
6
|
+
CloseNodeTag,
|
|
7
|
+
ReferenceTag,
|
|
8
|
+
GapTag,
|
|
9
|
+
NullTag,
|
|
10
|
+
ArrayTag,
|
|
11
|
+
LiteralTag,
|
|
12
|
+
EmbeddedNode,
|
|
13
|
+
} = require('@bablr/boot-helpers/symbols');
|
|
14
|
+
const btree = require('@bablr/boot-helpers/btree');
|
|
2
15
|
|
|
3
16
|
const { isInteger, isFinite } = Number;
|
|
4
17
|
const { isArray } = Array;
|
|
5
18
|
const isString = (val) => typeof val === 'string';
|
|
6
19
|
const isNumber = (val) => typeof val === 'number';
|
|
7
20
|
|
|
8
|
-
const { freeze } = Object;
|
|
21
|
+
const { freeze, hasOwn } = Object;
|
|
9
22
|
|
|
10
23
|
const get = (node, path) => {
|
|
24
|
+
const { type, properties } = node;
|
|
11
25
|
const { 1: name, 2: index } = /^([^\.]+)(?:\.(\d+))?/.exec(path) || [];
|
|
12
26
|
|
|
27
|
+
if (!hasOwn(properties, name)) {
|
|
28
|
+
throw new Error(`Cannot find {name: ${name}} on node of {type: ${type}}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
13
31
|
if (index != null) {
|
|
14
|
-
return
|
|
32
|
+
return btree.getAt(parseInt(index, 10), properties[name]);
|
|
15
33
|
} else {
|
|
16
|
-
return
|
|
34
|
+
return properties[name];
|
|
17
35
|
}
|
|
18
36
|
};
|
|
19
37
|
|
|
20
38
|
class Resolver {
|
|
21
|
-
constructor(
|
|
22
|
-
|
|
23
|
-
|
|
39
|
+
constructor(
|
|
40
|
+
states = emptyStack.push({ properties: new Map(), idx: 0 }),
|
|
41
|
+
reference = null,
|
|
42
|
+
popped = false,
|
|
43
|
+
) {
|
|
44
|
+
this.states = states;
|
|
45
|
+
this.reference = reference;
|
|
46
|
+
this.popped = popped;
|
|
24
47
|
}
|
|
25
48
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
49
|
+
get idx() {
|
|
50
|
+
return this.states.value.idx;
|
|
51
|
+
}
|
|
29
52
|
|
|
30
|
-
|
|
31
|
-
|
|
53
|
+
get properties() {
|
|
54
|
+
return this.states.value.properties;
|
|
55
|
+
}
|
|
32
56
|
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
if (counters.has(name)) throw new Error('attempted to consume property twice');
|
|
57
|
+
advance(tag) {
|
|
58
|
+
const { states } = this;
|
|
36
59
|
|
|
37
|
-
|
|
38
|
-
}
|
|
60
|
+
++states.value.idx;
|
|
39
61
|
|
|
40
|
-
|
|
41
|
-
}
|
|
62
|
+
this.popped = false;
|
|
42
63
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
switch (tag.type) {
|
|
65
|
+
case ReferenceTag: {
|
|
66
|
+
const { name, isArray } = tag.value;
|
|
67
|
+
const { properties } = states.value;
|
|
47
68
|
|
|
48
|
-
|
|
49
|
-
const count = counters.get(name) || 0;
|
|
69
|
+
this.reference = tag;
|
|
50
70
|
|
|
51
|
-
|
|
52
|
-
}
|
|
71
|
+
let state = properties.get(name);
|
|
53
72
|
|
|
54
|
-
|
|
55
|
-
|
|
73
|
+
if (isArray) {
|
|
74
|
+
if (state && !state.isArray) throw new Error();
|
|
56
75
|
|
|
57
|
-
|
|
58
|
-
if (!this.node) throw new Error('Cannot get from a resolver with no node');
|
|
76
|
+
const { count = -1 } = state || {};
|
|
59
77
|
|
|
60
|
-
|
|
61
|
-
|
|
78
|
+
state = { count: count + 1, isArray };
|
|
79
|
+
} else if (state) {
|
|
80
|
+
throw new Error(`attempted to consume property {name: ${name}} twice`);
|
|
81
|
+
} else {
|
|
82
|
+
state = { count: 1, isArray: false };
|
|
83
|
+
}
|
|
62
84
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
85
|
+
properties.set(name, state);
|
|
86
|
+
|
|
87
|
+
if (!isArray || state.count > 0) {
|
|
88
|
+
this.states = states.push({ properties: new Map(), idx: 0 });
|
|
89
|
+
}
|
|
66
90
|
|
|
67
|
-
|
|
68
|
-
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
69
93
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
94
|
+
case EmbeddedNode: {
|
|
95
|
+
this.reference = tag;
|
|
73
96
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
};
|
|
97
|
+
this.states = states.push({ properties: new Map(), idx: 0 });
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
78
100
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
101
|
+
case OpenNodeTag: {
|
|
102
|
+
const { flags } = tag.value;
|
|
103
|
+
const isRootNode = states.size === 1;
|
|
82
104
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
105
|
+
if (!isRootNode && !this.reference && !(flags.trivia || flags.escape)) {
|
|
106
|
+
throw new Error();
|
|
107
|
+
}
|
|
86
108
|
|
|
87
|
-
|
|
88
|
-
|
|
109
|
+
if (this.reference?.type !== EmbeddedNode && (flags.trivia || flags.escape)) {
|
|
110
|
+
this.states = states.push({ properties: new Map(), idx: 0 });
|
|
111
|
+
}
|
|
89
112
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
113
|
+
this.reference = null;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
93
116
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
value: freeze({ flags: freeze({ token, trivia, escape }), language, type, attributes }),
|
|
97
|
-
});
|
|
98
|
-
};
|
|
117
|
+
case ArrayTag: {
|
|
118
|
+
if (!this.reference) throw new Error();
|
|
99
119
|
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
120
|
+
const { name } = this.reference.value;
|
|
121
|
+
const { properties } = states.value;
|
|
122
|
+
const state = properties.get(name);
|
|
123
|
+
|
|
124
|
+
if (!state || !state.isArray || state.count !== 0) throw new Error();
|
|
125
|
+
|
|
126
|
+
properties.set(name, { count: 0, isArray: true });
|
|
127
|
+
|
|
128
|
+
this.reference = null;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
case NullTag:
|
|
133
|
+
case GapTag: {
|
|
134
|
+
this.states = states.pop();
|
|
135
|
+
this.popped = true;
|
|
136
|
+
this.reference = null;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
case CloseNodeTag: {
|
|
141
|
+
this.states = states.pop();
|
|
142
|
+
this.popped = true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return this;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
resolve(reference) {
|
|
150
|
+
let { name, isArray } = reference.value;
|
|
151
|
+
const { states } = this;
|
|
152
|
+
const state = states.value.properties.get(name);
|
|
153
|
+
let path = name;
|
|
154
|
+
|
|
155
|
+
if (isArray) {
|
|
156
|
+
const count = state?.count || 0;
|
|
157
|
+
path += '.' + count;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return path;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
103
163
|
|
|
104
164
|
function* streamFromTree(rootNode) {
|
|
105
|
-
if (!rootNode || rootNode.type ===
|
|
165
|
+
if (!rootNode || rootNode.type === GapTag) {
|
|
106
166
|
return rootNode;
|
|
107
167
|
}
|
|
108
168
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
let stack = emptyStack.push(buildFrame(rootNode));
|
|
169
|
+
let stack = emptyStack.push(rootNode);
|
|
170
|
+
const resolver = new Resolver();
|
|
113
171
|
|
|
114
172
|
stack: while (stack.size) {
|
|
115
|
-
const
|
|
116
|
-
const {
|
|
117
|
-
const { language, type, attributes, flags } = node;
|
|
173
|
+
const node = stack.value;
|
|
174
|
+
const { children } = node;
|
|
118
175
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
176
|
+
while (true) {
|
|
177
|
+
const tag = btree.getAt(resolver.idx, children);
|
|
122
178
|
|
|
123
|
-
|
|
124
|
-
|
|
179
|
+
switch (tag.type) {
|
|
180
|
+
case EmbeddedNode: {
|
|
181
|
+
stack = stack.push(tag.value);
|
|
125
182
|
|
|
126
|
-
|
|
127
|
-
case 'Literal':
|
|
128
|
-
case 'Gap':
|
|
129
|
-
case 'Null': {
|
|
130
|
-
yield terminal;
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
183
|
+
resolver.advance(tag);
|
|
133
184
|
|
|
134
|
-
case 'EmbeddedNode': {
|
|
135
|
-
stack = stack.push(buildFrame(terminal.value));
|
|
136
185
|
continue stack;
|
|
137
186
|
}
|
|
138
187
|
|
|
139
|
-
case
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
188
|
+
case ReferenceTag: {
|
|
189
|
+
const resolvedPath = resolver.resolve(tag);
|
|
190
|
+
const resolved = get(stack.value, resolvedPath);
|
|
191
|
+
const { name, isArray: refIsArray } = tag.value;
|
|
192
|
+
|
|
193
|
+
if (!resolved) throw new Error();
|
|
194
|
+
|
|
195
|
+
yield tag;
|
|
196
|
+
|
|
197
|
+
resolver.advance(tag);
|
|
143
198
|
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
199
|
+
const resolverState = resolver.properties.get(name);
|
|
200
|
+
|
|
201
|
+
const isEmptyArray = resolverState?.count === 0;
|
|
202
|
+
|
|
203
|
+
if (!refIsArray || !isEmptyArray) {
|
|
204
|
+
if (isArray(resolved)) throw new Error();
|
|
205
|
+
stack = stack.push(resolved);
|
|
151
206
|
}
|
|
207
|
+
continue stack;
|
|
152
208
|
}
|
|
153
209
|
|
|
154
|
-
|
|
155
|
-
|
|
210
|
+
case GapTag:
|
|
211
|
+
case NullTag:
|
|
212
|
+
case CloseNodeTag: {
|
|
213
|
+
stack = stack.pop();
|
|
214
|
+
resolver.advance(tag);
|
|
215
|
+
yield tag;
|
|
216
|
+
continue stack;
|
|
156
217
|
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
218
|
|
|
160
|
-
|
|
161
|
-
|
|
219
|
+
default:
|
|
220
|
+
resolver.advance(tag);
|
|
221
|
+
yield tag;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
162
224
|
}
|
|
163
|
-
|
|
164
|
-
stack = stack.pop();
|
|
165
225
|
}
|
|
166
|
-
yield buildNodeCloseTag();
|
|
167
226
|
}
|
|
168
227
|
|
|
169
228
|
const printExpression = (expr) => {
|
|
@@ -213,58 +272,66 @@ const printFlags = (flags) => {
|
|
|
213
272
|
return `${hash}${star}${at}`;
|
|
214
273
|
};
|
|
215
274
|
|
|
216
|
-
const
|
|
217
|
-
switch (
|
|
218
|
-
case
|
|
275
|
+
const printTag = (tag) => {
|
|
276
|
+
switch (tag?.type || NullTag) {
|
|
277
|
+
case NullTag: {
|
|
219
278
|
return 'null';
|
|
220
279
|
}
|
|
221
280
|
|
|
222
|
-
case
|
|
281
|
+
case GapTag: {
|
|
223
282
|
return `<//>`;
|
|
224
283
|
}
|
|
225
284
|
|
|
226
|
-
case
|
|
227
|
-
return
|
|
285
|
+
case ArrayTag: {
|
|
286
|
+
return `[]`;
|
|
228
287
|
}
|
|
229
288
|
|
|
230
|
-
case
|
|
231
|
-
|
|
289
|
+
case LiteralTag: {
|
|
290
|
+
return printString(tag.value);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
case DoctypeTag: {
|
|
294
|
+
let { doctype, attributes } = tag.value;
|
|
232
295
|
|
|
233
296
|
attributes = attributes ? ` ${printAttributes(attributes)}` : '';
|
|
234
297
|
|
|
235
298
|
return `<!${doctype}${attributes}>`;
|
|
236
299
|
}
|
|
237
300
|
|
|
238
|
-
case
|
|
239
|
-
const { name, isArray } =
|
|
301
|
+
case ReferenceTag: {
|
|
302
|
+
const { name, isArray } = tag.value;
|
|
240
303
|
const pathBraces = isArray ? '[]' : '';
|
|
241
304
|
|
|
242
305
|
return `${name}${pathBraces}:`;
|
|
243
306
|
}
|
|
244
307
|
|
|
245
|
-
case
|
|
246
|
-
const { flags, language: tagLanguage, type, attributes } =
|
|
308
|
+
case OpenNodeTag: {
|
|
309
|
+
const { flags, language: tagLanguage, type, attributes } = tag.value;
|
|
247
310
|
const printedAttributes = attributes && printAttributes(attributes);
|
|
248
311
|
const attributesFrag = printedAttributes ? ` ${printedAttributes}` : '';
|
|
249
312
|
|
|
313
|
+
if (type === sym.gap) {
|
|
314
|
+
return '';
|
|
315
|
+
}
|
|
316
|
+
|
|
250
317
|
if (flags.escape && flags.trivia) throw new Error('Node cannot be escape and trivia');
|
|
251
318
|
|
|
252
319
|
return `<${printFlags(flags)}${printTagPath(tagLanguage, type)}${attributesFrag}>`;
|
|
253
320
|
}
|
|
254
321
|
|
|
255
|
-
case
|
|
322
|
+
case CloseNodeTag: {
|
|
256
323
|
return `</>`;
|
|
257
324
|
}
|
|
258
325
|
|
|
259
326
|
default:
|
|
260
|
-
throw new Error();
|
|
327
|
+
throw new Error(`Unexpected tag {type: ${tag?.type}}`);
|
|
261
328
|
}
|
|
262
329
|
};
|
|
263
330
|
|
|
264
331
|
const printPrettyCSTML = (tree, indent = ' ') => {
|
|
265
|
-
const
|
|
332
|
+
const tags = streamFromTree(tree);
|
|
266
333
|
|
|
267
|
-
if (!
|
|
334
|
+
if (!tags) {
|
|
268
335
|
return '<//>';
|
|
269
336
|
}
|
|
270
337
|
|
|
@@ -272,23 +339,23 @@ const printPrettyCSTML = (tree, indent = ' ') => {
|
|
|
272
339
|
let indentLevel = 0;
|
|
273
340
|
let first = true;
|
|
274
341
|
|
|
275
|
-
for (const
|
|
276
|
-
if (!first &&
|
|
342
|
+
for (const tag of tags) {
|
|
343
|
+
if (!first && tag.type !== NullTag) {
|
|
277
344
|
printed += '\n';
|
|
278
345
|
}
|
|
279
346
|
|
|
280
|
-
if (
|
|
347
|
+
if (tag.type === CloseNodeTag) {
|
|
281
348
|
indentLevel--;
|
|
282
349
|
}
|
|
283
350
|
|
|
284
|
-
if (
|
|
351
|
+
if (tag.type !== NullTag) {
|
|
285
352
|
printed += indent.repeat(indentLevel);
|
|
286
353
|
} else {
|
|
287
354
|
printed += ' ';
|
|
288
355
|
}
|
|
289
|
-
printed +=
|
|
356
|
+
printed += printTag(tag);
|
|
290
357
|
|
|
291
|
-
if (
|
|
358
|
+
if (tag.type === OpenNodeTag) {
|
|
292
359
|
indentLevel++;
|
|
293
360
|
}
|
|
294
361
|
|
|
@@ -323,9 +390,10 @@ const printString = (str) => {
|
|
|
323
390
|
};
|
|
324
391
|
|
|
325
392
|
module.exports = {
|
|
393
|
+
Resolver,
|
|
326
394
|
printExpression,
|
|
327
395
|
printAttributes,
|
|
328
|
-
|
|
396
|
+
printTag,
|
|
329
397
|
printPrettyCSTML,
|
|
330
398
|
printSingleString,
|
|
331
399
|
printDoubleString,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bablr/boot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Compile-time tools for bootstrapping BABLR VM",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=12.0.0"
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"@babel/helper-module-imports": "^7.22.15",
|
|
23
23
|
"@babel/template": "^7.22.15",
|
|
24
24
|
"@babel/types": "7.23.0",
|
|
25
|
-
"@bablr/boot-helpers": "0.
|
|
25
|
+
"@bablr/boot-helpers": "0.3.1",
|
|
26
|
+
"@iter-tools/imm-stack": "1.1.0",
|
|
26
27
|
"escape-string-regexp": "4.0.0",
|
|
27
28
|
"iter-tools": "^7.5.3",
|
|
28
29
|
"jest-diff": "29.7.0"
|