@bablr/agast-helpers 0.1.5 → 0.2.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 +33 -20
- package/lib/path.js +1 -1
- package/lib/print.js +17 -21
- package/lib/shorthand.js +0 -4
- package/lib/stream.js +115 -39
- package/lib/tree.js +161 -94
- package/package.json +1 -1
package/lib/builders.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
const { freeze } = Object;
|
|
2
2
|
|
|
3
|
+
export const buildBeginningOfStreamToken = () => {
|
|
4
|
+
return freeze({ type: Symbol.for('@bablr/beginning-of-stream'), value: undefined });
|
|
5
|
+
};
|
|
6
|
+
|
|
3
7
|
export const buildReference = (name, isArray) => {
|
|
4
8
|
return freeze({ type: 'Reference', value: freeze({ name, isArray }) });
|
|
5
9
|
};
|
|
@@ -12,6 +16,10 @@ export const buildGap = () => {
|
|
|
12
16
|
return freeze({ type: 'Gap', value: undefined });
|
|
13
17
|
};
|
|
14
18
|
|
|
19
|
+
export const buildShift = () => {
|
|
20
|
+
return freeze({ type: 'Shift', value: undefined });
|
|
21
|
+
};
|
|
22
|
+
|
|
15
23
|
export const buildEmbedded = (node) => {
|
|
16
24
|
return freeze({ type: 'Embedded', value: node });
|
|
17
25
|
};
|
|
@@ -23,11 +31,15 @@ export const buildDoctypeTag = (attributes) => {
|
|
|
23
31
|
});
|
|
24
32
|
};
|
|
25
33
|
|
|
26
|
-
export const buildNodeOpenTag = (
|
|
34
|
+
export const buildNodeOpenTag = (
|
|
35
|
+
flags = {},
|
|
36
|
+
language = null,
|
|
37
|
+
type = null,
|
|
38
|
+
intrinsicValue = null,
|
|
39
|
+
attributes = {},
|
|
40
|
+
) => {
|
|
27
41
|
let { token, trivia, escape, expression, intrinsic } = flags;
|
|
28
42
|
|
|
29
|
-
if (!type) throw new Error();
|
|
30
|
-
|
|
31
43
|
token = !!token;
|
|
32
44
|
trivia = !!trivia;
|
|
33
45
|
escape = !!escape;
|
|
@@ -46,27 +58,10 @@ export const buildNodeOpenTag = (flags, language, type, intrinsicValue, attribut
|
|
|
46
58
|
});
|
|
47
59
|
};
|
|
48
60
|
|
|
49
|
-
export const buildFragmentOpenTag = (flags = nodeFlags, language) => {
|
|
50
|
-
let { token, trivia, escape } = flags;
|
|
51
|
-
|
|
52
|
-
token = !!token;
|
|
53
|
-
trivia = !!trivia;
|
|
54
|
-
escape = !!escape;
|
|
55
|
-
|
|
56
|
-
return freeze({
|
|
57
|
-
type: 'OpenFragmentTag',
|
|
58
|
-
value: freeze({ flags: freeze({ token, trivia, escape }) }),
|
|
59
|
-
});
|
|
60
|
-
};
|
|
61
|
-
|
|
62
61
|
export const buildNodeCloseTag = (type = null, language = null) => {
|
|
63
62
|
return freeze({ type: 'CloseNodeTag', value: freeze({ language, type }) });
|
|
64
63
|
};
|
|
65
64
|
|
|
66
|
-
export const buildFragmentCloseTag = () => {
|
|
67
|
-
return freeze({ type: 'CloseFragmentTag', value: freeze({}) });
|
|
68
|
-
};
|
|
69
|
-
|
|
70
65
|
const isString = (val) => typeof val === 'string';
|
|
71
66
|
|
|
72
67
|
export const buildLiteral = (value) => {
|
|
@@ -227,3 +222,21 @@ export const buildTriviaNode = (language, type, children = [], properties = {},
|
|
|
227
222
|
properties: freeze(properties),
|
|
228
223
|
attributes: freeze(attributes),
|
|
229
224
|
});
|
|
225
|
+
|
|
226
|
+
export const buildEffect = (value) => {
|
|
227
|
+
return freeze({ type: 'Effect', value });
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export const buildWriteEffect = (text, options = {}) => {
|
|
231
|
+
return buildEffect(freeze({ verb: 'write', value: freeze({ text, options }) }));
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
export const buildAnsiPushEffect = (spans = '') => {
|
|
235
|
+
return buildEffect(
|
|
236
|
+
freeze({ verb: 'ansi-push', value: { spans: spans === '' ? [] : spans.split(' ') } }),
|
|
237
|
+
);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
export const buildAnsiPopEffect = () => {
|
|
241
|
+
return buildEffect(freeze({ verb: 'ansi-pop', value: undefined }));
|
|
242
|
+
};
|
package/lib/path.js
CHANGED
package/lib/print.js
CHANGED
|
@@ -3,6 +3,10 @@ const { isArray } = Array;
|
|
|
3
3
|
const isString = (val) => typeof val === 'string';
|
|
4
4
|
const isNumber = (val) => typeof val === 'number';
|
|
5
5
|
const isObject = (val) => val && typeof val === 'object' && !isArray(val);
|
|
6
|
+
const isFunction = (val) => typeof val === 'function';
|
|
7
|
+
|
|
8
|
+
const when = (condition, value) =>
|
|
9
|
+
condition ? (isFunction(value) ? value() : value) : { *[Symbol.iterator]() {} };
|
|
6
10
|
|
|
7
11
|
export const printExpression = (expr) => {
|
|
8
12
|
if (isString(expr)) {
|
|
@@ -11,6 +15,7 @@ export const printExpression = (expr) => {
|
|
|
11
15
|
return String(expr);
|
|
12
16
|
} else if (isNumber(expr)) {
|
|
13
17
|
if (!isFinite(expr)) {
|
|
18
|
+
if (isNaN(expr)) throw new Error();
|
|
14
19
|
return expr === -Infinity ? '-Infinity' : '+Infinity';
|
|
15
20
|
} else if (isInteger(expr)) {
|
|
16
21
|
return String(expr);
|
|
@@ -41,7 +46,9 @@ export const printLanguage = (language) => {
|
|
|
41
46
|
};
|
|
42
47
|
|
|
43
48
|
export const printTagPath = (language, type) => {
|
|
44
|
-
return language?.length
|
|
49
|
+
return [...when(language?.length, () => [printLanguage(language)]), ...when(type, [type])].join(
|
|
50
|
+
':',
|
|
51
|
+
);
|
|
45
52
|
};
|
|
46
53
|
|
|
47
54
|
const escapeReplacer = (esc) => {
|
|
@@ -78,6 +85,12 @@ export const printGap = (terminal) => {
|
|
|
78
85
|
return `<//>`;
|
|
79
86
|
};
|
|
80
87
|
|
|
88
|
+
export const printShift = (terminal) => {
|
|
89
|
+
if (terminal?.type !== 'Shift') throw new Error();
|
|
90
|
+
|
|
91
|
+
return `^^^`;
|
|
92
|
+
};
|
|
93
|
+
|
|
81
94
|
export const printReference = (terminal) => {
|
|
82
95
|
if (terminal?.type !== 'Reference') throw new Error();
|
|
83
96
|
|
|
@@ -147,26 +160,12 @@ export const printOpenNodeTag = (terminal) => {
|
|
|
147
160
|
)}${intrinsicFrag}${attributesFrag}${selfCloser}>`;
|
|
148
161
|
};
|
|
149
162
|
|
|
150
|
-
export const printOpenFragmentTag = (terminal) => {
|
|
151
|
-
if (terminal?.type !== 'OpenFragmentTag') throw new Error();
|
|
152
|
-
|
|
153
|
-
let { flags } = terminal.value;
|
|
154
|
-
|
|
155
|
-
return `<${printFlags(flags)}>`;
|
|
156
|
-
};
|
|
157
|
-
|
|
158
163
|
export const printCloseNodeTag = (terminal) => {
|
|
159
164
|
if (terminal?.type !== 'CloseNodeTag') throw new Error();
|
|
160
165
|
|
|
161
166
|
return `</>`;
|
|
162
167
|
};
|
|
163
168
|
|
|
164
|
-
export const printCloseFragmentTag = (terminal) => {
|
|
165
|
-
if (terminal?.type !== 'CloseFragmentTag') throw new Error();
|
|
166
|
-
|
|
167
|
-
return `</>`;
|
|
168
|
-
};
|
|
169
|
-
|
|
170
169
|
export const printTerminal = (terminal) => {
|
|
171
170
|
if (!isObject(terminal)) throw new Error();
|
|
172
171
|
|
|
@@ -177,6 +176,9 @@ export const printTerminal = (terminal) => {
|
|
|
177
176
|
case 'Gap':
|
|
178
177
|
return printGap(terminal);
|
|
179
178
|
|
|
179
|
+
case 'Shift':
|
|
180
|
+
return printShift(terminal);
|
|
181
|
+
|
|
180
182
|
case 'Literal':
|
|
181
183
|
return printLiteral(terminal);
|
|
182
184
|
|
|
@@ -189,15 +191,9 @@ export const printTerminal = (terminal) => {
|
|
|
189
191
|
case 'OpenNodeTag':
|
|
190
192
|
return printOpenNodeTag(terminal);
|
|
191
193
|
|
|
192
|
-
case 'OpenFragmentTag':
|
|
193
|
-
return printOpenFragmentTag(terminal);
|
|
194
|
-
|
|
195
194
|
case 'CloseNodeTag':
|
|
196
195
|
return printCloseNodeTag(terminal);
|
|
197
196
|
|
|
198
|
-
case 'CloseFragmentTag':
|
|
199
|
-
return printCloseFragmentTag(terminal);
|
|
200
|
-
|
|
201
197
|
default:
|
|
202
198
|
throw new Error();
|
|
203
199
|
}
|
package/lib/shorthand.js
CHANGED
|
@@ -3,9 +3,7 @@ import {
|
|
|
3
3
|
buildGap,
|
|
4
4
|
buildEmbedded,
|
|
5
5
|
buildNodeOpenTag,
|
|
6
|
-
buildFragmentOpenTag,
|
|
7
6
|
buildNodeCloseTag,
|
|
8
|
-
buildFragmentCloseTag,
|
|
9
7
|
buildLiteral,
|
|
10
8
|
buildNode,
|
|
11
9
|
buildSyntacticNode,
|
|
@@ -47,9 +45,7 @@ export const lit = (str) => buildLiteral(stripArray(str));
|
|
|
47
45
|
export const gap = buildGap;
|
|
48
46
|
export const embedded = buildEmbedded;
|
|
49
47
|
export const nodeOpen = buildNodeOpenTag;
|
|
50
|
-
export const fragOpen = buildFragmentOpenTag;
|
|
51
48
|
export const nodeClose = buildNodeCloseTag;
|
|
52
|
-
export const fragClose = buildFragmentCloseTag;
|
|
53
49
|
export const node = buildNode;
|
|
54
50
|
export const s_node = buildSyntacticNode;
|
|
55
51
|
export const s_i_node = buildSyntacticIntrinsicNode;
|
package/lib/stream.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Coroutine } from '@bablr/coroutine';
|
|
2
2
|
import { printTerminal } from './print.js';
|
|
3
|
+
import { buildWriteEffect } from './builders.js';
|
|
3
4
|
export * from './print.js';
|
|
4
5
|
|
|
5
6
|
export const getStreamIterator = (obj) => {
|
|
@@ -21,12 +22,11 @@ export class SyncGenerator {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
if (step.done) {
|
|
24
|
-
return
|
|
25
|
+
return step;
|
|
25
26
|
} else if (step.value instanceof Promise) {
|
|
26
27
|
throw new Error('sync generators cannot resolve promises');
|
|
27
28
|
} else {
|
|
28
|
-
|
|
29
|
-
return { value, done: false };
|
|
29
|
+
return step;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -60,14 +60,13 @@ export class AsyncGenerator {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
if (step.done) {
|
|
63
|
-
return Promise.resolve(
|
|
63
|
+
return Promise.resolve(step);
|
|
64
64
|
} else if (step.value instanceof Promise) {
|
|
65
65
|
return step.value.then((value) => {
|
|
66
66
|
return this.next(value);
|
|
67
67
|
});
|
|
68
68
|
} else {
|
|
69
|
-
|
|
70
|
-
return Promise.resolve({ value, done: false });
|
|
69
|
+
return Promise.resolve(step);
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
72
|
|
|
@@ -93,14 +92,13 @@ export class StreamGenerator {
|
|
|
93
92
|
const step = this.generator.next(value);
|
|
94
93
|
|
|
95
94
|
if (step.done) {
|
|
96
|
-
return
|
|
95
|
+
return step;
|
|
97
96
|
} else if (step.value instanceof Promise) {
|
|
98
97
|
return step.value.then((value) => {
|
|
99
98
|
return this.next(value);
|
|
100
99
|
});
|
|
101
100
|
} else {
|
|
102
|
-
|
|
103
|
-
return { value, done: false };
|
|
101
|
+
return step;
|
|
104
102
|
}
|
|
105
103
|
}
|
|
106
104
|
|
|
@@ -139,9 +137,9 @@ export const maybeWait = (maybePromise, callback) => {
|
|
|
139
137
|
}
|
|
140
138
|
};
|
|
141
139
|
|
|
142
|
-
function*
|
|
140
|
+
function* __generateCSTMLStrategy(terminals) {
|
|
143
141
|
if (!terminals) {
|
|
144
|
-
yield
|
|
142
|
+
yield buildWriteEffect('<//>');
|
|
145
143
|
return;
|
|
146
144
|
}
|
|
147
145
|
|
|
@@ -160,23 +158,88 @@ function* __generateCSTML(terminals) {
|
|
|
160
158
|
const terminal = co.value;
|
|
161
159
|
|
|
162
160
|
if (terminal.type === 'Reference' && prevTerminal.type === 'Null') {
|
|
163
|
-
yield
|
|
161
|
+
yield buildWriteEffect(' ');
|
|
164
162
|
}
|
|
165
163
|
|
|
166
|
-
|
|
164
|
+
if (terminal.type !== 'Effect') {
|
|
165
|
+
buildWriteEffect(printTerminal(terminal));
|
|
167
166
|
|
|
168
|
-
|
|
167
|
+
prevTerminal = terminal;
|
|
168
|
+
} else {
|
|
169
|
+
yield terminal;
|
|
170
|
+
}
|
|
169
171
|
}
|
|
170
172
|
|
|
171
|
-
yield
|
|
173
|
+
yield buildWriteEffect('\n');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export const generateCSTMLStrategy = (terminals) =>
|
|
177
|
+
new StreamIterable(__generateCSTMLStrategy(terminals));
|
|
178
|
+
|
|
179
|
+
function* __generateStandardOutput(terminals) {
|
|
180
|
+
const co = new Coroutine(getStreamIterator(terminals));
|
|
181
|
+
|
|
182
|
+
for (;;) {
|
|
183
|
+
co.advance();
|
|
184
|
+
|
|
185
|
+
if (co.current instanceof Promise) {
|
|
186
|
+
co.current = yield co.current;
|
|
187
|
+
}
|
|
188
|
+
if (co.done) break;
|
|
189
|
+
|
|
190
|
+
const terminal = co.value;
|
|
191
|
+
|
|
192
|
+
if (terminal.type === 'Effect') {
|
|
193
|
+
const effect = terminal.value;
|
|
194
|
+
if (effect.verb === 'write' && (effect.value.stream == null || effect.value.stream === 1)) {
|
|
195
|
+
yield* effect.value.text;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
172
199
|
}
|
|
173
200
|
|
|
174
|
-
export const
|
|
201
|
+
export const generateStandardOutput = (terminals) =>
|
|
202
|
+
new StreamIterable(__generateStandardOutput(terminals));
|
|
203
|
+
|
|
204
|
+
function* __generateAllOutput(terminals) {
|
|
205
|
+
const co = new Coroutine(getStreamIterator(terminals));
|
|
206
|
+
|
|
207
|
+
let currentStream = null;
|
|
208
|
+
|
|
209
|
+
for (;;) {
|
|
210
|
+
co.advance();
|
|
211
|
+
|
|
212
|
+
if (co.current instanceof Promise) {
|
|
213
|
+
co.current = yield co.current;
|
|
214
|
+
}
|
|
215
|
+
if (co.done) break;
|
|
216
|
+
|
|
217
|
+
const terminal = co.value;
|
|
218
|
+
|
|
219
|
+
if (terminal.type === 'Effect') {
|
|
220
|
+
const effect = terminal.value;
|
|
221
|
+
if (effect.verb === 'write') {
|
|
222
|
+
const prevStream = currentStream;
|
|
223
|
+
currentStream = effect.value.options.stream || 1;
|
|
224
|
+
if (prevStream && prevStream !== currentStream && !effect.value.text.startsWith('\n')) {
|
|
225
|
+
yield* '\n';
|
|
226
|
+
}
|
|
227
|
+
yield* effect.value.text;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export const generateAllOutput = (terminals) => new StreamIterable(__generateAllOutput(terminals));
|
|
175
234
|
|
|
176
235
|
export const printCSTML = (terminals) => {
|
|
177
|
-
return stringFromStream(
|
|
236
|
+
return stringFromStream(generateStandardOutput(generateCSTMLStrategy(terminals)));
|
|
178
237
|
};
|
|
179
238
|
|
|
239
|
+
function* __emptyStreamIterator() {}
|
|
240
|
+
|
|
241
|
+
export const emptyStreamIterator = () => new StreamIterable(__emptyStreamIterator());
|
|
242
|
+
|
|
180
243
|
export const asyncStringFromStream = async (stream) => {
|
|
181
244
|
const co = new Coroutine(getStreamIterator(stream));
|
|
182
245
|
let str = '';
|
|
@@ -215,9 +278,11 @@ export const stringFromStream = (stream) => {
|
|
|
215
278
|
return str;
|
|
216
279
|
};
|
|
217
280
|
|
|
218
|
-
function*
|
|
281
|
+
function* __generatePrettyCSTMLStrategy(terminals, options) {
|
|
282
|
+
const { indent = ' ', emitEffects = false, inline: inlineOption = true } = options;
|
|
283
|
+
|
|
219
284
|
if (!terminals) {
|
|
220
|
-
yield
|
|
285
|
+
yield buildWriteEffect('<//>');
|
|
221
286
|
return;
|
|
222
287
|
}
|
|
223
288
|
|
|
@@ -236,18 +301,29 @@ function* __generatePrettyCSTML(terminals, indent) {
|
|
|
236
301
|
|
|
237
302
|
const terminal = co.value;
|
|
238
303
|
|
|
304
|
+
if (terminal.type === 'Effect') {
|
|
305
|
+
const effect = terminal.value;
|
|
306
|
+
if (emitEffects && effect.verb === 'write') {
|
|
307
|
+
yield buildWriteEffect((first ? '' : '\n') + effect.value.text, effect.value.options);
|
|
308
|
+
|
|
309
|
+
first = false;
|
|
310
|
+
}
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
|
|
239
314
|
const inline =
|
|
240
|
-
|
|
241
|
-
terminal.type === '
|
|
242
|
-
|
|
243
|
-
terminal.
|
|
244
|
-
|
|
315
|
+
inlineOption &&
|
|
316
|
+
(terminal.type === 'Null' ||
|
|
317
|
+
terminal.type === 'Gap' ||
|
|
318
|
+
(terminal.type === 'OpenNodeTag' &&
|
|
319
|
+
terminal.value.intrinsicValue &&
|
|
320
|
+
terminal.value.flags.intrinsic));
|
|
245
321
|
|
|
246
322
|
if (!first && !inline) {
|
|
247
|
-
yield
|
|
323
|
+
yield buildWriteEffect('\n');
|
|
248
324
|
}
|
|
249
325
|
|
|
250
|
-
if (
|
|
326
|
+
if (terminal.type === 'CloseNodeTag') {
|
|
251
327
|
if (indentLevel === 0) {
|
|
252
328
|
throw new Error('imbalanced tag stack');
|
|
253
329
|
}
|
|
@@ -256,15 +332,15 @@ function* __generatePrettyCSTML(terminals, indent) {
|
|
|
256
332
|
}
|
|
257
333
|
|
|
258
334
|
if (!inline) {
|
|
259
|
-
yield
|
|
335
|
+
yield buildWriteEffect(indent.repeat(indentLevel));
|
|
260
336
|
} else {
|
|
261
|
-
yield
|
|
337
|
+
yield buildWriteEffect(' ');
|
|
262
338
|
}
|
|
263
|
-
yield
|
|
339
|
+
yield buildWriteEffect(printTerminal(terminal));
|
|
264
340
|
|
|
265
341
|
if (
|
|
266
|
-
terminal.type === '
|
|
267
|
-
(terminal.
|
|
342
|
+
terminal.type === 'OpenNodeTag' &&
|
|
343
|
+
(!terminal.value.intrinsicValue || !terminal.value.type)
|
|
268
344
|
) {
|
|
269
345
|
indentLevel++;
|
|
270
346
|
}
|
|
@@ -272,15 +348,15 @@ function* __generatePrettyCSTML(terminals, indent) {
|
|
|
272
348
|
first = false;
|
|
273
349
|
}
|
|
274
350
|
|
|
275
|
-
yield
|
|
351
|
+
yield buildWriteEffect('\n');
|
|
276
352
|
}
|
|
277
353
|
|
|
278
|
-
export const
|
|
279
|
-
return new StreamIterable(
|
|
354
|
+
export const generatePrettyCSTMLStrategy = (terminals, options = {}) => {
|
|
355
|
+
return new StreamIterable(__generatePrettyCSTMLStrategy(terminals, options));
|
|
280
356
|
};
|
|
281
357
|
|
|
282
|
-
export const printPrettyCSTML = (terminals) => {
|
|
283
|
-
return stringFromStream(
|
|
358
|
+
export const printPrettyCSTML = (terminals, options = {}) => {
|
|
359
|
+
return stringFromStream(generateStandardOutput(generatePrettyCSTMLStrategy(terminals, options)));
|
|
284
360
|
};
|
|
285
361
|
|
|
286
362
|
export const getCooked = (terminals) => {
|
|
@@ -353,11 +429,11 @@ export function* generateSourceTextFor(terminals) {
|
|
|
353
429
|
export const sourceTextFor = printSource;
|
|
354
430
|
|
|
355
431
|
export const startsDocument = (terminal) => {
|
|
356
|
-
const { type } = terminal;
|
|
357
|
-
if (type === '
|
|
432
|
+
const { type, value } = terminal;
|
|
433
|
+
if ((type === 'OpenNodeTag' && !value.type) || type === 'DoctypeTag') {
|
|
358
434
|
return true;
|
|
359
435
|
} else if (type === 'OpenNodeTag') {
|
|
360
|
-
const { flags } =
|
|
436
|
+
const { flags } = value;
|
|
361
437
|
|
|
362
438
|
return flags.trivia || flags.escape;
|
|
363
439
|
}
|
package/lib/tree.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import emptyStack from '@iter-tools/imm-stack';
|
|
2
|
+
import { Coroutine } from '@bablr/coroutine';
|
|
2
3
|
import {
|
|
3
4
|
buildNodeCloseTag,
|
|
4
5
|
buildNodeOpenTag,
|
|
@@ -6,13 +7,12 @@ import {
|
|
|
6
7
|
buildEmbedded,
|
|
7
8
|
nodeFlags,
|
|
8
9
|
buildDoctypeTag,
|
|
9
|
-
buildFragmentOpenTag,
|
|
10
|
-
buildFragmentCloseTag,
|
|
11
|
-
buildReference,
|
|
12
10
|
} from './builders.js';
|
|
13
11
|
import {
|
|
14
12
|
printPrettyCSTML as printPrettyCSTMLFromStream,
|
|
15
13
|
printCSTML as printCSTMLFromStream,
|
|
14
|
+
getStreamIterator,
|
|
15
|
+
StreamIterable,
|
|
16
16
|
} from './stream.js';
|
|
17
17
|
export * from './builders.js';
|
|
18
18
|
export * from './print.js';
|
|
@@ -38,134 +38,201 @@ const get = (node, path) => {
|
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
41
|
+
function* __treeFromStream(tokens) {
|
|
42
|
+
let nodes = emptyStack;
|
|
43
|
+
let rootNode;
|
|
44
|
+
let held = null;
|
|
45
|
+
const co = new Coroutine(getStreamIterator(tokens));
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
for (;;) {
|
|
48
|
+
co.advance();
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
flags: nodeFlags,
|
|
54
|
-
type: Symbol.for('@bablr/fragment'),
|
|
55
|
-
children: [],
|
|
56
|
-
properties: {},
|
|
57
|
-
attributes: freeze(attributes),
|
|
58
|
-
}),
|
|
59
|
-
);
|
|
50
|
+
if (co.current instanceof Promise) {
|
|
51
|
+
co.current = yield co.current;
|
|
60
52
|
}
|
|
61
53
|
|
|
62
|
-
|
|
63
|
-
freeze(nodes.value.properties);
|
|
64
|
-
freeze(nodes.value.children);
|
|
54
|
+
if (co.done) break;
|
|
65
55
|
|
|
66
|
-
|
|
67
|
-
}
|
|
56
|
+
const token = co.value;
|
|
68
57
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
case 'Reference': {
|
|
72
|
-
nodes.value.children.push(token);
|
|
73
|
-
return nodes;
|
|
58
|
+
if (token.type === 'Effect') {
|
|
59
|
+
continue;
|
|
74
60
|
}
|
|
75
61
|
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const node = nodes.value;
|
|
84
|
-
const newNode = freeze({
|
|
85
|
-
flags,
|
|
86
|
-
language,
|
|
87
|
-
type,
|
|
88
|
-
children: intrinsicValue && flags.intrinsic ? buildStringTerminals(intrinsicValue) : [],
|
|
62
|
+
if (token.type === 'DoctypeTag') {
|
|
63
|
+
const { attributes } = token.value;
|
|
64
|
+
|
|
65
|
+
rootNode = freeze({
|
|
66
|
+
flags: nodeFlags,
|
|
67
|
+
type: null,
|
|
68
|
+
children: [],
|
|
89
69
|
properties: {},
|
|
90
70
|
attributes: freeze(attributes),
|
|
91
71
|
});
|
|
72
|
+
nodes = nodes.push(rootNode);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
92
75
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
76
|
+
if (!nodes.size) {
|
|
77
|
+
throw new Error('imbalanced tag stack');
|
|
78
|
+
}
|
|
97
79
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
80
|
+
switch (token.type) {
|
|
81
|
+
case 'Null': {
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
case 'Literal':
|
|
86
|
+
case 'Reference': {
|
|
87
|
+
nodes.value.children.push(token);
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
101
90
|
|
|
102
|
-
|
|
91
|
+
case 'Gap': {
|
|
92
|
+
if (held) {
|
|
93
|
+
const { children, properties } = nodes.value;
|
|
94
|
+
const ref = arrayLast(children);
|
|
103
95
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
if (ref.type !== 'Reference') throw new Error();
|
|
97
|
+
|
|
98
|
+
if (ref.value.isArray) {
|
|
99
|
+
if (!properties[ref.value.name]) {
|
|
100
|
+
properties[ref.value.name] = [];
|
|
101
|
+
}
|
|
102
|
+
properties[ref.value.name].push(held);
|
|
103
|
+
} else {
|
|
104
|
+
properties[ref.value.name] = held;
|
|
107
105
|
}
|
|
108
|
-
const array = node.properties[name];
|
|
109
106
|
|
|
110
|
-
|
|
111
|
-
} else {
|
|
112
|
-
node.properties[name] = newNode;
|
|
107
|
+
held = null;
|
|
113
108
|
}
|
|
109
|
+
break;
|
|
114
110
|
}
|
|
115
111
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
case 'Shift': {
|
|
113
|
+
const { children, properties } = nodes.value;
|
|
114
|
+
|
|
115
|
+
const ref = arrayLast(children);
|
|
116
|
+
let node = properties[ref.value.name];
|
|
117
|
+
|
|
118
|
+
if (ref.value.isArray) {
|
|
119
|
+
node = arrayLast(node);
|
|
120
|
+
properties[ref.value.name].pop();
|
|
121
|
+
} else {
|
|
122
|
+
properties[ref.value.name] = null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
held = node;
|
|
126
|
+
break;
|
|
120
127
|
}
|
|
121
128
|
|
|
122
|
-
|
|
123
|
-
|
|
129
|
+
case 'OpenNodeTag': {
|
|
130
|
+
const buildStringTerminals = (str) => {
|
|
131
|
+
// do better
|
|
132
|
+
return [{ type: 'Literal', value: str }];
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const { flags, language, type, intrinsicValue, attributes } = token.value;
|
|
136
|
+
const node = nodes.value;
|
|
124
137
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
138
|
+
if (!type) {
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
128
141
|
|
|
129
|
-
|
|
130
|
-
|
|
142
|
+
const newNode = freeze({
|
|
143
|
+
flags,
|
|
144
|
+
language,
|
|
145
|
+
type,
|
|
146
|
+
children: intrinsicValue && flags.intrinsic ? buildStringTerminals(intrinsicValue) : [],
|
|
147
|
+
properties: {},
|
|
148
|
+
attributes: freeze(attributes),
|
|
149
|
+
});
|
|
131
150
|
|
|
132
|
-
|
|
151
|
+
if (node && !(flags.escape || flags.trivia)) {
|
|
152
|
+
if (!node.children.length) {
|
|
153
|
+
throw new Error('Nodes must follow references');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const { name, isArray } = arrayLast(node.children).value;
|
|
157
|
+
|
|
158
|
+
if (isArray) {
|
|
159
|
+
if (!hasOwn(node.properties, name)) {
|
|
160
|
+
node.properties[name] = [];
|
|
161
|
+
}
|
|
162
|
+
const array = node.properties[name];
|
|
163
|
+
|
|
164
|
+
array.push(newNode);
|
|
165
|
+
} else {
|
|
166
|
+
node.properties[name] = newNode;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (intrinsicValue && flags.intrinsic) {
|
|
171
|
+
freeze(newNode.children);
|
|
172
|
+
freeze(newNode.properties);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
nodes = nodes.push(newNode);
|
|
177
|
+
break;
|
|
133
178
|
}
|
|
134
179
|
|
|
135
|
-
|
|
136
|
-
|
|
180
|
+
case 'CloseNodeTag': {
|
|
181
|
+
const completedNode = nodes.value;
|
|
182
|
+
const { flags } = completedNode;
|
|
137
183
|
|
|
138
|
-
|
|
139
|
-
|
|
184
|
+
if (flags.escape || flags.trivia) {
|
|
185
|
+
const parentChildren = nodes.prev.value.children;
|
|
140
186
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
};
|
|
187
|
+
parentChildren.push(buildEmbedded(completedNode));
|
|
188
|
+
}
|
|
146
189
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
let rootNode;
|
|
190
|
+
freeze(completedNode.properties);
|
|
191
|
+
freeze(completedNode.children);
|
|
150
192
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
193
|
+
if (!completedNode.type && nodes.size !== 1) {
|
|
194
|
+
throw new Error('imbalanced tag stack');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
nodes = nodes.pop();
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
default: {
|
|
202
|
+
throw new Error();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
154
205
|
}
|
|
155
206
|
|
|
156
207
|
return rootNode;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export const treeFromStream = (terminals) => new StreamIterable(__treeFromStream(terminals));
|
|
211
|
+
|
|
212
|
+
export const treeFromStreamSync = (tokens) => {
|
|
213
|
+
return evaluateReturnSync(treeFromStream(tokens));
|
|
157
214
|
};
|
|
158
215
|
|
|
159
216
|
export const treeFromStreamAsync = async (tokens) => {
|
|
160
|
-
|
|
161
|
-
|
|
217
|
+
return evaluateReturnAsync(treeFromStream(tokens));
|
|
218
|
+
};
|
|
162
219
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
220
|
+
export const evaluateReturnSync = (generator) => {
|
|
221
|
+
const co = new Coroutine(generator[Symbol.iterator]());
|
|
222
|
+
while (!co.done) co.advance();
|
|
223
|
+
return co.value;
|
|
224
|
+
};
|
|
167
225
|
|
|
168
|
-
|
|
226
|
+
export const evaluateReturnAsync = async (generator) => {
|
|
227
|
+
const co = new Coroutine(getStreamIterator(generator));
|
|
228
|
+
while (!co.done) {
|
|
229
|
+
co.advance();
|
|
230
|
+
|
|
231
|
+
if (co.current instanceof Promise) {
|
|
232
|
+
co.current = await co.current;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return co.value;
|
|
169
236
|
};
|
|
170
237
|
|
|
171
238
|
export function* streamFromTree(rootNode) {
|
|
@@ -174,7 +241,7 @@ export function* streamFromTree(rootNode) {
|
|
|
174
241
|
}
|
|
175
242
|
|
|
176
243
|
yield buildDoctypeTag(rootNode.attributes);
|
|
177
|
-
yield
|
|
244
|
+
yield buildNodeOpenTag();
|
|
178
245
|
|
|
179
246
|
let stack = emptyStack.push(buildFrame(rootNode));
|
|
180
247
|
|
|
@@ -236,7 +303,7 @@ export function* streamFromTree(rootNode) {
|
|
|
236
303
|
|
|
237
304
|
stack = stack.pop();
|
|
238
305
|
}
|
|
239
|
-
yield
|
|
306
|
+
yield buildNodeCloseTag();
|
|
240
307
|
}
|
|
241
308
|
|
|
242
309
|
export const getCooked = (cookable) => {
|