@bablr/agast-helpers 0.10.9 → 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 +916 -120
- 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 +584 -635
- 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,89 +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();
|
|
29
|
-
|
|
30
|
-
for (const value of values(item)) {
|
|
31
|
-
if (isObject(value)) {
|
|
32
|
-
list = list.push(value);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
freeze(item);
|
|
37
|
-
}
|
|
25
|
+
export const symbolName = (name) => {
|
|
26
|
+
return isString(name) ? Symbol.for(name) : name;
|
|
38
27
|
};
|
|
39
28
|
|
|
40
|
-
export const buildSpan = (name, guard = null, props = {}) => {
|
|
29
|
+
export const buildSpan = (name, guard = null, props = '{}') => {
|
|
41
30
|
if (!name) throw new Error();
|
|
42
|
-
|
|
43
|
-
|
|
31
|
+
if (!isString(props)) throw new Error();
|
|
32
|
+
|
|
33
|
+
return freezeRecord({ name, guard, props });
|
|
44
34
|
};
|
|
45
35
|
|
|
46
|
-
export const
|
|
47
|
-
|
|
48
|
-
deepFreeze(props);
|
|
49
|
-
return freeze({ type, name, guard, props });
|
|
36
|
+
export const buildSpanEntry = (name, guard = null, props = '{}') => {
|
|
37
|
+
return freezeRecord([name, buildSpan(name, guard, props)]);
|
|
50
38
|
};
|
|
51
39
|
|
|
52
40
|
export const buildProperty = (tags, shift) => {
|
|
53
41
|
if (shift && !shift.index) throw new Error();
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (tags[0] && ![ReferenceTag, ShiftTag].includes(tags[0].type)) throw new Error();
|
|
57
|
-
if (tags.length > 1 && !isArray(tags[1])) throw new Error();
|
|
42
|
+
let { 0: open, 1: bindings, 2: node } = tags[1];
|
|
58
43
|
|
|
59
|
-
|
|
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();
|
|
60
53
|
|
|
61
54
|
// if (property.node && !(tags.length === 3)) throw new Error();
|
|
62
55
|
// if (tags[0].type === ShiftTag && !property.shift) throw new Error();
|
|
63
56
|
|
|
64
|
-
if ((
|
|
65
|
-
if (
|
|
57
|
+
if (!isArray(open) && (parseTagType(open) === ShiftTag) !== !!shift) throw new Error();
|
|
58
|
+
if (node && ![NullNode, GapNode, TreeNode].includes(node.type)) throw new Error();
|
|
66
59
|
|
|
67
|
-
return
|
|
60
|
+
return freezeRecord({
|
|
68
61
|
tags,
|
|
69
|
-
reference:
|
|
70
|
-
bindings:
|
|
71
|
-
|
|
72
|
-
|
|
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,
|
|
73
68
|
});
|
|
74
69
|
};
|
|
75
70
|
|
|
76
71
|
export const buildPathFrame = (property, parentIndex = null, isGap = false) => {
|
|
77
|
-
return
|
|
72
|
+
return freezeRecord({ property, parentIndex, isGap });
|
|
78
73
|
};
|
|
79
74
|
|
|
80
75
|
export const buildPropertyTag = (tags, shift) => {
|
|
81
|
-
|
|
76
|
+
if (BList.getType(tags) !== Symbol.for('List')) throw new Error();
|
|
77
|
+
return buildTag(Property, buildProperty(tags, shift));
|
|
82
78
|
};
|
|
83
79
|
|
|
84
80
|
export const buildDocument = (doctypeTag, tree) => {
|
|
85
|
-
return
|
|
81
|
+
return buildTag(Document, { doctypeTag, tree });
|
|
86
82
|
};
|
|
87
83
|
|
|
88
84
|
export const buildBeginningOfStreamToken = () => {
|
|
89
|
-
return
|
|
85
|
+
return buildTag(Symbol.for('@bablr/beginning-of-stream'));
|
|
90
86
|
};
|
|
91
87
|
|
|
92
88
|
export const buildReferenceTag = (type = null, name = null, flags = referenceFlags) => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return freeze({
|
|
96
|
-
type: ReferenceTag,
|
|
97
|
-
value: buildReference(type, name, flags),
|
|
98
|
-
});
|
|
89
|
+
return buildTag(ReferenceTag, buildReference(type, name, flags));
|
|
99
90
|
};
|
|
100
91
|
|
|
101
92
|
export const buildReference = (type = null, name = null, flags = referenceFlags) => {
|
|
93
|
+
if (!Object.isFrozen(flags)) throw new Error();
|
|
102
94
|
if (type != null && !['_', '.', '#', '@'].includes(type)) throw new Error();
|
|
103
95
|
if (type && ['_', '#', '@'].includes(type) && (flags.intrinsic || flags.hasGap))
|
|
104
96
|
throw new Error();
|
|
@@ -112,74 +104,78 @@ export const buildReference = (type = null, name = null, flags = referenceFlags)
|
|
|
112
104
|
|
|
113
105
|
let type_ = type == null && name == null ? '.' : type;
|
|
114
106
|
|
|
115
|
-
return
|
|
107
|
+
return freezeRecord({
|
|
108
|
+
type: type_,
|
|
109
|
+
name,
|
|
110
|
+
flags: freezeRecord({ array, expression, intrinsic, hasGap }),
|
|
111
|
+
});
|
|
116
112
|
};
|
|
117
113
|
|
|
118
114
|
export const referenceFromMatcher = (matcher) => {
|
|
115
|
+
if (!matcher) return buildReference();
|
|
119
116
|
let { type, name, flags } = matcher;
|
|
117
|
+
|
|
120
118
|
return buildReference(type, name, flags);
|
|
121
119
|
};
|
|
122
120
|
|
|
121
|
+
export const buildBounds = (leading, trailing) => {
|
|
122
|
+
return freezeRecord({ leading, trailing });
|
|
123
|
+
};
|
|
124
|
+
|
|
123
125
|
export const buildShift = (index, height) => {
|
|
124
126
|
if (index == null || height == null) throw new Error();
|
|
125
|
-
|
|
127
|
+
|
|
128
|
+
return freezeRecord({ index, height });
|
|
126
129
|
};
|
|
127
130
|
|
|
128
131
|
export const buildNullTag = () => {
|
|
129
|
-
return
|
|
132
|
+
return buildTag(NullTag);
|
|
130
133
|
};
|
|
131
134
|
|
|
132
|
-
export const buildBinding = (
|
|
133
|
-
|
|
134
|
-
if (
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
freeze(isString(segment) ? { type: null, name: segment } : segment),
|
|
139
|
-
),
|
|
140
|
-
});
|
|
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 });
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
-
export const buildBindingTag = (
|
|
144
|
-
return
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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 });
|
|
148
151
|
};
|
|
149
152
|
|
|
150
153
|
export const buildChild = (type, value) => {
|
|
151
154
|
if (!isSymbol(type)) throw new Error();
|
|
152
|
-
|
|
155
|
+
let child = buildTag(type, value);
|
|
156
|
+
if (!parseTag(child)) throw new Error();
|
|
157
|
+
return child;
|
|
153
158
|
};
|
|
154
159
|
|
|
155
160
|
export const buildGapTag = () => {
|
|
156
|
-
return
|
|
161
|
+
return buildTag(GapTag);
|
|
157
162
|
};
|
|
158
163
|
|
|
159
164
|
export const buildShiftTag = () => {
|
|
160
|
-
|
|
161
|
-
{},
|
|
162
|
-
{
|
|
163
|
-
index: {
|
|
164
|
-
get() {
|
|
165
|
-
throw new Error('moved');
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
height: {
|
|
169
|
-
get() {
|
|
170
|
-
throw new Error('moved');
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
);
|
|
175
|
-
return freeze({ type: ShiftTag, value });
|
|
165
|
+
return buildTag(ShiftTag);
|
|
176
166
|
};
|
|
177
167
|
|
|
178
|
-
export const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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));
|
|
183
179
|
};
|
|
184
180
|
|
|
185
181
|
export const buildOpenFragmentTag = (flags = nodeFlags, selfClosing = false) => {
|
|
@@ -194,55 +190,67 @@ export const buildOpenNodeTag = (
|
|
|
194
190
|
flags,
|
|
195
191
|
name,
|
|
196
192
|
literalValue = null,
|
|
197
|
-
attributes = {},
|
|
193
|
+
attributes = '{}',
|
|
198
194
|
selfClosing = !!literalValue,
|
|
199
195
|
) => {
|
|
200
|
-
if (!name && !flags.token) throw new Error();
|
|
201
196
|
return buildFullOpenNodeTag(flags, null, name, literalValue, attributes, selfClosing);
|
|
202
197
|
};
|
|
203
198
|
|
|
204
|
-
export const
|
|
199
|
+
export const buildOpenNode = (
|
|
205
200
|
flags = nodeFlags,
|
|
206
201
|
type = Symbol.for('__'),
|
|
207
202
|
name = null,
|
|
208
203
|
literalValue = null,
|
|
209
|
-
attributes = {},
|
|
204
|
+
attributes = '{}',
|
|
210
205
|
selfClosing = !!literalValue,
|
|
211
206
|
) => {
|
|
212
|
-
if (!
|
|
207
|
+
if (!isString(attributes)) throw new Error();
|
|
213
208
|
if (literalValue && !isString(literalValue)) throw new Error();
|
|
214
|
-
if (!type && !name && !flags.token &&
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
literalValue,
|
|
225
|
-
attributes,
|
|
226
|
-
selfClosing,
|
|
227
|
-
}),
|
|
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,
|
|
228
219
|
});
|
|
229
220
|
};
|
|
230
221
|
|
|
231
|
-
export const
|
|
232
|
-
|
|
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
|
+
);
|
|
233
235
|
};
|
|
234
236
|
|
|
235
|
-
const
|
|
237
|
+
export const buildCloseNodeTag = () => {
|
|
238
|
+
return buildTag(CloseNodeTag);
|
|
239
|
+
};
|
|
236
240
|
|
|
237
241
|
export const buildLiteralTag = (value) => {
|
|
238
242
|
if (!isString(value)) throw new Error('invalid literal');
|
|
239
|
-
return
|
|
243
|
+
return buildTag(LiteralTag, value);
|
|
240
244
|
};
|
|
241
245
|
|
|
242
246
|
export const buildAttributeDefinition = (key, value) => {
|
|
243
247
|
if (!key?.length) throw new Error();
|
|
244
|
-
|
|
245
|
-
return
|
|
248
|
+
|
|
249
|
+
return freezeRecord({ path: key, value });
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export const buildAttributeDefinitionTag = (key, value) => {
|
|
253
|
+
return buildTag(AttributeDefinition, buildAttributeDefinition(key, value));
|
|
246
254
|
};
|
|
247
255
|
|
|
248
256
|
const flagsWithGap = new WeakMap();
|
|
@@ -252,7 +260,7 @@ export const getFlagsWithGap = (flags, hasGap = true) => {
|
|
|
252
260
|
|
|
253
261
|
let gapFlags = flagsWithGap.get(flags);
|
|
254
262
|
if (!gapFlags) {
|
|
255
|
-
gapFlags =
|
|
263
|
+
gapFlags = freezeRecord({
|
|
256
264
|
token: flags.token,
|
|
257
265
|
hasGap,
|
|
258
266
|
});
|
|
@@ -261,40 +269,828 @@ export const getFlagsWithGap = (flags, hasGap = true) => {
|
|
|
261
269
|
return gapFlags;
|
|
262
270
|
};
|
|
263
271
|
|
|
264
|
-
export const nodeFlags =
|
|
272
|
+
export const nodeFlags = freezeRecord({
|
|
265
273
|
token: false,
|
|
266
274
|
hasGap: false,
|
|
267
275
|
});
|
|
268
276
|
|
|
269
|
-
export const tokenFlags =
|
|
277
|
+
export const tokenFlags = freezeRecord({
|
|
270
278
|
token: true,
|
|
271
279
|
hasGap: false,
|
|
272
280
|
});
|
|
273
281
|
|
|
274
|
-
export const referenceFlags =
|
|
282
|
+
export const referenceFlags = freezeRecord({
|
|
275
283
|
array: false,
|
|
276
284
|
expression: false,
|
|
277
285
|
intrinsic: false,
|
|
278
286
|
hasGap: false,
|
|
279
287
|
});
|
|
280
288
|
|
|
281
|
-
export const intrinsicReferenceFlags =
|
|
289
|
+
export const intrinsicReferenceFlags = freezeRecord({
|
|
282
290
|
array: false,
|
|
283
291
|
expression: false,
|
|
284
292
|
intrinsic: true,
|
|
285
293
|
hasGap: false,
|
|
286
294
|
});
|
|
287
295
|
|
|
288
|
-
export const gapReferenceFlags =
|
|
296
|
+
export const gapReferenceFlags = freezeRecord({
|
|
289
297
|
array: false,
|
|
290
298
|
expression: false,
|
|
291
299
|
intrinsic: false,
|
|
292
300
|
hasGap: true,
|
|
293
301
|
});
|
|
294
302
|
|
|
295
|
-
export const expressionReferenceFlags =
|
|
303
|
+
export const expressionReferenceFlags = freezeRecord({
|
|
296
304
|
array: false,
|
|
297
305
|
expression: true,
|
|
298
306
|
intrinsic: false,
|
|
299
307
|
hasGap: false,
|
|
300
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
|
+
};
|