@bablr/agast-helpers 0.10.10 → 0.11.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/README.md +1 -1
- package/lib/b-list-keyed.js +1 -0
- package/lib/b-list.js +1 -0
- package/lib/b-map.js +1 -0
- package/lib/b-set.js +1 -0
- package/lib/builders.js +917 -115
- package/lib/debug.js +39 -0
- package/lib/debug.register.js +5 -0
- package/lib/iterable.js +170 -0
- package/lib/object.js +52 -3
- package/lib/parse.js +30 -0
- package/lib/path.js +580 -634
- package/lib/print.js +45 -22
- package/lib/shorthand.js +4 -30
- package/lib/spans.js +0 -163
- package/lib/stream.js +345 -196
- package/lib/symbols.js +1 -0
- package/lib/tags.js +256 -77
- package/lib/template.js +19 -16
- package/lib/tree.js +49 -44
- package/package.json +14 -8
- package/lib/btree.js +0 -1
package/lib/builders.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { isObject, isSymbol } from './object.js';
|
|
1
|
+
import * as BList from './b-list.js';
|
|
2
|
+
import { hasOwn, isObject, isArray, isSymbol, isString, isFrozen, freezeRecord } from './object.js';
|
|
3
|
+
import { buildParser, match } from './parse.js';
|
|
4
|
+
import { printExpression, printTag } from './print.js';
|
|
5
|
+
|
|
3
6
|
import {
|
|
4
7
|
DoctypeTag,
|
|
5
8
|
OpenNodeTag,
|
|
@@ -16,83 +19,78 @@ import {
|
|
|
16
19
|
NullNode,
|
|
17
20
|
GapNode,
|
|
18
21
|
TreeNode,
|
|
22
|
+
StreamTag,
|
|
19
23
|
} from './symbols.js';
|
|
20
24
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export const deepFreeze = (object) => {
|
|
25
|
-
let list = emptyStack.push(object);
|
|
26
|
-
while (list.size) {
|
|
27
|
-
let item = list.value;
|
|
28
|
-
list = list.pop();
|
|
25
|
+
export const symbolName = (name) => {
|
|
26
|
+
return isString(name) ? Symbol.for(name) : name;
|
|
27
|
+
};
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
}
|
|
29
|
+
export const buildSpan = (name, guard = null, props = '{}') => {
|
|
30
|
+
if (!name) throw new Error();
|
|
31
|
+
if (!isString(props)) throw new Error();
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
}
|
|
33
|
+
return freezeRecord({ name, guard, props });
|
|
38
34
|
};
|
|
39
35
|
|
|
40
|
-
export const
|
|
41
|
-
|
|
42
|
-
deepFreeze(props);
|
|
43
|
-
return freeze({ name, guard, props });
|
|
36
|
+
export const buildSpanEntry = (name, guard = null, props = '{}') => {
|
|
37
|
+
return freezeRecord([name, buildSpan(name, guard, props)]);
|
|
44
38
|
};
|
|
45
39
|
|
|
46
40
|
export const buildProperty = (tags, shift) => {
|
|
47
41
|
if (shift && !shift.index) throw new Error();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (tags[0] && ![ReferenceTag, ShiftTag].includes(tags[0].type)) throw new Error();
|
|
51
|
-
if (tags.length > 1 && !isArray(tags[1])) throw new Error();
|
|
42
|
+
let { 0: open, 1: bindings, 2: node } = tags[1];
|
|
52
43
|
|
|
53
|
-
|
|
44
|
+
if (!BList.getType(tags) === Symbol.for('List')) throw new Error();
|
|
45
|
+
if (
|
|
46
|
+
!(
|
|
47
|
+
(isArray(open) && open[0] === 0) ||
|
|
48
|
+
(isString(open) && [ReferenceTag, ShiftTag].includes(parseTagType(open)))
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
throw new Error();
|
|
52
|
+
if (tags[1].length > 1 && !isArray(bindings)) throw new Error();
|
|
54
53
|
|
|
55
54
|
// if (property.node && !(tags.length === 3)) throw new Error();
|
|
56
55
|
// if (tags[0].type === ShiftTag && !property.shift) throw new Error();
|
|
57
56
|
|
|
58
|
-
if ((
|
|
59
|
-
if (
|
|
57
|
+
if (!isArray(open) && (parseTagType(open) === ShiftTag) !== !!shift) throw new Error();
|
|
58
|
+
if (node && ![NullNode, GapNode, TreeNode].includes(node.type)) throw new Error();
|
|
60
59
|
|
|
61
|
-
return
|
|
60
|
+
return freezeRecord({
|
|
62
61
|
tags,
|
|
63
|
-
reference:
|
|
64
|
-
bindings:
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
reference: shift ? null : (!isArray(open) && parseTag(open).value) || buildReference(),
|
|
63
|
+
bindings: freezeRecord(
|
|
64
|
+
bindings ? [...BList.traverse(bindings)].map((tag) => parseTag(tag).value) : [],
|
|
65
|
+
),
|
|
66
|
+
node: node || null,
|
|
67
|
+
shift: shift ? shift : null,
|
|
67
68
|
});
|
|
68
69
|
};
|
|
69
70
|
|
|
70
71
|
export const buildPathFrame = (property, parentIndex = null, isGap = false) => {
|
|
71
|
-
return
|
|
72
|
+
return freezeRecord({ property, parentIndex, isGap });
|
|
72
73
|
};
|
|
73
74
|
|
|
74
75
|
export const buildPropertyTag = (tags, shift) => {
|
|
75
|
-
|
|
76
|
+
if (BList.getType(tags) !== Symbol.for('List')) throw new Error();
|
|
77
|
+
return buildTag(Property, buildProperty(tags, shift));
|
|
76
78
|
};
|
|
77
79
|
|
|
78
80
|
export const buildDocument = (doctypeTag, tree) => {
|
|
79
|
-
return
|
|
81
|
+
return buildTag(Document, { doctypeTag, tree });
|
|
80
82
|
};
|
|
81
83
|
|
|
82
84
|
export const buildBeginningOfStreamToken = () => {
|
|
83
|
-
return
|
|
85
|
+
return buildTag(Symbol.for('@bablr/beginning-of-stream'));
|
|
84
86
|
};
|
|
85
87
|
|
|
86
88
|
export const buildReferenceTag = (type = null, name = null, flags = referenceFlags) => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return freeze({
|
|
90
|
-
type: ReferenceTag,
|
|
91
|
-
value: buildReference(type, name, flags),
|
|
92
|
-
});
|
|
89
|
+
return buildTag(ReferenceTag, buildReference(type, name, flags));
|
|
93
90
|
};
|
|
94
91
|
|
|
95
92
|
export const buildReference = (type = null, name = null, flags = referenceFlags) => {
|
|
93
|
+
if (!Object.isFrozen(flags)) throw new Error();
|
|
96
94
|
if (type != null && !['_', '.', '#', '@'].includes(type)) throw new Error();
|
|
97
95
|
if (type && ['_', '#', '@'].includes(type) && (flags.intrinsic || flags.hasGap))
|
|
98
96
|
throw new Error();
|
|
@@ -106,74 +104,78 @@ export const buildReference = (type = null, name = null, flags = referenceFlags)
|
|
|
106
104
|
|
|
107
105
|
let type_ = type == null && name == null ? '.' : type;
|
|
108
106
|
|
|
109
|
-
return
|
|
107
|
+
return freezeRecord({
|
|
108
|
+
type: type_,
|
|
109
|
+
name,
|
|
110
|
+
flags: freezeRecord({ array, expression, intrinsic, hasGap }),
|
|
111
|
+
});
|
|
110
112
|
};
|
|
111
113
|
|
|
112
114
|
export const referenceFromMatcher = (matcher) => {
|
|
115
|
+
if (!matcher) return buildReference();
|
|
113
116
|
let { type, name, flags } = matcher;
|
|
117
|
+
|
|
114
118
|
return buildReference(type, name, flags);
|
|
115
119
|
};
|
|
116
120
|
|
|
121
|
+
export const buildBounds = (leading, trailing) => {
|
|
122
|
+
return freezeRecord({ leading, trailing });
|
|
123
|
+
};
|
|
124
|
+
|
|
117
125
|
export const buildShift = (index, height) => {
|
|
118
126
|
if (index == null || height == null) throw new Error();
|
|
119
|
-
|
|
127
|
+
|
|
128
|
+
return freezeRecord({ index, height });
|
|
120
129
|
};
|
|
121
130
|
|
|
122
131
|
export const buildNullTag = () => {
|
|
123
|
-
return
|
|
132
|
+
return buildTag(NullTag);
|
|
124
133
|
};
|
|
125
134
|
|
|
126
|
-
export const buildBinding = (
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
freeze(isString(segment) ? { type: null, name: segment } : segment),
|
|
133
|
-
),
|
|
134
|
-
});
|
|
135
|
+
export const buildBinding = (type, name) => {
|
|
136
|
+
if (type && (!isSymbol(type) || !['..', '_'].includes(type.desription))) throw new Error();
|
|
137
|
+
if (name && !isSymbol(name)) throw new Error();
|
|
138
|
+
if (type && name) throw new Error();
|
|
139
|
+
|
|
140
|
+
return freezeRecord({ type, name });
|
|
135
141
|
};
|
|
136
142
|
|
|
137
|
-
export const buildBindingTag = (
|
|
138
|
-
return
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
export const buildBindingTag = (type, name) => {
|
|
144
|
+
return buildTag(BindingTag, buildBinding(type, name));
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const buildStreamTag = (processPath, stream) => {
|
|
148
|
+
if (stream && !(stream >= 1 && stream <= 4)) throw new Error();
|
|
149
|
+
|
|
150
|
+
return buildTag(StreamTag, { processPath, stream });
|
|
142
151
|
};
|
|
143
152
|
|
|
144
153
|
export const buildChild = (type, value) => {
|
|
145
154
|
if (!isSymbol(type)) throw new Error();
|
|
146
|
-
|
|
155
|
+
let child = buildTag(type, value);
|
|
156
|
+
if (!parseTag(child)) throw new Error();
|
|
157
|
+
return child;
|
|
147
158
|
};
|
|
148
159
|
|
|
149
160
|
export const buildGapTag = () => {
|
|
150
|
-
return
|
|
161
|
+
return buildTag(GapTag);
|
|
151
162
|
};
|
|
152
163
|
|
|
153
164
|
export const buildShiftTag = () => {
|
|
154
|
-
|
|
155
|
-
{},
|
|
156
|
-
{
|
|
157
|
-
index: {
|
|
158
|
-
get() {
|
|
159
|
-
throw new Error('moved');
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
height: {
|
|
163
|
-
get() {
|
|
164
|
-
throw new Error('moved');
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
);
|
|
169
|
-
return freeze({ type: ShiftTag, value });
|
|
165
|
+
return buildTag(ShiftTag);
|
|
170
166
|
};
|
|
171
167
|
|
|
172
|
-
export const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
168
|
+
export const buildDoctype = (version = 0, attributes = '{}') => {
|
|
169
|
+
if (!isString(attributes)) throw new Error();
|
|
170
|
+
return freezeRecord({ doctype: 'cstml', version, attributes });
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export const buildTag = (type, value) => {
|
|
174
|
+
return freezeRecord({ type, value });
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export const buildDoctypeTag = (version = 0, attributes = '{}') => {
|
|
178
|
+
return buildTag(DoctypeTag, buildDoctype(version, attributes));
|
|
177
179
|
};
|
|
178
180
|
|
|
179
181
|
export const buildOpenFragmentTag = (flags = nodeFlags, selfClosing = false) => {
|
|
@@ -188,55 +190,67 @@ export const buildOpenNodeTag = (
|
|
|
188
190
|
flags,
|
|
189
191
|
name,
|
|
190
192
|
literalValue = null,
|
|
191
|
-
attributes = {},
|
|
193
|
+
attributes = '{}',
|
|
192
194
|
selfClosing = !!literalValue,
|
|
193
195
|
) => {
|
|
194
|
-
if (!name && !flags.token) throw new Error();
|
|
195
196
|
return buildFullOpenNodeTag(flags, null, name, literalValue, attributes, selfClosing);
|
|
196
197
|
};
|
|
197
198
|
|
|
198
|
-
export const
|
|
199
|
+
export const buildOpenNode = (
|
|
199
200
|
flags = nodeFlags,
|
|
200
201
|
type = Symbol.for('__'),
|
|
201
202
|
name = null,
|
|
202
203
|
literalValue = null,
|
|
203
|
-
attributes = {},
|
|
204
|
+
attributes = '{}',
|
|
204
205
|
selfClosing = !!literalValue,
|
|
205
206
|
) => {
|
|
206
|
-
if (!
|
|
207
|
+
if (!isString(attributes)) throw new Error();
|
|
207
208
|
if (literalValue && !isString(literalValue)) throw new Error();
|
|
208
|
-
if (!type && !name && !flags.token &&
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
literalValue,
|
|
219
|
-
attributes,
|
|
220
|
-
selfClosing,
|
|
221
|
-
}),
|
|
209
|
+
if (!type && !name && !flags.token && literalValue != null) throw new Error();
|
|
210
|
+
if (literalValue != null && !selfClosing) throw new Error();
|
|
211
|
+
|
|
212
|
+
return freezeRecord({
|
|
213
|
+
flags,
|
|
214
|
+
name: symbolName(name),
|
|
215
|
+
type: symbolName(type),
|
|
216
|
+
literalValue,
|
|
217
|
+
attributes,
|
|
218
|
+
selfClosing,
|
|
222
219
|
});
|
|
223
220
|
};
|
|
224
221
|
|
|
225
|
-
export const
|
|
226
|
-
|
|
222
|
+
export const buildFullOpenNodeTag = (
|
|
223
|
+
flags = nodeFlags,
|
|
224
|
+
type = Symbol.for('__'),
|
|
225
|
+
name = null,
|
|
226
|
+
literalValue = null,
|
|
227
|
+
attributes = '{}',
|
|
228
|
+
selfClosing = !!literalValue,
|
|
229
|
+
) => {
|
|
230
|
+
if (!hasOwn(flags, 'token')) throw new Error();
|
|
231
|
+
return buildTag(
|
|
232
|
+
OpenNodeTag,
|
|
233
|
+
buildOpenNode(flags, type, name, literalValue, attributes, selfClosing),
|
|
234
|
+
);
|
|
227
235
|
};
|
|
228
236
|
|
|
229
|
-
const
|
|
237
|
+
export const buildCloseNodeTag = () => {
|
|
238
|
+
return buildTag(CloseNodeTag);
|
|
239
|
+
};
|
|
230
240
|
|
|
231
241
|
export const buildLiteralTag = (value) => {
|
|
232
242
|
if (!isString(value)) throw new Error('invalid literal');
|
|
233
|
-
return
|
|
243
|
+
return buildTag(LiteralTag, value);
|
|
234
244
|
};
|
|
235
245
|
|
|
236
246
|
export const buildAttributeDefinition = (key, value) => {
|
|
237
247
|
if (!key?.length) throw new Error();
|
|
238
|
-
|
|
239
|
-
return
|
|
248
|
+
|
|
249
|
+
return freezeRecord({ path: key, value });
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export const buildAttributeDefinitionTag = (key, value) => {
|
|
253
|
+
return buildTag(AttributeDefinition, buildAttributeDefinition(key, value));
|
|
240
254
|
};
|
|
241
255
|
|
|
242
256
|
const flagsWithGap = new WeakMap();
|
|
@@ -246,7 +260,7 @@ export const getFlagsWithGap = (flags, hasGap = true) => {
|
|
|
246
260
|
|
|
247
261
|
let gapFlags = flagsWithGap.get(flags);
|
|
248
262
|
if (!gapFlags) {
|
|
249
|
-
gapFlags =
|
|
263
|
+
gapFlags = freezeRecord({
|
|
250
264
|
token: flags.token,
|
|
251
265
|
hasGap,
|
|
252
266
|
});
|
|
@@ -255,40 +269,828 @@ export const getFlagsWithGap = (flags, hasGap = true) => {
|
|
|
255
269
|
return gapFlags;
|
|
256
270
|
};
|
|
257
271
|
|
|
258
|
-
export const nodeFlags =
|
|
272
|
+
export const nodeFlags = freezeRecord({
|
|
259
273
|
token: false,
|
|
260
274
|
hasGap: false,
|
|
261
275
|
});
|
|
262
276
|
|
|
263
|
-
export const tokenFlags =
|
|
277
|
+
export const tokenFlags = freezeRecord({
|
|
264
278
|
token: true,
|
|
265
279
|
hasGap: false,
|
|
266
280
|
});
|
|
267
281
|
|
|
268
|
-
export const referenceFlags =
|
|
282
|
+
export const referenceFlags = freezeRecord({
|
|
269
283
|
array: false,
|
|
270
284
|
expression: false,
|
|
271
285
|
intrinsic: false,
|
|
272
286
|
hasGap: false,
|
|
273
287
|
});
|
|
274
288
|
|
|
275
|
-
export const intrinsicReferenceFlags =
|
|
289
|
+
export const intrinsicReferenceFlags = freezeRecord({
|
|
276
290
|
array: false,
|
|
277
291
|
expression: false,
|
|
278
292
|
intrinsic: true,
|
|
279
293
|
hasGap: false,
|
|
280
294
|
});
|
|
281
295
|
|
|
282
|
-
export const gapReferenceFlags =
|
|
296
|
+
export const gapReferenceFlags = freezeRecord({
|
|
283
297
|
array: false,
|
|
284
298
|
expression: false,
|
|
285
299
|
intrinsic: false,
|
|
286
300
|
hasGap: true,
|
|
287
301
|
});
|
|
288
302
|
|
|
289
|
-
export const expressionReferenceFlags =
|
|
303
|
+
export const expressionReferenceFlags = freezeRecord({
|
|
290
304
|
array: false,
|
|
291
305
|
expression: true,
|
|
292
306
|
intrinsic: false,
|
|
293
307
|
hasGap: false,
|
|
294
308
|
});
|
|
309
|
+
|
|
310
|
+
export const canStartIdentifier = (chr) => {
|
|
311
|
+
let code = chr.charCodeAt(0);
|
|
312
|
+
return (
|
|
313
|
+
(code >= 97 && code <= 122) ||
|
|
314
|
+
(code >= 65 && code <= 90) ||
|
|
315
|
+
code === 96 ||
|
|
316
|
+
(code >= 0x80 && code <= 0x10ffff)
|
|
317
|
+
);
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
export const canContinueIdentifier = (chr) => {
|
|
321
|
+
if (canStartIdentifier(chr)) return true;
|
|
322
|
+
let code = chr.charCodeAt(0);
|
|
323
|
+
|
|
324
|
+
return code === 45 || code === 95 || (code >= 48 && code <= 57);
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
export const parseTagType = (input) => {
|
|
328
|
+
let p = buildTagParser(input);
|
|
329
|
+
let { str, idx } = p;
|
|
330
|
+
if (str == null) return null;
|
|
331
|
+
if (isObject(str)) return str.type;
|
|
332
|
+
if (!isString(str)) throw new Error();
|
|
333
|
+
if (!str.length) throw new Error();
|
|
334
|
+
|
|
335
|
+
switch (str[idx]) {
|
|
336
|
+
case 'n':
|
|
337
|
+
return str[idx + 1] === 'u' && str[idx + 2] === 'l' && str[idx + 3] === 'l'
|
|
338
|
+
? NullTag
|
|
339
|
+
: ReferenceTag;
|
|
340
|
+
case '<':
|
|
341
|
+
if (str[idx + 1] === '-' && str[idx + 2] === '-' && str[idx + 3] === '-') {
|
|
342
|
+
throw new Error('reserved syntax');
|
|
343
|
+
}
|
|
344
|
+
return str[idx + 1] === '!'
|
|
345
|
+
? DoctypeTag
|
|
346
|
+
: str[idx + 1] === '/'
|
|
347
|
+
? str[idx + 2] === '/'
|
|
348
|
+
? GapTag
|
|
349
|
+
: CloseNodeTag
|
|
350
|
+
: str[idx + 1] === '-'
|
|
351
|
+
? StreamTag
|
|
352
|
+
: OpenNodeTag;
|
|
353
|
+
case '^':
|
|
354
|
+
return ShiftTag;
|
|
355
|
+
case ':':
|
|
356
|
+
return BindingTag;
|
|
357
|
+
case '.':
|
|
358
|
+
case '#':
|
|
359
|
+
case '@':
|
|
360
|
+
case '_':
|
|
361
|
+
return ReferenceTag;
|
|
362
|
+
case '"':
|
|
363
|
+
case "'":
|
|
364
|
+
return LiteralTag;
|
|
365
|
+
case '{':
|
|
366
|
+
return AttributeDefinition;
|
|
367
|
+
default:
|
|
368
|
+
if (canStartIdentifier(str[idx])) {
|
|
369
|
+
return ReferenceTag;
|
|
370
|
+
} else {
|
|
371
|
+
throw new Error();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
export const parseObject = (input) => {
|
|
377
|
+
let p = buildParser(input);
|
|
378
|
+
let { str } = p;
|
|
379
|
+
if (!str.length) throw new Error();
|
|
380
|
+
let obj = {};
|
|
381
|
+
let chr = str[p.idx];
|
|
382
|
+
|
|
383
|
+
if (chr !== '{') throw new Error();
|
|
384
|
+
chr = str[++p.idx];
|
|
385
|
+
|
|
386
|
+
while (chr === ' ') {
|
|
387
|
+
chr = str[++p.idx];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
let sep = true;
|
|
391
|
+
while (sep && chr !== '}') {
|
|
392
|
+
let key = `'"`.includes(chr) ? parseString(p) : parseIdentifier(p);
|
|
393
|
+
chr = str[p.idx];
|
|
394
|
+
|
|
395
|
+
if (chr !== ':') throw new Error();
|
|
396
|
+
chr = str[++p.idx];
|
|
397
|
+
|
|
398
|
+
while (chr === ' ') chr = str[++p.idx];
|
|
399
|
+
|
|
400
|
+
let value = parseExpression(p);
|
|
401
|
+
chr = str[p.idx];
|
|
402
|
+
|
|
403
|
+
obj[key] = value;
|
|
404
|
+
|
|
405
|
+
sep = chr === ',' ? chr : null;
|
|
406
|
+
if (sep) {
|
|
407
|
+
chr = str[++p.idx];
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
while (chr === ' ') chr = str[++p.idx];
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (chr !== '}') throw new Error();
|
|
414
|
+
chr = str[++p.idx];
|
|
415
|
+
|
|
416
|
+
return freezeRecord(obj);
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
export const parseArray = (input) => {
|
|
420
|
+
let p = buildParser(input);
|
|
421
|
+
let { str } = p;
|
|
422
|
+
let arr = [];
|
|
423
|
+
let chr = str[p.idx];
|
|
424
|
+
|
|
425
|
+
if (chr !== '[') throw new Error();
|
|
426
|
+
chr = str[++p.idx];
|
|
427
|
+
|
|
428
|
+
while (chr === ' ') {
|
|
429
|
+
chr = str[++p.idx];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
let value = parseExpression(p);
|
|
433
|
+
chr = str[p.idx];
|
|
434
|
+
|
|
435
|
+
arr.push(value);
|
|
436
|
+
|
|
437
|
+
while (chr === ' ') {
|
|
438
|
+
chr = str[++p.idx];
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (chr !== ']') throw new Error();
|
|
442
|
+
chr = str[++p.idx];
|
|
443
|
+
|
|
444
|
+
return freezeRecord(arr);
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
export const parseString = (input) => {
|
|
448
|
+
let p = buildParser(input);
|
|
449
|
+
let { str } = p;
|
|
450
|
+
let chr = str[p.idx];
|
|
451
|
+
let q = chr;
|
|
452
|
+
let result = [];
|
|
453
|
+
|
|
454
|
+
if (!`'"`.includes(q)) throw new Error();
|
|
455
|
+
chr = str[++p.idx];
|
|
456
|
+
|
|
457
|
+
while (chr && chr !== q) {
|
|
458
|
+
if (chr === '\\') {
|
|
459
|
+
result.push(parseEscape(p));
|
|
460
|
+
chr = str[p.idx];
|
|
461
|
+
} else {
|
|
462
|
+
result.push(chr);
|
|
463
|
+
chr = str[++p.idx];
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (chr === q) {
|
|
468
|
+
chr = str[++p.idx];
|
|
469
|
+
} else {
|
|
470
|
+
throw new Error();
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return result.join('');
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
export const parseUnsignedInteger = (input) => {
|
|
477
|
+
let p = buildParser(input);
|
|
478
|
+
let { str } = p;
|
|
479
|
+
let chr = str[p.idx];
|
|
480
|
+
let digits = [];
|
|
481
|
+
|
|
482
|
+
while (chr >= '0' && chr <= '9') {
|
|
483
|
+
digits.push(chr);
|
|
484
|
+
chr = str[++p.idx];
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return parseInt(digits.join(''), 10);
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
export const parseEscape = (input) => {
|
|
491
|
+
let p = buildParser(input);
|
|
492
|
+
let { str } = p;
|
|
493
|
+
let chr = str[p.idx];
|
|
494
|
+
|
|
495
|
+
if (chr !== '\\') throw new Error();
|
|
496
|
+
chr = str[++p.idx];
|
|
497
|
+
|
|
498
|
+
switch (chr) {
|
|
499
|
+
case 'u': {
|
|
500
|
+
chr = str[++p.idx];
|
|
501
|
+
|
|
502
|
+
let digits = [];
|
|
503
|
+
let q = chr === '{' ? chr : null;
|
|
504
|
+
if (q) {
|
|
505
|
+
chr = str[++p.idx];
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
let i = 0;
|
|
509
|
+
while (q ? chr !== '}' : i < 4) {
|
|
510
|
+
if (
|
|
511
|
+
!((chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z'))
|
|
512
|
+
)
|
|
513
|
+
throw new Error();
|
|
514
|
+
|
|
515
|
+
digits.push(chr);
|
|
516
|
+
chr = str[++p.idx];
|
|
517
|
+
i++;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (q) {
|
|
521
|
+
if (chr !== '}') throw new Error();
|
|
522
|
+
chr = str[++p.idx];
|
|
523
|
+
}
|
|
524
|
+
return String.fromCodePoint(parseInt(digits.join(''), 16));
|
|
525
|
+
}
|
|
526
|
+
case 'r':
|
|
527
|
+
chr = str[++p.idx];
|
|
528
|
+
return '\r';
|
|
529
|
+
case 'n':
|
|
530
|
+
chr = str[++p.idx];
|
|
531
|
+
return '\n';
|
|
532
|
+
case 't':
|
|
533
|
+
chr = str[++p.idx];
|
|
534
|
+
return '\t';
|
|
535
|
+
case '\\':
|
|
536
|
+
case '"':
|
|
537
|
+
case "'":
|
|
538
|
+
case '`':
|
|
539
|
+
++p.idx;
|
|
540
|
+
return chr;
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
export const parseExpression = (input) => {
|
|
545
|
+
let p = buildParser(input);
|
|
546
|
+
let { str } = p;
|
|
547
|
+
let chr = str[p.idx];
|
|
548
|
+
|
|
549
|
+
switch (chr) {
|
|
550
|
+
case '{':
|
|
551
|
+
return parseObject(p);
|
|
552
|
+
case '[':
|
|
553
|
+
return parseArray(p);
|
|
554
|
+
case '"':
|
|
555
|
+
case "'":
|
|
556
|
+
return parseString(p);
|
|
557
|
+
case 'n':
|
|
558
|
+
if (match(p, 'null')) {
|
|
559
|
+
p.idx += 4;
|
|
560
|
+
return null;
|
|
561
|
+
} else {
|
|
562
|
+
throw new Error();
|
|
563
|
+
}
|
|
564
|
+
case 't':
|
|
565
|
+
if (match(p, 'true')) {
|
|
566
|
+
p.idx += 4;
|
|
567
|
+
return true;
|
|
568
|
+
} else {
|
|
569
|
+
throw new Error();
|
|
570
|
+
}
|
|
571
|
+
case 'f':
|
|
572
|
+
if (match(p, 'false')) {
|
|
573
|
+
p.idx += 5;
|
|
574
|
+
return false;
|
|
575
|
+
} else {
|
|
576
|
+
throw new Error();
|
|
577
|
+
}
|
|
578
|
+
case 'u':
|
|
579
|
+
if (match(p, 'undefined')) {
|
|
580
|
+
p.idx += 9;
|
|
581
|
+
return undefined;
|
|
582
|
+
} else {
|
|
583
|
+
throw new Error();
|
|
584
|
+
}
|
|
585
|
+
case 'N':
|
|
586
|
+
if (match(p, 'NaN')) {
|
|
587
|
+
p.idx += 3;
|
|
588
|
+
return NaN;
|
|
589
|
+
} else {
|
|
590
|
+
throw new Error();
|
|
591
|
+
}
|
|
592
|
+
case 'I':
|
|
593
|
+
if (match(p, 'Infinity')) {
|
|
594
|
+
p.idx += 8;
|
|
595
|
+
return Infinity;
|
|
596
|
+
} else {
|
|
597
|
+
throw new Error();
|
|
598
|
+
}
|
|
599
|
+
case '-':
|
|
600
|
+
if (str[p.idx + 1] === 'I' && match(p, '-Infinity')) {
|
|
601
|
+
p.idx += 9;
|
|
602
|
+
return -Infinity;
|
|
603
|
+
} else {
|
|
604
|
+
throw new Error();
|
|
605
|
+
}
|
|
606
|
+
case '+':
|
|
607
|
+
if (str[p.idx + 1] === 'I' && match(p, '+Infinity')) {
|
|
608
|
+
p.idx += 9;
|
|
609
|
+
return Infinity;
|
|
610
|
+
} else {
|
|
611
|
+
throw new Error();
|
|
612
|
+
}
|
|
613
|
+
default:
|
|
614
|
+
if (chr >= '0' && chr <= '9') {
|
|
615
|
+
return parseUnsignedInteger(p);
|
|
616
|
+
} else {
|
|
617
|
+
throw new Error();
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
export const parseIdentifier = (input) => {
|
|
623
|
+
let p = buildParser(input);
|
|
624
|
+
let { str } = p;
|
|
625
|
+
let chr = str[p.idx];
|
|
626
|
+
let value = [];
|
|
627
|
+
|
|
628
|
+
let q = chr === '`' ? chr : null;
|
|
629
|
+
if (q) {
|
|
630
|
+
chr = str[++p.idx];
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
let lit, esc;
|
|
634
|
+
do {
|
|
635
|
+
lit = null;
|
|
636
|
+
esc = null;
|
|
637
|
+
if (chr === '\\') {
|
|
638
|
+
esc = parseEscape(p);
|
|
639
|
+
chr = str[p.idx];
|
|
640
|
+
} else {
|
|
641
|
+
if (!q) {
|
|
642
|
+
if (!value.length) {
|
|
643
|
+
if (canStartIdentifier(chr)) {
|
|
644
|
+
lit = chr;
|
|
645
|
+
value.push(chr);
|
|
646
|
+
chr = str[++p.idx];
|
|
647
|
+
} else {
|
|
648
|
+
throw new Error();
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
while (chr && canContinueIdentifier(chr)) {
|
|
653
|
+
lit = chr;
|
|
654
|
+
value.push(chr);
|
|
655
|
+
chr = str[++p.idx];
|
|
656
|
+
}
|
|
657
|
+
} else {
|
|
658
|
+
while (!'`\\\r\n'.includes(chr)) {
|
|
659
|
+
lit = chr;
|
|
660
|
+
value.push(chr);
|
|
661
|
+
chr = str[++p.idx];
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
} while (lit || esc);
|
|
666
|
+
|
|
667
|
+
if (q) {
|
|
668
|
+
if (chr !== '`') throw new Error();
|
|
669
|
+
chr = str[++p.idx];
|
|
670
|
+
}
|
|
671
|
+
return value.join('');
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
let buildTagParser = (tag) => {
|
|
675
|
+
switch (typeof tag) {
|
|
676
|
+
case 'string':
|
|
677
|
+
return { idx: 0, str: tag };
|
|
678
|
+
case 'object':
|
|
679
|
+
if (tag.type) {
|
|
680
|
+
// TODO str is sometimes not a string!
|
|
681
|
+
return { idx: 0, str: typeof tag === 'string' ? tag : printTag(tag) };
|
|
682
|
+
} else {
|
|
683
|
+
return tag;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
export const parseOpenNodeTag = (tag) => {
|
|
689
|
+
let p = buildTagParser(tag);
|
|
690
|
+
let { str } = p;
|
|
691
|
+
let chr = str[p.idx];
|
|
692
|
+
|
|
693
|
+
if (chr !== '<') throw new Error();
|
|
694
|
+
chr = str[++p.idx];
|
|
695
|
+
let token = false;
|
|
696
|
+
let hasGap = false;
|
|
697
|
+
if (chr === '*') {
|
|
698
|
+
chr = str[++p.idx];
|
|
699
|
+
token = true;
|
|
700
|
+
}
|
|
701
|
+
if (chr === '$') {
|
|
702
|
+
chr = str[++p.idx];
|
|
703
|
+
hasGap = true;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
let flags = token ? tokenFlags : nodeFlags;
|
|
707
|
+
|
|
708
|
+
let type = null;
|
|
709
|
+
let name = null;
|
|
710
|
+
|
|
711
|
+
if (chr === '_') {
|
|
712
|
+
chr = str[++p.idx];
|
|
713
|
+
type = Symbol.for('_');
|
|
714
|
+
|
|
715
|
+
if (chr === '_') {
|
|
716
|
+
chr = str[++p.idx];
|
|
717
|
+
type = Symbol.for('__');
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (!` {'"/>`.includes(chr)) {
|
|
721
|
+
name = parseIdentifier(p);
|
|
722
|
+
chr = str[p.idx];
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
while (chr === ' ') {
|
|
726
|
+
chr = str[++p.idx];
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
let literalValue = null;
|
|
730
|
+
|
|
731
|
+
if (`'"`.includes(chr)) {
|
|
732
|
+
literalValue = parseString(p);
|
|
733
|
+
chr = str[p.idx];
|
|
734
|
+
|
|
735
|
+
while (chr === ' ') chr = str[++p.idx];
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
let attributes = freezeRecord({});
|
|
739
|
+
|
|
740
|
+
if (chr === '{') {
|
|
741
|
+
attributes = parseObject(p);
|
|
742
|
+
chr = str[p.idx];
|
|
743
|
+
|
|
744
|
+
while (chr === ' ') chr = str[++p.idx];
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
let selfClosing = false;
|
|
748
|
+
|
|
749
|
+
if (chr === '/') {
|
|
750
|
+
chr = str[++p.idx];
|
|
751
|
+
selfClosing = true;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (chr === '>') {
|
|
755
|
+
chr = str[++p.idx];
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if (isString(tag) && p.idx !== str.length) throw new Error();
|
|
759
|
+
|
|
760
|
+
if (hasGap) flags = getFlagsWithGap(flags);
|
|
761
|
+
|
|
762
|
+
return buildTag(OpenNodeTag, {
|
|
763
|
+
flags,
|
|
764
|
+
name: symbolName(name),
|
|
765
|
+
type: symbolName(type),
|
|
766
|
+
literalValue,
|
|
767
|
+
attributes: printExpression(attributes),
|
|
768
|
+
selfClosing,
|
|
769
|
+
});
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
export const parseReferenceFlags = (input) => {
|
|
773
|
+
let p = buildTagParser(input);
|
|
774
|
+
let { str } = p;
|
|
775
|
+
let chr = str[p.idx];
|
|
776
|
+
let array = false;
|
|
777
|
+
let expression = false;
|
|
778
|
+
let intrinsic = false;
|
|
779
|
+
let hasGap = false;
|
|
780
|
+
|
|
781
|
+
if (chr === '[') {
|
|
782
|
+
chr = str[++p.idx];
|
|
783
|
+
array = true;
|
|
784
|
+
if (chr !== ']') throw new Error();
|
|
785
|
+
chr = str[++p.idx];
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (chr === '+') {
|
|
789
|
+
chr = str[++p.idx];
|
|
790
|
+
expression = true;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if (chr === '*') {
|
|
794
|
+
chr = str[++p.idx];
|
|
795
|
+
intrinsic = true;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (chr === '$') {
|
|
799
|
+
chr = str[++p.idx];
|
|
800
|
+
hasGap = true;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
return freezeRecord({ array, expression, intrinsic, hasGap });
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
export const parseReferenceTag = (tag) => {
|
|
807
|
+
let p = buildTagParser(tag);
|
|
808
|
+
let { str } = p;
|
|
809
|
+
let chr = str[p.idx];
|
|
810
|
+
let type = null;
|
|
811
|
+
let name = null;
|
|
812
|
+
|
|
813
|
+
if ('.#@_'.includes(chr)) {
|
|
814
|
+
type = chr;
|
|
815
|
+
chr = str[++p.idx];
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
if (!type || (type === '#' && canStartIdentifier(chr))) {
|
|
819
|
+
name = parseIdentifier(p);
|
|
820
|
+
chr = str[p.idx];
|
|
821
|
+
}
|
|
822
|
+
while (chr === ' ') chr = str[++p.idx];
|
|
823
|
+
|
|
824
|
+
let flags = parseReferenceFlags(p);
|
|
825
|
+
chr = str[p.idx];
|
|
826
|
+
|
|
827
|
+
while (chr === ' ') chr = str[++p.idx];
|
|
828
|
+
|
|
829
|
+
if (chr !== ':') throw new Error();
|
|
830
|
+
chr = str[++p.idx];
|
|
831
|
+
|
|
832
|
+
if (isString(tag) && p.idx !== p.str.length) throw new Error();
|
|
833
|
+
|
|
834
|
+
return buildTag(ReferenceTag, { type, name, flags });
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
export const parseLiteralTag = (tag) => {
|
|
838
|
+
let p = buildTagParser(tag);
|
|
839
|
+
|
|
840
|
+
let str = parseString(p);
|
|
841
|
+
|
|
842
|
+
if (isString(tag) && p.idx !== p.str.length) throw new Error();
|
|
843
|
+
|
|
844
|
+
return buildLiteralTag(str);
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
export const parseCloseNodeTag = (tag) => {
|
|
848
|
+
let p = buildTagParser(tag);
|
|
849
|
+
if (!match(p, '</>')) throw new Error();
|
|
850
|
+
p.idx += 3;
|
|
851
|
+
|
|
852
|
+
if (isString(tag) && p.idx !== p.str.length) throw new Error();
|
|
853
|
+
|
|
854
|
+
return buildCloseNodeTag();
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
export const parseGapTag = (tag) => {
|
|
858
|
+
let p = buildTagParser(tag);
|
|
859
|
+
if (!match(p, '<//>')) throw new Error();
|
|
860
|
+
p.idx += 4;
|
|
861
|
+
if (isString(tag) && p.idx !== p.str.length) throw new Error();
|
|
862
|
+
return buildGapTag();
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
export const parseNullTag = (tag) => {
|
|
866
|
+
let p = buildTagParser(tag);
|
|
867
|
+
if (!match(p, 'null')) throw new Error();
|
|
868
|
+
p.idx += 4;
|
|
869
|
+
if (isString(tag) && p.idx !== p.str.length) throw new Error();
|
|
870
|
+
return buildNullTag();
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
export const parseShiftTag = (tag) => {
|
|
874
|
+
let p = buildTagParser(tag);
|
|
875
|
+
if (!match(p, '^^^')) throw new Error();
|
|
876
|
+
p.idx += 3;
|
|
877
|
+
if (isString(tag) && p.idx !== p.str.length) throw new Error();
|
|
878
|
+
return buildShiftTag();
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
export const parseIdentifierPath = (p) => {
|
|
882
|
+
let { str } = buildParser(p);
|
|
883
|
+
let chr = str[p.idx];
|
|
884
|
+
let segments = [];
|
|
885
|
+
|
|
886
|
+
let sep = true;
|
|
887
|
+
while (sep) {
|
|
888
|
+
segments.push(parseIdentifier(p));
|
|
889
|
+
chr = str[p.idx];
|
|
890
|
+
|
|
891
|
+
sep = chr === '.' ? chr : null;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
return freezeRecord(segments);
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
export const parseAttributeDefinitionTag = (tag) => {
|
|
898
|
+
let p = buildTagParser(tag);
|
|
899
|
+
let { str } = p;
|
|
900
|
+
let chr = str[p.idx];
|
|
901
|
+
|
|
902
|
+
if (chr !== '{') throw new Error();
|
|
903
|
+
chr = str[++p.idx];
|
|
904
|
+
|
|
905
|
+
while (chr === ' ') {
|
|
906
|
+
chr = str[++p.idx];
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
let key = parseIdentifierPath(p);
|
|
910
|
+
chr = str[p.idx];
|
|
911
|
+
|
|
912
|
+
if (chr !== ':') throw new Error();
|
|
913
|
+
chr = str[++p.idx];
|
|
914
|
+
|
|
915
|
+
while (chr === ' ') {
|
|
916
|
+
chr = str[++p.idx];
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
let value = parseExpression(p);
|
|
920
|
+
chr = str[p.idx];
|
|
921
|
+
|
|
922
|
+
while (chr === ' ') {
|
|
923
|
+
chr = str[++p.idx];
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
if (chr !== '}') throw new Error();
|
|
927
|
+
chr = str[++p.idx];
|
|
928
|
+
|
|
929
|
+
if (isString(tag) && p.idx !== p.str.length) throw new Error();
|
|
930
|
+
|
|
931
|
+
return buildTag(AttributeDefinition, { path: key, value });
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
export const parseBindingTag = (tag) => {
|
|
935
|
+
let p = buildTagParser(tag);
|
|
936
|
+
let { str } = p;
|
|
937
|
+
let chr = str[p.idx];
|
|
938
|
+
let type = null;
|
|
939
|
+
let name = null;
|
|
940
|
+
|
|
941
|
+
if (chr !== ':') throw new Error();
|
|
942
|
+
chr = str[++p.idx];
|
|
943
|
+
|
|
944
|
+
if (chr === '.' && str[p.idx + 1] === '.') {
|
|
945
|
+
p.idx += 2;
|
|
946
|
+
type = Symbol.for('..');
|
|
947
|
+
} else {
|
|
948
|
+
name = Symbol.for(parseIdentifier(p));
|
|
949
|
+
chr = str[p.idx];
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (chr !== ':') throw new Error();
|
|
953
|
+
chr = str[++p.idx];
|
|
954
|
+
|
|
955
|
+
if (isString(tag) && p.idx !== p.str.length) throw new Error();
|
|
956
|
+
|
|
957
|
+
return buildBindingTag(type, name);
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
export const parseDoctypeTag = (tag) => {
|
|
961
|
+
let p = buildTagParser(tag);
|
|
962
|
+
let { str } = p;
|
|
963
|
+
let chr = str[p.idx];
|
|
964
|
+
|
|
965
|
+
if (chr !== '<' || str[p.idx + 1] !== '!') throw new Error();
|
|
966
|
+
p.idx += 2;
|
|
967
|
+
|
|
968
|
+
let version = parseUnsignedInteger(p);
|
|
969
|
+
chr = str[p.idx];
|
|
970
|
+
|
|
971
|
+
if (!match(p, ':cstml')) throw new Error();
|
|
972
|
+
p.idx += 6;
|
|
973
|
+
chr = str[p.idx];
|
|
974
|
+
|
|
975
|
+
while (chr === ' ') {
|
|
976
|
+
chr = str[++p.idx];
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
let attributes = {};
|
|
980
|
+
if (chr === '{') {
|
|
981
|
+
attributes = parseObject(p);
|
|
982
|
+
chr = str[p.idx];
|
|
983
|
+
|
|
984
|
+
while (chr === ' ') chr = str[++p.idx];
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
if (chr !== '>') throw new Error();
|
|
988
|
+
chr = str[++p.idx];
|
|
989
|
+
|
|
990
|
+
if (isString(tag) && p.idx !== p.str.length) throw new Error();
|
|
991
|
+
|
|
992
|
+
return buildDoctypeTag(version, printExpression(attributes));
|
|
993
|
+
};
|
|
994
|
+
|
|
995
|
+
export const parseStreamTag = (tag) => {
|
|
996
|
+
let p = buildTagParser(tag);
|
|
997
|
+
let { str } = p;
|
|
998
|
+
let chr = str[p.idx];
|
|
999
|
+
|
|
1000
|
+
if (chr !== '<') throw new Error();
|
|
1001
|
+
chr = str[++p.idx];
|
|
1002
|
+
|
|
1003
|
+
let processPath = [];
|
|
1004
|
+
let stream = 1;
|
|
1005
|
+
|
|
1006
|
+
if (chr >= '0' && chr <= '9') {
|
|
1007
|
+
do {
|
|
1008
|
+
let digits = [];
|
|
1009
|
+
while (chr >= '0' && chr <= '9') {
|
|
1010
|
+
digits.push(chr);
|
|
1011
|
+
chr = str[++p.idx];
|
|
1012
|
+
}
|
|
1013
|
+
processPath.push(parseInt(digits.join(''), 10));
|
|
1014
|
+
} while (chr === '.');
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
if (chr === '-') {
|
|
1018
|
+
chr = str[++p.idx];
|
|
1019
|
+
if ('1234'.includes(chr)) {
|
|
1020
|
+
stream = parseInt(chr, 10);
|
|
1021
|
+
chr = str[++p.idx];
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
if (chr !== '>') throw new Error();
|
|
1026
|
+
chr = str[++p.idx];
|
|
1027
|
+
|
|
1028
|
+
return buildStreamTag(processPath, stream);
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1031
|
+
export const parseTag = (tag) => {
|
|
1032
|
+
let p = buildTagParser(tag);
|
|
1033
|
+
if (tag == null) return null;
|
|
1034
|
+
|
|
1035
|
+
let tagType = parseTagType(p);
|
|
1036
|
+
|
|
1037
|
+
switch (tagType) {
|
|
1038
|
+
case DoctypeTag:
|
|
1039
|
+
return parseDoctypeTag(tag);
|
|
1040
|
+
case AttributeDefinition:
|
|
1041
|
+
return parseAttributeDefinitionTag(tag);
|
|
1042
|
+
case OpenNodeTag:
|
|
1043
|
+
return parseOpenNodeTag(tag);
|
|
1044
|
+
case ReferenceTag:
|
|
1045
|
+
return parseReferenceTag(tag);
|
|
1046
|
+
case BindingTag:
|
|
1047
|
+
return parseBindingTag(tag);
|
|
1048
|
+
case LiteralTag:
|
|
1049
|
+
return parseLiteralTag(tag);
|
|
1050
|
+
case CloseNodeTag:
|
|
1051
|
+
return parseCloseNodeTag(tag);
|
|
1052
|
+
case StreamTag:
|
|
1053
|
+
return parseStreamTag(tag);
|
|
1054
|
+
case GapTag:
|
|
1055
|
+
return parseGapTag(tag);
|
|
1056
|
+
case NullTag:
|
|
1057
|
+
return parseNullTag(tag);
|
|
1058
|
+
case ShiftTag:
|
|
1059
|
+
return parseShiftTag(tag);
|
|
1060
|
+
case Property:
|
|
1061
|
+
return tag;
|
|
1062
|
+
case TreeNode:
|
|
1063
|
+
case GapNode:
|
|
1064
|
+
case NullNode:
|
|
1065
|
+
default:
|
|
1066
|
+
throw new Error();
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
|
|
1070
|
+
export const isValidTag = (tag) => {
|
|
1071
|
+
if (isString(tag)) return !!parseTag(tag);
|
|
1072
|
+
if (!isFrozen(tag)) return false;
|
|
1073
|
+
if (!hasOwn(tag, 'type') || !hasOwn(tag, 'value')) return false;
|
|
1074
|
+
|
|
1075
|
+
switch (tag.type) {
|
|
1076
|
+
case ReferenceTag:
|
|
1077
|
+
case OpenNodeTag:
|
|
1078
|
+
case AttributeDefinition:
|
|
1079
|
+
case CloseNodeTag:
|
|
1080
|
+
case ShiftTag:
|
|
1081
|
+
case NullTag:
|
|
1082
|
+
case GapTag:
|
|
1083
|
+
case LiteralTag:
|
|
1084
|
+
case BindingTag:
|
|
1085
|
+
case DoctypeTag:
|
|
1086
|
+
return parseTag(tag);
|
|
1087
|
+
case Property:
|
|
1088
|
+
case TreeNode:
|
|
1089
|
+
case GapNode:
|
|
1090
|
+
case NullNode:
|
|
1091
|
+
return false;
|
|
1092
|
+
|
|
1093
|
+
default:
|
|
1094
|
+
throw new Error();
|
|
1095
|
+
}
|
|
1096
|
+
};
|