@bablr/boot 0.1.9 → 0.2.1
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 -90
- package/lib/languages/instruction.js +10 -5
- package/lib/languages/regex.js +124 -36
- package/lib/languages/spamex.js +30 -59
- 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 +200 -104
- package/lib/languages/number.js +0 -38
- package/lib/languages/string.js +0 -86
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,53 +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
|
|
67
|
-
if (esc === '\r') {
|
|
68
|
-
return '\\r';
|
|
69
|
-
} else if (esc === '\n') {
|
|
70
|
-
return '\\n';
|
|
71
|
-
} else if (esc === '\0') {
|
|
72
|
-
return '\\0';
|
|
73
|
-
} else {
|
|
74
|
-
return `\\${esc}`;
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
const printTemplateString = (str) => {
|
|
78
|
-
return `\`${str.replace(/[`\\\0\r\n]/g, escapeReplacer)}\``;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const generateNodeChild = (child, bindings) => {
|
|
48
|
+
const generateBabelNodeChild = (child, exprs, bindings) => {
|
|
82
49
|
if (child.type === 'Reference') {
|
|
83
|
-
|
|
84
|
-
const printedRef = pathIsArray ? `${pathName}[]` : pathName;
|
|
85
|
-
return expression(`%%t%%.ref\`${printedRef}\``)({ t: bindings.t });
|
|
50
|
+
return expression(`%%t%%.ref\`${printRef(child.value)}\``)({ t: bindings.t });
|
|
86
51
|
} else if (child.type === 'Literal') {
|
|
87
|
-
return expression(`%%t%%.lit
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return expression(`%%t%%.
|
|
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%%)`)({
|
|
93
58
|
t: bindings.t,
|
|
59
|
+
value: generateBabelNode(child.value, exprs, bindings),
|
|
94
60
|
});
|
|
95
61
|
} else {
|
|
96
62
|
throw new Error(`Unknown child type ${child.type}`);
|
|
97
63
|
}
|
|
98
64
|
};
|
|
99
65
|
|
|
100
|
-
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) => {
|
|
101
81
|
const resolver = new PathResolver(node);
|
|
102
|
-
const { children, type, language, attributes } = node;
|
|
82
|
+
const { flags = {}, children, type, language, attributes } = node;
|
|
83
|
+
|
|
103
84
|
const properties_ = {};
|
|
104
85
|
const children_ = [];
|
|
105
86
|
|
|
@@ -108,64 +89,122 @@ const generateNode = (node, exprs, bindings) => {
|
|
|
108
89
|
}
|
|
109
90
|
|
|
110
91
|
for (const child of children) {
|
|
111
|
-
children_.push(generateNodeChild(child, bindings));
|
|
112
|
-
|
|
113
92
|
if (child.type === 'Reference') {
|
|
114
93
|
const path = child.value;
|
|
115
|
-
const { pathIsArray } = path;
|
|
94
|
+
const { isArray: pathIsArray } = path;
|
|
116
95
|
const resolved = resolver.get(path);
|
|
117
96
|
|
|
118
|
-
let embedded = resolved;
|
|
119
97
|
if (resolved) {
|
|
120
|
-
|
|
98
|
+
set(properties_, path, generateBabelNode(resolved, exprs, bindings));
|
|
99
|
+
children_.push(generateBabelNodeChild(child, exprs, bindings));
|
|
121
100
|
} else {
|
|
122
|
-
|
|
123
|
-
const
|
|
101
|
+
// gap
|
|
102
|
+
const expr = exprs.pop();
|
|
103
|
+
const { interpolateArray, interpolateArrayChildren, interpolateString } = bindings;
|
|
124
104
|
|
|
125
105
|
if (pathIsArray) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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));
|
|
135
146
|
}
|
|
136
147
|
}
|
|
137
|
-
|
|
138
|
-
|
|
148
|
+
} else {
|
|
149
|
+
children_.push(generateBabelNodeChild(child, exprs, bindings));
|
|
139
150
|
}
|
|
140
151
|
}
|
|
141
152
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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})`)({
|
|
145
177
|
t: bindings.t,
|
|
146
|
-
|
|
178
|
+
l: bindings.l,
|
|
179
|
+
language: t.identifier(namesFor[language]),
|
|
180
|
+
nodeType: t.identifier(nodeType),
|
|
147
181
|
type: t.stringLiteral(type),
|
|
148
|
-
children:
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
),
|
|
154
|
-
attributes: t.objectExpression(
|
|
155
|
-
Object.entries(attributes).map(([key, value]) =>
|
|
156
|
-
t.objectProperty(t.identifier(key), getASTValue(value, exprs, bindings)),
|
|
157
|
-
),
|
|
158
|
-
),
|
|
182
|
+
children:
|
|
183
|
+
nodeType === 's_node' || nodeType === 's_i_node'
|
|
184
|
+
? t.stringLiteral(children[0].value)
|
|
185
|
+
: t.arrayExpression(children_),
|
|
186
|
+
...propsAttsValue,
|
|
159
187
|
});
|
|
160
188
|
};
|
|
161
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
|
+
|
|
162
201
|
const languages = {
|
|
163
|
-
i,
|
|
164
|
-
re,
|
|
165
|
-
spam,
|
|
166
|
-
str,
|
|
167
|
-
num,
|
|
168
|
-
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',
|
|
169
208
|
};
|
|
170
209
|
|
|
171
210
|
const topTypes = {
|
|
@@ -177,10 +216,13 @@ const topTypes = {
|
|
|
177
216
|
cst: 'Fragment',
|
|
178
217
|
};
|
|
179
218
|
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
219
|
+
const miniLanguages = {
|
|
220
|
+
i,
|
|
221
|
+
re,
|
|
222
|
+
spam,
|
|
223
|
+
str: cstml,
|
|
224
|
+
num: cstml,
|
|
225
|
+
cst: cstml,
|
|
184
226
|
};
|
|
185
227
|
|
|
186
228
|
const shorthandMacro = ({ references }) => {
|
|
@@ -197,16 +239,30 @@ const shorthandMacro = ({ references }) => {
|
|
|
197
239
|
references.cst,
|
|
198
240
|
)) {
|
|
199
241
|
if (!bindings.t) {
|
|
200
|
-
bindings.t = addNamespace(getTopScope(ref.scope).path, '@bablr/
|
|
242
|
+
bindings.t = addNamespace(getTopScope(ref.scope).path, '@bablr/agast-helpers/shorthand', {
|
|
201
243
|
nameHint: 't',
|
|
202
244
|
});
|
|
203
245
|
}
|
|
204
246
|
|
|
247
|
+
if (!bindings.l) {
|
|
248
|
+
bindings.l = addNamespace(getTopScope(ref.scope).path, '@bablr/agast-vm-helpers/languages', {
|
|
249
|
+
nameHint: 'l',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
205
253
|
if (!bindings.interpolateArray) {
|
|
206
254
|
bindings.interpolateArray = addNamed(
|
|
207
255
|
getTopScope(ref.scope).path,
|
|
208
256
|
'interpolateArray',
|
|
209
|
-
'@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',
|
|
210
266
|
);
|
|
211
267
|
}
|
|
212
268
|
|
|
@@ -214,7 +270,7 @@ const shorthandMacro = ({ references }) => {
|
|
|
214
270
|
bindings.interpolateString = addNamed(
|
|
215
271
|
getTopScope(ref.scope).path,
|
|
216
272
|
'interpolateString',
|
|
217
|
-
'@bablr/
|
|
273
|
+
'@bablr/agast-helpers/template',
|
|
218
274
|
);
|
|
219
275
|
}
|
|
220
276
|
|
|
@@ -230,15 +286,55 @@ const shorthandMacro = ({ references }) => {
|
|
|
230
286
|
? ref.parentPath.node.property.name
|
|
231
287
|
: topTypes[tagName];
|
|
232
288
|
|
|
233
|
-
|
|
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];
|
|
234
296
|
|
|
235
297
|
const ast = new TemplateParser(
|
|
236
|
-
|
|
298
|
+
miniLanguage,
|
|
237
299
|
quasis.map((q) => q.value.raw),
|
|
238
300
|
expressions.map(() => null),
|
|
239
|
-
).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
|
+
// }
|
|
240
336
|
|
|
241
|
-
taggedTemplate.replaceWith(
|
|
337
|
+
taggedTemplate.replaceWith(generateBabelNode(agAST, expressions.reverse(), bindings));
|
|
242
338
|
}
|
|
243
339
|
};
|
|
244
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: ['Integer', 'Infinity'],
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const grammar = class NumberMiniparserGrammar {
|
|
14
|
-
// @Node
|
|
15
|
-
Integer(p) {
|
|
16
|
-
p.eatMatch('-', 'Punctuator', { path: 'negative' });
|
|
17
|
-
p.eatProduction('Digits', { path: 'digits[]' });
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// @Node
|
|
21
|
-
Infinity(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,86 +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 = /\\u([0-9a-f]{4})/iy.exec(escape))) {
|
|
32
|
-
//continue
|
|
33
|
-
} else if ((hexMatch = /\\u{([0-9a-f]+)}/iy.exec(escape))) {
|
|
34
|
-
//continue
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (hexMatch) {
|
|
38
|
-
return String.fromCodePoint(parseInt(hexMatch[1], 16));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const litPattern = span === 'Single' ? /\\([\\nrt0'])/y : /\\([\\nrt0"])/y;
|
|
42
|
-
const litMatch = litPattern.exec(escape);
|
|
43
|
-
|
|
44
|
-
if (litMatch) {
|
|
45
|
-
return escapables.get(litMatch[1]) || litMatch[1];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
throw new Error('unable to cook string escape');
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const grammar = class StringMiniparserGrammar {
|
|
52
|
-
// @Node
|
|
53
|
-
String(p) {
|
|
54
|
-
const q = p.match(/['"]/y) || '"';
|
|
55
|
-
|
|
56
|
-
const span = q === '"' ? 'Double' : 'Single';
|
|
57
|
-
|
|
58
|
-
p.eat(q, PN, { path: 'open', startSpan: span, balanced: q });
|
|
59
|
-
while (p.match(/./sy) || p.atExpression) {
|
|
60
|
-
p.eatProduction('Content', { path: 'content' });
|
|
61
|
-
}
|
|
62
|
-
p.eat(q, PN, { path: 'close', endSpan: span, balancer: true });
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// @Node
|
|
66
|
-
Content(p) {
|
|
67
|
-
let esc, lit;
|
|
68
|
-
let i = 0;
|
|
69
|
-
do {
|
|
70
|
-
esc =
|
|
71
|
-
p.span.type === 'Single'
|
|
72
|
-
? p.eatMatchEscape(/\\(u(\{\d{1,6}\}|\d{4})|[\\nrt0'])/y)
|
|
73
|
-
: p.eatMatchEscape(/\\(u(\{\d{1,6}\}|\d{4})|[\\nrt0"])/y);
|
|
74
|
-
lit =
|
|
75
|
-
p.span.type === 'Single'
|
|
76
|
-
? p.eatMatchLiteral(/[^\r\n\0\\']+/y)
|
|
77
|
-
: p.eatMatchLiteral(/[^\r\n\0\\"]+/y);
|
|
78
|
-
i++;
|
|
79
|
-
} while (esc || lit);
|
|
80
|
-
if (i === 1 && !esc && !lit) {
|
|
81
|
-
throw new Error('Invalid string content');
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
module.exports = { name, dependencies, covers, grammar, escapables, cookEscape };
|