@bablr/boot 0.1.8 → 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/README.md +19 -0
- package/lib/builders.js +422 -0
- package/lib/index.js +124 -19
- package/lib/languages/cstml.js +258 -93
- package/lib/languages/instruction.js +10 -5
- package/lib/languages/regex.js +124 -36
- package/lib/languages/spamex.js +31 -60
- package/lib/miniparser.js +12 -10
- package/lib/path.js +3 -3
- package/lib/print.js +352 -0
- package/lib/utils.js +9 -8
- package/package.json +8 -5
- package/shorthand.macro.js +201 -91
- package/lib/languages/number.js +0 -38
- package/lib/languages/string.js +0 -88
package/shorthand.macro.js
CHANGED
|
@@ -1,47 +1,29 @@
|
|
|
1
|
+
const { spawnSync } = require('node:child_process');
|
|
1
2
|
const t = require('@babel/types');
|
|
2
3
|
const { expression } = require('@babel/template');
|
|
4
|
+
const { diff } = require('jest-diff');
|
|
3
5
|
const isObject = require('iter-tools-es/methods/is-object');
|
|
4
6
|
const isUndefined = require('iter-tools-es/methods/is-undefined');
|
|
5
7
|
const isNull = require('iter-tools-es/methods/is-null');
|
|
6
8
|
const isString = require('iter-tools-es/methods/is-string');
|
|
7
9
|
const concat = require('iter-tools-es/methods/concat');
|
|
8
10
|
const { createMacro } = require('babel-plugin-macros');
|
|
9
|
-
const { TemplateParser } = require('./lib/
|
|
11
|
+
const { TemplateParser, set, getAgASTValue } = require('./lib/index.js');
|
|
10
12
|
const i = require('./lib/languages/instruction.js');
|
|
11
13
|
const re = require('./lib/languages/regex.js');
|
|
12
14
|
const spam = require('./lib/languages/spamex.js');
|
|
13
|
-
const str = require('./lib/languages/string.js');
|
|
14
|
-
const num = require('./lib/languages/number.js');
|
|
15
15
|
const cstml = require('./lib/languages/cstml.js');
|
|
16
16
|
const { addNamespace, addNamed } = require('@babel/helper-module-imports');
|
|
17
17
|
const { PathResolver } = require('@bablr/boot-helpers/path');
|
|
18
|
+
const { printPrettyCSTML } = require('./lib/print.js');
|
|
18
19
|
|
|
19
|
-
const { hasOwn } = Object;
|
|
20
20
|
const { isArray } = Array;
|
|
21
21
|
const isNumber = (v) => typeof v === 'number';
|
|
22
22
|
const isBoolean = (v) => typeof v === 'boolean';
|
|
23
|
-
|
|
24
23
|
const isPlainObject = (v) => isObject(v) && !isArray(v);
|
|
24
|
+
const printRef = (ref) => (ref.isArray ? `${ref.name}[]` : ref.name);
|
|
25
25
|
|
|
26
|
-
const
|
|
27
|
-
const { pathName, pathIsArray } = path;
|
|
28
|
-
if (pathIsArray) {
|
|
29
|
-
if (!obj[pathName]) {
|
|
30
|
-
obj[pathName] = [];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (!isArray(obj[pathName])) throw new Error('bad array value');
|
|
34
|
-
|
|
35
|
-
obj[pathName].push(value);
|
|
36
|
-
} else {
|
|
37
|
-
if (hasOwn(obj, pathName)) {
|
|
38
|
-
throw new Error('duplicate child name');
|
|
39
|
-
}
|
|
40
|
-
obj[pathName] = value;
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const getASTValue = (v, exprs, bindings) => {
|
|
26
|
+
const getBabelASTValue = (v, exprs, bindings) => {
|
|
45
27
|
return isNull(v)
|
|
46
28
|
? t.nullLiteral()
|
|
47
29
|
: isUndefined(v)
|
|
@@ -53,39 +35,52 @@ const getASTValue = (v, exprs, bindings) => {
|
|
|
53
35
|
: isBoolean(v)
|
|
54
36
|
? t.booleanLiteral(v)
|
|
55
37
|
: isArray(v)
|
|
56
|
-
? t.arrayExpression(v.map((v) =>
|
|
38
|
+
? t.arrayExpression(v.map((v) => getBabelASTValue(v, exprs, bindings)))
|
|
57
39
|
: isPlainObject(v) && !v.language
|
|
58
40
|
? t.objectExpression(
|
|
59
41
|
Object.entries(v).map(([k, v]) =>
|
|
60
|
-
t.objectProperty(t.identifier(k),
|
|
42
|
+
t.objectProperty(t.identifier(k), getBabelASTValue(v, exprs, bindings)),
|
|
61
43
|
),
|
|
62
44
|
)
|
|
63
|
-
:
|
|
45
|
+
: generateBabelNode(v, exprs, bindings);
|
|
64
46
|
};
|
|
65
47
|
|
|
66
|
-
const
|
|
48
|
+
const generateBabelNodeChild = (child, exprs, bindings) => {
|
|
67
49
|
if (child.type === 'Reference') {
|
|
68
|
-
|
|
69
|
-
const printedRef = pathIsArray ? `${pathName}[]` : pathName;
|
|
70
|
-
return expression(`%%t%%.ref\`${printedRef}\``)({ t: bindings.t });
|
|
50
|
+
return expression(`%%t%%.ref\`${printRef(child.value)}\``)({ t: bindings.t });
|
|
71
51
|
} else if (child.type === 'Literal') {
|
|
72
|
-
return expression(`%%t%%.lit
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
52
|
+
return expression(`%%t%%.lit(%%value%%)`)({
|
|
53
|
+
t: bindings.t,
|
|
54
|
+
value: getBabelASTValue(child.value, exprs, bindings),
|
|
55
|
+
});
|
|
56
|
+
} else if (child.type === 'Embedded') {
|
|
57
|
+
return expression(`%%t%%.embedded(%%value%%)`)({
|
|
77
58
|
t: bindings.t,
|
|
78
|
-
|
|
79
|
-
raw: child.raw,
|
|
59
|
+
value: generateBabelNode(child.value, exprs, bindings),
|
|
80
60
|
});
|
|
81
61
|
} else {
|
|
82
62
|
throw new Error(`Unknown child type ${child.type}`);
|
|
83
63
|
}
|
|
84
64
|
};
|
|
85
65
|
|
|
86
|
-
const
|
|
66
|
+
const getAgastNodeType = (flags) => {
|
|
67
|
+
if (flags.intrinsic && flags.token) {
|
|
68
|
+
return 's_i_node';
|
|
69
|
+
} else if (flags.token && flags.trivia) {
|
|
70
|
+
return 's_t_node';
|
|
71
|
+
} else if (flags.token && flags.escape) {
|
|
72
|
+
return 's_e_node';
|
|
73
|
+
} else if (flags.token) {
|
|
74
|
+
return 's_node';
|
|
75
|
+
} else {
|
|
76
|
+
return 'node';
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const generateBabelNode = (node, exprs, bindings) => {
|
|
87
81
|
const resolver = new PathResolver(node);
|
|
88
|
-
const { children, type, language, attributes } = node;
|
|
82
|
+
const { flags = {}, children, type, language, attributes } = node;
|
|
83
|
+
|
|
89
84
|
const properties_ = {};
|
|
90
85
|
const children_ = [];
|
|
91
86
|
|
|
@@ -94,64 +89,122 @@ const generateNode = (node, exprs, bindings) => {
|
|
|
94
89
|
}
|
|
95
90
|
|
|
96
91
|
for (const child of children) {
|
|
97
|
-
children_.push(generateNodeChild(child, bindings));
|
|
98
|
-
|
|
99
92
|
if (child.type === 'Reference') {
|
|
100
93
|
const path = child.value;
|
|
101
|
-
const { pathIsArray } = path;
|
|
94
|
+
const { isArray: pathIsArray } = path;
|
|
102
95
|
const resolved = resolver.get(path);
|
|
103
96
|
|
|
104
|
-
let embedded = resolved;
|
|
105
97
|
if (resolved) {
|
|
106
|
-
|
|
98
|
+
set(properties_, path, generateBabelNode(resolved, exprs, bindings));
|
|
99
|
+
children_.push(generateBabelNodeChild(child, exprs, bindings));
|
|
107
100
|
} else {
|
|
108
|
-
|
|
109
|
-
const
|
|
101
|
+
// gap
|
|
102
|
+
const expr = exprs.pop();
|
|
103
|
+
const { interpolateArray, interpolateArrayChildren, interpolateString } = bindings;
|
|
110
104
|
|
|
111
105
|
if (pathIsArray) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
106
|
+
set(
|
|
107
|
+
properties_,
|
|
108
|
+
path,
|
|
109
|
+
expression('[...%%interpolateArray%%(%%expr%%)]')({
|
|
110
|
+
interpolateArray,
|
|
111
|
+
expr,
|
|
112
|
+
}).elements[0],
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
children_.push(
|
|
116
|
+
t.spreadElement(
|
|
117
|
+
expression('%%interpolateArrayChildren%%(%%expr%%, %%ref%%, %%sep%%)')({
|
|
118
|
+
interpolateArrayChildren,
|
|
119
|
+
expr,
|
|
120
|
+
ref: expression(`%%t%%.ref\`${printRef(child.value)}\``)({ t: bindings.t }),
|
|
121
|
+
|
|
122
|
+
// Really really awful unsafe-as-heck hack, to be removed ASAP
|
|
123
|
+
// Fixing this requires having interpolation happen during parsing
|
|
124
|
+
// That way the grammar can deal with the separators!
|
|
125
|
+
sep: expression(
|
|
126
|
+
"%%t%%.embedded(%%t%%.t_node(%%l%%.Comment, null, [%%t%%.embedded(%%t%%.t_node('Space', 'Space', [%%t%%.lit(' ')]))]))",
|
|
127
|
+
)({ t: bindings.t, l: bindings.l }),
|
|
128
|
+
}),
|
|
129
|
+
),
|
|
130
|
+
);
|
|
131
|
+
} else if (language === cstml.canonicalURL && type === 'String') {
|
|
132
|
+
set(
|
|
133
|
+
properties_,
|
|
134
|
+
path,
|
|
135
|
+
expression('%%interpolateString%%(%%expr%%)')({
|
|
136
|
+
interpolateString,
|
|
137
|
+
expr,
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
children_.push(generateBabelNodeChild(child, exprs, bindings));
|
|
142
|
+
} else {
|
|
143
|
+
set(properties_, path, expr);
|
|
144
|
+
|
|
145
|
+
children_.push(generateBabelNodeChild(child, exprs, bindings));
|
|
121
146
|
}
|
|
122
147
|
}
|
|
123
|
-
|
|
124
|
-
|
|
148
|
+
} else {
|
|
149
|
+
children_.push(generateBabelNodeChild(child, exprs, bindings));
|
|
125
150
|
}
|
|
126
151
|
}
|
|
127
152
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
153
|
+
const nodeType = getAgastNodeType(flags);
|
|
154
|
+
|
|
155
|
+
const propsAtts =
|
|
156
|
+
nodeType === 's_node' || nodeType === 's_i_node' ? '' : ', %%properties%%, %%attributes%%';
|
|
157
|
+
const propsAttsValue =
|
|
158
|
+
nodeType === 's_node' || nodeType === 's_i_node'
|
|
159
|
+
? {}
|
|
160
|
+
: {
|
|
161
|
+
properties: t.objectExpression(
|
|
162
|
+
Object.entries(properties_).map(([key, value]) =>
|
|
163
|
+
t.objectProperty(
|
|
164
|
+
t.identifier(key),
|
|
165
|
+
isArray(value) ? t.arrayExpression(value) : value,
|
|
166
|
+
),
|
|
167
|
+
),
|
|
168
|
+
),
|
|
169
|
+
attributes: t.objectExpression(
|
|
170
|
+
Object.entries(attributes).map(([key, value]) =>
|
|
171
|
+
t.objectProperty(t.identifier(key), getBabelASTValue(value, exprs, bindings)),
|
|
172
|
+
),
|
|
173
|
+
),
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return expression(`%%t%%.%%nodeType%%(%%l%%.%%language%%, %%type%%, %%children%%${propsAtts})`)({
|
|
131
177
|
t: bindings.t,
|
|
132
|
-
|
|
178
|
+
l: bindings.l,
|
|
179
|
+
language: t.identifier(namesFor[language]),
|
|
180
|
+
nodeType: t.identifier(nodeType),
|
|
133
181
|
type: t.stringLiteral(type),
|
|
134
|
-
children:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
),
|
|
140
|
-
attributes: t.objectExpression(
|
|
141
|
-
Object.entries(attributes).map(([key, value]) =>
|
|
142
|
-
t.objectProperty(t.identifier(key), getASTValue(value, exprs, bindings)),
|
|
143
|
-
),
|
|
144
|
-
),
|
|
182
|
+
children:
|
|
183
|
+
nodeType === 's_node' || nodeType === 's_i_node'
|
|
184
|
+
? t.stringLiteral(children[0].value)
|
|
185
|
+
: t.arrayExpression(children_),
|
|
186
|
+
...propsAttsValue,
|
|
145
187
|
});
|
|
146
188
|
};
|
|
147
189
|
|
|
190
|
+
const getTopScope = (scope) => {
|
|
191
|
+
let top = scope;
|
|
192
|
+
while (top.parent) top = top.parent;
|
|
193
|
+
return top;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const namesFor = Object.fromEntries([
|
|
197
|
+
...[i, re, spam, cstml].map((l) => [l.canonicalURL, l.name]),
|
|
198
|
+
['https://bablr.org/languages/core/space-tab-newline', 'Space'],
|
|
199
|
+
]);
|
|
200
|
+
|
|
148
201
|
const languages = {
|
|
149
|
-
i,
|
|
150
|
-
re,
|
|
151
|
-
spam,
|
|
152
|
-
str,
|
|
153
|
-
num,
|
|
154
|
-
cst: cstml,
|
|
202
|
+
i: '@bablr/language-bablr-vm-instruction',
|
|
203
|
+
re: '@bablr/language-regex-vm-pattern',
|
|
204
|
+
spam: '@bablr/language-spamex',
|
|
205
|
+
str: '@bablr/language-cstml',
|
|
206
|
+
num: '@bablr/language-cstml',
|
|
207
|
+
cst: '@bablr/language-cstml',
|
|
155
208
|
};
|
|
156
209
|
|
|
157
210
|
const topTypes = {
|
|
@@ -159,14 +212,17 @@ const topTypes = {
|
|
|
159
212
|
re: 'Pattern',
|
|
160
213
|
spam: 'Matcher',
|
|
161
214
|
str: 'String',
|
|
162
|
-
num: '
|
|
215
|
+
num: 'Integer',
|
|
163
216
|
cst: 'Fragment',
|
|
164
217
|
};
|
|
165
218
|
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
219
|
+
const miniLanguages = {
|
|
220
|
+
i,
|
|
221
|
+
re,
|
|
222
|
+
spam,
|
|
223
|
+
str: cstml,
|
|
224
|
+
num: cstml,
|
|
225
|
+
cst: cstml,
|
|
170
226
|
};
|
|
171
227
|
|
|
172
228
|
const shorthandMacro = ({ references }) => {
|
|
@@ -183,16 +239,30 @@ const shorthandMacro = ({ references }) => {
|
|
|
183
239
|
references.cst,
|
|
184
240
|
)) {
|
|
185
241
|
if (!bindings.t) {
|
|
186
|
-
bindings.t = addNamespace(getTopScope(ref.scope).path, '@bablr/
|
|
242
|
+
bindings.t = addNamespace(getTopScope(ref.scope).path, '@bablr/agast-helpers/shorthand', {
|
|
187
243
|
nameHint: 't',
|
|
188
244
|
});
|
|
189
245
|
}
|
|
190
246
|
|
|
247
|
+
if (!bindings.l) {
|
|
248
|
+
bindings.l = addNamespace(getTopScope(ref.scope).path, '@bablr/agast-vm-helpers/languages', {
|
|
249
|
+
nameHint: 'l',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
191
253
|
if (!bindings.interpolateArray) {
|
|
192
254
|
bindings.interpolateArray = addNamed(
|
|
193
255
|
getTopScope(ref.scope).path,
|
|
194
256
|
'interpolateArray',
|
|
195
|
-
'@bablr/
|
|
257
|
+
'@bablr/agast-helpers/template',
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!bindings.interpolateArrayChildren) {
|
|
262
|
+
bindings.interpolateArrayChildren = addNamed(
|
|
263
|
+
getTopScope(ref.scope).path,
|
|
264
|
+
'interpolateArrayChildren',
|
|
265
|
+
'@bablr/agast-helpers/template',
|
|
196
266
|
);
|
|
197
267
|
}
|
|
198
268
|
|
|
@@ -200,7 +270,7 @@ const shorthandMacro = ({ references }) => {
|
|
|
200
270
|
bindings.interpolateString = addNamed(
|
|
201
271
|
getTopScope(ref.scope).path,
|
|
202
272
|
'interpolateString',
|
|
203
|
-
'@bablr/
|
|
273
|
+
'@bablr/agast-helpers/template',
|
|
204
274
|
);
|
|
205
275
|
}
|
|
206
276
|
|
|
@@ -216,15 +286,55 @@ const shorthandMacro = ({ references }) => {
|
|
|
216
286
|
? ref.parentPath.node.property.name
|
|
217
287
|
: topTypes[tagName];
|
|
218
288
|
|
|
219
|
-
|
|
289
|
+
const streamText = quasis
|
|
290
|
+
.map((q) => `"${q.value.raw.replace(/[\\"]/g, '\\$&')}"`)
|
|
291
|
+
.join(' <//> ');
|
|
292
|
+
|
|
293
|
+
// console.log(streamText);
|
|
294
|
+
|
|
295
|
+
const miniLanguage = miniLanguages[tagName];
|
|
220
296
|
|
|
221
297
|
const ast = new TemplateParser(
|
|
222
|
-
|
|
298
|
+
miniLanguage,
|
|
223
299
|
quasis.map((q) => q.value.raw),
|
|
224
300
|
expressions.map(() => null),
|
|
225
|
-
).eval({ language:
|
|
301
|
+
).eval({ language: miniLanguage.name, type });
|
|
302
|
+
|
|
303
|
+
const agAST = getAgASTValue(miniLanguage, ast);
|
|
304
|
+
let referenceDocument = null;
|
|
305
|
+
|
|
306
|
+
const document = printPrettyCSTML(agAST);
|
|
307
|
+
|
|
308
|
+
// try {
|
|
309
|
+
// const documentResult = spawnSync(
|
|
310
|
+
// '../bablr-cli/bin/index.js',
|
|
311
|
+
// ['-l', language, '-p', type, '-f'],
|
|
312
|
+
// {
|
|
313
|
+
// input: streamText,
|
|
314
|
+
// encoding: 'utf8',
|
|
315
|
+
// },
|
|
316
|
+
// );
|
|
317
|
+
|
|
318
|
+
// if (documentResult.status > 0) {
|
|
319
|
+
// throw new Error('bablr CLI parse return non-zero exit');
|
|
320
|
+
// }
|
|
321
|
+
|
|
322
|
+
// if (documentResult.error) {
|
|
323
|
+
// throw new Error(documentResult.error);
|
|
324
|
+
// }
|
|
325
|
+
|
|
326
|
+
// referenceDocument = documentResult.stdout.slice(0, -1);
|
|
327
|
+
|
|
328
|
+
// if (!referenceDocument.length) {
|
|
329
|
+
// throw new Error();
|
|
330
|
+
// }
|
|
331
|
+
|
|
332
|
+
// // secondaryAst = parse(cstml, 'Document', document);
|
|
333
|
+
// } catch (e) {
|
|
334
|
+
// console.warn(' parse failure');
|
|
335
|
+
// }
|
|
226
336
|
|
|
227
|
-
taggedTemplate.replaceWith(
|
|
337
|
+
taggedTemplate.replaceWith(generateBabelNode(agAST, expressions.reverse(), bindings));
|
|
228
338
|
}
|
|
229
339
|
};
|
|
230
340
|
|
package/lib/languages/number.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const { buildCovers } = require('../utils.js');
|
|
2
|
-
const sym = require('../symbols.js');
|
|
3
|
-
|
|
4
|
-
const name = 'Number';
|
|
5
|
-
|
|
6
|
-
const dependencies = {};
|
|
7
|
-
|
|
8
|
-
const covers = buildCovers({
|
|
9
|
-
[sym.node]: ['Number', 'Digit'],
|
|
10
|
-
Number: ['LiteralNumber', 'SymbolicNumber'],
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const grammar = class NumberMiniparserGrammar {
|
|
14
|
-
// @Node
|
|
15
|
-
LiteralNumber(p) {
|
|
16
|
-
p.eatMatch('-', 'Punctuator', { path: 'negative' });
|
|
17
|
-
p.eatProduction('Digits', { path: 'digits[]' });
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// @Node
|
|
21
|
-
SymbolicNumber(p) {
|
|
22
|
-
p.eatMatch('-', 'Punctuator', { path: 'negative' });
|
|
23
|
-
p.eat('Infinity', 'Keyword', { path: 'value' });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
Digits(p) {
|
|
27
|
-
while (p.match(/\d/y)) {
|
|
28
|
-
p.eatProduction('Digit');
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// @Node
|
|
33
|
-
Digit(p) {
|
|
34
|
-
p.eatLiteral(/\d/y);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
module.exports = { name, dependencies, covers, grammar };
|
package/lib/languages/string.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
const objectEntries = require('iter-tools-es/methods/object-entries');
|
|
2
|
-
const { buildCovers } = require('../utils.js');
|
|
3
|
-
const sym = require('../symbols.js');
|
|
4
|
-
|
|
5
|
-
const name = 'String';
|
|
6
|
-
|
|
7
|
-
const dependencies = {};
|
|
8
|
-
|
|
9
|
-
const covers = buildCovers({
|
|
10
|
-
[sym.node]: ['String', 'Content'],
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const PN = 'Punctuator';
|
|
14
|
-
|
|
15
|
-
const escapables = new Map(
|
|
16
|
-
objectEntries({
|
|
17
|
-
n: '\n',
|
|
18
|
-
r: '\r',
|
|
19
|
-
t: '\t',
|
|
20
|
-
0: '\0',
|
|
21
|
-
}),
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const cookEscape = (escape, span) => {
|
|
25
|
-
let hexMatch;
|
|
26
|
-
|
|
27
|
-
if (!escape.startsWith('\\')) {
|
|
28
|
-
throw new Error('string escape must start with \\');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if ((hexMatch = /\\x([0-9a-f]{2})/iy.exec(escape))) {
|
|
32
|
-
//continue
|
|
33
|
-
} else if ((hexMatch = /\\u([0-9a-f]{4})/iy.exec(escape))) {
|
|
34
|
-
//continue
|
|
35
|
-
} else if ((hexMatch = /\\u{([0-9a-f]+)}/iy.exec(escape))) {
|
|
36
|
-
//continue
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (hexMatch) {
|
|
40
|
-
return String.fromCodePoint(parseInt(hexMatch[1], 16));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const litPattern = span === 'Single' ? /\\([\\nrt0'])/y : /\\([\\nrt0"])/y;
|
|
44
|
-
const litMatch = litPattern.exec(escape);
|
|
45
|
-
|
|
46
|
-
if (litMatch) {
|
|
47
|
-
return escapables.get(litMatch[1]) || litMatch[1];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
throw new Error('unable to cook string escape');
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const grammar = class StringMiniparserGrammar {
|
|
54
|
-
// @Node
|
|
55
|
-
String(p) {
|
|
56
|
-
const q = p.match(/['"]/y) || '"';
|
|
57
|
-
|
|
58
|
-
const span = q === '"' ? 'Double' : 'Single';
|
|
59
|
-
|
|
60
|
-
p.eat(q, PN, { path: 'open', startSpan: span, balanced: q });
|
|
61
|
-
while (p.match(/./sy) || p.atExpression) {
|
|
62
|
-
p.eatProduction('Content', { path: 'content' });
|
|
63
|
-
}
|
|
64
|
-
p.eat(q, PN, { path: 'close', endSpan: span, balancer: true });
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// @Node
|
|
68
|
-
Content(p) {
|
|
69
|
-
let esc, lit;
|
|
70
|
-
let i = 0;
|
|
71
|
-
do {
|
|
72
|
-
esc =
|
|
73
|
-
p.span.type === 'Single'
|
|
74
|
-
? p.eatMatchEscape(/\\(u(\{\d{1,6}\}|\d{4})|x[0-9a-fA-F]{2}|[\\nrt0'])/y)
|
|
75
|
-
: p.eatMatchEscape(/\\(u(\{\d{1,6}\}|\d{4})|x[0-9a-fA-F]{2}|[\\nrt0"])/y);
|
|
76
|
-
lit =
|
|
77
|
-
p.span.type === 'Single'
|
|
78
|
-
? p.eatMatchLiteral(/[^\r\n\0\\']+/y)
|
|
79
|
-
: p.eatMatchLiteral(/[^\r\n\0\\"]+/y);
|
|
80
|
-
i++;
|
|
81
|
-
} while (esc || lit);
|
|
82
|
-
if (i === 1 && !esc && !lit) {
|
|
83
|
-
throw new Error('Invalid string content');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
module.exports = { name, dependencies, covers, grammar, escapables, cookEscape };
|