@intlify/message-compiler 11.1.2 → 12.0.0-alpha.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/LICENSE +1 -1
- package/dist/message-compiler.d.ts +6 -2
- package/dist/message-compiler.esm-browser.js +549 -547
- package/dist/message-compiler.esm-browser.prod.js +3 -3
- package/dist/message-compiler.esm-bundler.js +1 -1
- package/dist/message-compiler.global.js +551 -547
- package/dist/message-compiler.global.prod.js +3 -3
- package/dist/{message-compiler.mjs → message-compiler.js} +397 -395
- package/dist/{message-compiler.node.mjs → message-compiler.node.js} +1186 -1184
- package/package.json +7 -15
- package/dist/message-compiler.cjs +0 -1587
- package/dist/message-compiler.cjs.js +0 -1
- package/dist/message-compiler.cjs.prod.js +0 -1
- package/dist/message-compiler.prod.cjs +0 -1570
- package/index.js +0 -7
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* message-compiler
|
|
3
|
-
* (c)
|
|
2
|
+
* message-compiler v12.0.0-alpha.1
|
|
3
|
+
* (c) 2016-present kazuya kawaguchi and contributors
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
6
|
-
import { format,
|
|
6
|
+
import { format, isString, join, assign } from '@intlify/shared';
|
|
7
7
|
import { SourceMapGenerator } from 'source-map-js';
|
|
8
8
|
|
|
9
9
|
const LOCATION_STUB = {
|
|
@@ -88,171 +88,536 @@ function defaultOnError(error) {
|
|
|
88
88
|
throw error;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
// eslint-disable-next-line
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const isLineEnd = (index) => isCRLF(index) || isLF(index) || isPS(index) || isLS(index);
|
|
111
|
-
const index = () => _index;
|
|
112
|
-
const line = () => _line;
|
|
113
|
-
const column = () => _column;
|
|
114
|
-
const peekOffset = () => _peekOffset;
|
|
115
|
-
const charAt = (offset) => isCRLF(offset) || isPS(offset) || isLS(offset) ? CHAR_LF : _buf[offset];
|
|
116
|
-
const currentChar = () => charAt(_index);
|
|
117
|
-
const currentPeek = () => charAt(_index + _peekOffset);
|
|
118
|
-
function next() {
|
|
119
|
-
_peekOffset = 0;
|
|
120
|
-
if (isLineEnd(_index)) {
|
|
121
|
-
_line++;
|
|
122
|
-
_column = 0;
|
|
123
|
-
}
|
|
124
|
-
if (isCRLF(_index)) {
|
|
125
|
-
_index++;
|
|
126
|
-
}
|
|
127
|
-
_index++;
|
|
128
|
-
_column++;
|
|
129
|
-
return _buf[_index];
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
|
92
|
+
/// <reference types="source-map-js" />
|
|
93
|
+
const ERROR_DOMAIN$3 = 'parser';
|
|
94
|
+
function createCodeGenerator(ast, options) {
|
|
95
|
+
const { sourceMap, filename, breakLineCode, needIndent: _needIndent } = options;
|
|
96
|
+
const location = options.location !== false;
|
|
97
|
+
const _context = {
|
|
98
|
+
filename,
|
|
99
|
+
code: '',
|
|
100
|
+
column: 1,
|
|
101
|
+
line: 1,
|
|
102
|
+
offset: 0,
|
|
103
|
+
map: undefined,
|
|
104
|
+
breakLineCode,
|
|
105
|
+
needIndent: _needIndent,
|
|
106
|
+
indentLevel: 0
|
|
107
|
+
};
|
|
108
|
+
if (location && ast.loc) {
|
|
109
|
+
_context.source = ast.loc.source;
|
|
130
110
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
111
|
+
const context = () => _context;
|
|
112
|
+
function push(code, node) {
|
|
113
|
+
_context.code += code;
|
|
114
|
+
if (_context.map) {
|
|
115
|
+
if (node && node.loc && node.loc !== LOCATION_STUB) {
|
|
116
|
+
addMapping(node.loc.start, getMappingName(node));
|
|
117
|
+
}
|
|
118
|
+
advancePositionWithSource(_context, code);
|
|
134
119
|
}
|
|
135
|
-
_peekOffset++;
|
|
136
|
-
return _buf[_index + _peekOffset];
|
|
137
120
|
}
|
|
138
|
-
function
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
_column = 1;
|
|
142
|
-
_peekOffset = 0;
|
|
121
|
+
function _newline(n, withBreakLine = true) {
|
|
122
|
+
const _breakLineCode = withBreakLine ? breakLineCode : '';
|
|
123
|
+
push(_needIndent ? _breakLineCode + ` `.repeat(n) : _breakLineCode);
|
|
143
124
|
}
|
|
144
|
-
function
|
|
145
|
-
|
|
125
|
+
function indent(withNewLine = true) {
|
|
126
|
+
const level = ++_context.indentLevel;
|
|
127
|
+
withNewLine && _newline(level);
|
|
146
128
|
}
|
|
147
|
-
function
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
129
|
+
function deindent(withNewLine = true) {
|
|
130
|
+
const level = --_context.indentLevel;
|
|
131
|
+
withNewLine && _newline(level);
|
|
132
|
+
}
|
|
133
|
+
function newline() {
|
|
134
|
+
_newline(_context.indentLevel);
|
|
135
|
+
}
|
|
136
|
+
const helper = (key) => `_${key}`;
|
|
137
|
+
const needIndent = () => _context.needIndent;
|
|
138
|
+
function addMapping(loc, name) {
|
|
139
|
+
_context.map.addMapping({
|
|
140
|
+
name,
|
|
141
|
+
source: _context.filename,
|
|
142
|
+
original: {
|
|
143
|
+
line: loc.line,
|
|
144
|
+
column: loc.column - 1
|
|
145
|
+
},
|
|
146
|
+
generated: {
|
|
147
|
+
line: _context.line,
|
|
148
|
+
column: _context.column - 1
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if (location && sourceMap) {
|
|
153
|
+
_context.map = new SourceMapGenerator();
|
|
154
|
+
_context.map.setSourceContent(filename, _context.source);
|
|
153
155
|
}
|
|
154
156
|
return {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
next,
|
|
163
|
-
peek,
|
|
164
|
-
reset,
|
|
165
|
-
resetPeek,
|
|
166
|
-
skipToPeek
|
|
157
|
+
context,
|
|
158
|
+
push,
|
|
159
|
+
indent,
|
|
160
|
+
deindent,
|
|
161
|
+
newline,
|
|
162
|
+
helper,
|
|
163
|
+
needIndent
|
|
167
164
|
};
|
|
168
165
|
}
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const currentOffset = () => _scnr.index();
|
|
178
|
-
const currentPosition = () => createPosition(_scnr.line(), _scnr.column(), _scnr.index());
|
|
179
|
-
const _initLoc = currentPosition();
|
|
180
|
-
const _initOffset = currentOffset();
|
|
181
|
-
const _context = {
|
|
182
|
-
currentType: 13 /* TokenTypes.EOF */,
|
|
183
|
-
offset: _initOffset,
|
|
184
|
-
startLoc: _initLoc,
|
|
185
|
-
endLoc: _initLoc,
|
|
186
|
-
lastType: 13 /* TokenTypes.EOF */,
|
|
187
|
-
lastOffset: _initOffset,
|
|
188
|
-
lastStartLoc: _initLoc,
|
|
189
|
-
lastEndLoc: _initLoc,
|
|
190
|
-
braceNest: 0,
|
|
191
|
-
inLinked: false,
|
|
192
|
-
text: ''
|
|
193
|
-
};
|
|
194
|
-
const context = () => _context;
|
|
195
|
-
const { onError } = options;
|
|
196
|
-
function emitError(code, pos, offset, ...args) {
|
|
197
|
-
const ctx = context();
|
|
198
|
-
pos.column += offset;
|
|
199
|
-
pos.offset += offset;
|
|
200
|
-
if (onError) {
|
|
201
|
-
const loc = location ? createLocation(ctx.startLoc, pos) : null;
|
|
202
|
-
const err = createCompileError(code, loc, {
|
|
203
|
-
domain: ERROR_DOMAIN$3,
|
|
204
|
-
args
|
|
205
|
-
});
|
|
206
|
-
onError(err);
|
|
207
|
-
}
|
|
166
|
+
function generateLinkedNode(generator, node) {
|
|
167
|
+
const { helper } = generator;
|
|
168
|
+
generator.push(`${helper("linked" /* HelperNameMap.LINKED */)}(`);
|
|
169
|
+
generateNode(generator, node.key);
|
|
170
|
+
if (node.modifier) {
|
|
171
|
+
generator.push(`, `);
|
|
172
|
+
generateNode(generator, node.modifier);
|
|
173
|
+
generator.push(`, _type`);
|
|
208
174
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
context.currentType = type;
|
|
212
|
-
const token = { type };
|
|
213
|
-
if (location) {
|
|
214
|
-
token.loc = createLocation(context.startLoc, context.endLoc);
|
|
215
|
-
}
|
|
216
|
-
if (value != null) {
|
|
217
|
-
token.value = value;
|
|
218
|
-
}
|
|
219
|
-
return token;
|
|
175
|
+
else {
|
|
176
|
+
generator.push(`, undefined, _type`);
|
|
220
177
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
178
|
+
generator.push(`)`);
|
|
179
|
+
}
|
|
180
|
+
function generateMessageNode(generator, node) {
|
|
181
|
+
const { helper, needIndent } = generator;
|
|
182
|
+
generator.push(`${helper("normalize" /* HelperNameMap.NORMALIZE */)}([`);
|
|
183
|
+
generator.indent(needIndent());
|
|
184
|
+
const length = node.items.length;
|
|
185
|
+
for (let i = 0; i < length; i++) {
|
|
186
|
+
generateNode(generator, node.items[i]);
|
|
187
|
+
if (i === length - 1) {
|
|
188
|
+
break;
|
|
230
189
|
}
|
|
190
|
+
generator.push(', ');
|
|
231
191
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
192
|
+
generator.deindent(needIndent());
|
|
193
|
+
generator.push('])');
|
|
194
|
+
}
|
|
195
|
+
function generatePluralNode(generator, node) {
|
|
196
|
+
const { helper, needIndent } = generator;
|
|
197
|
+
if (node.cases.length > 1) {
|
|
198
|
+
generator.push(`${helper("plural" /* HelperNameMap.PLURAL */)}([`);
|
|
199
|
+
generator.indent(needIndent());
|
|
200
|
+
const length = node.cases.length;
|
|
201
|
+
for (let i = 0; i < length; i++) {
|
|
202
|
+
generateNode(generator, node.cases[i]);
|
|
203
|
+
if (i === length - 1) {
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
generator.push(', ');
|
|
237
207
|
}
|
|
238
|
-
|
|
208
|
+
generator.deindent(needIndent());
|
|
209
|
+
generator.push(`])`);
|
|
239
210
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
211
|
+
}
|
|
212
|
+
function generateResource(generator, node) {
|
|
213
|
+
if (node.body) {
|
|
214
|
+
generateNode(generator, node.body);
|
|
244
215
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
return false;
|
|
248
|
-
}
|
|
249
|
-
const cc = ch.charCodeAt(0);
|
|
250
|
-
return ((cc >= 97 && cc <= 122) || // a-z
|
|
251
|
-
(cc >= 65 && cc <= 90) || // A-Z
|
|
252
|
-
cc === 95 // _
|
|
253
|
-
);
|
|
216
|
+
else {
|
|
217
|
+
generator.push('null');
|
|
254
218
|
}
|
|
255
|
-
|
|
219
|
+
}
|
|
220
|
+
function generateNode(generator, node) {
|
|
221
|
+
const { helper } = generator;
|
|
222
|
+
switch (node.type) {
|
|
223
|
+
case 0 /* NodeTypes.Resource */:
|
|
224
|
+
generateResource(generator, node);
|
|
225
|
+
break;
|
|
226
|
+
case 1 /* NodeTypes.Plural */:
|
|
227
|
+
generatePluralNode(generator, node);
|
|
228
|
+
break;
|
|
229
|
+
case 2 /* NodeTypes.Message */:
|
|
230
|
+
generateMessageNode(generator, node);
|
|
231
|
+
break;
|
|
232
|
+
case 6 /* NodeTypes.Linked */:
|
|
233
|
+
generateLinkedNode(generator, node);
|
|
234
|
+
break;
|
|
235
|
+
case 8 /* NodeTypes.LinkedModifier */:
|
|
236
|
+
generator.push(JSON.stringify(node.value), node);
|
|
237
|
+
break;
|
|
238
|
+
case 7 /* NodeTypes.LinkedKey */:
|
|
239
|
+
generator.push(JSON.stringify(node.value), node);
|
|
240
|
+
break;
|
|
241
|
+
case 5 /* NodeTypes.List */:
|
|
242
|
+
generator.push(`${helper("interpolate" /* HelperNameMap.INTERPOLATE */)}(${helper("list" /* HelperNameMap.LIST */)}(${node.index}))`, node);
|
|
243
|
+
break;
|
|
244
|
+
case 4 /* NodeTypes.Named */:
|
|
245
|
+
generator.push(`${helper("interpolate" /* HelperNameMap.INTERPOLATE */)}(${helper("named" /* HelperNameMap.NAMED */)}(${JSON.stringify(node.key)}))`, node);
|
|
246
|
+
break;
|
|
247
|
+
case 9 /* NodeTypes.Literal */:
|
|
248
|
+
generator.push(JSON.stringify(node.value), node);
|
|
249
|
+
break;
|
|
250
|
+
case 3 /* NodeTypes.Text */:
|
|
251
|
+
generator.push(JSON.stringify(node.value), node);
|
|
252
|
+
break;
|
|
253
|
+
default:
|
|
254
|
+
if ((process.env.NODE_ENV !== 'production')) {
|
|
255
|
+
throw createCompileError(CompileErrorCodes.UNHANDLED_CODEGEN_NODE_TYPE, null, {
|
|
256
|
+
domain: ERROR_DOMAIN$3,
|
|
257
|
+
args: [node.type]
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// generate code from AST
|
|
263
|
+
const generate = (ast, options = {}) => {
|
|
264
|
+
const mode = isString(options.mode) ? options.mode : 'normal';
|
|
265
|
+
const filename = isString(options.filename)
|
|
266
|
+
? options.filename
|
|
267
|
+
: 'message.intl';
|
|
268
|
+
const sourceMap = !!options.sourceMap;
|
|
269
|
+
// prettier-ignore
|
|
270
|
+
const breakLineCode = options.breakLineCode != null
|
|
271
|
+
? options.breakLineCode
|
|
272
|
+
: mode === 'arrow'
|
|
273
|
+
? ';'
|
|
274
|
+
: '\n';
|
|
275
|
+
const needIndent = options.needIndent ? options.needIndent : mode !== 'arrow';
|
|
276
|
+
const helpers = ast.helpers || [];
|
|
277
|
+
const generator = createCodeGenerator(ast, {
|
|
278
|
+
mode,
|
|
279
|
+
filename,
|
|
280
|
+
sourceMap,
|
|
281
|
+
breakLineCode,
|
|
282
|
+
needIndent
|
|
283
|
+
});
|
|
284
|
+
generator.push(mode === 'normal' ? `function __msg__ (ctx) {` : `(ctx) => {`);
|
|
285
|
+
generator.indent(needIndent);
|
|
286
|
+
if (helpers.length > 0) {
|
|
287
|
+
generator.push(`const { ${join(helpers.map(s => `${s}: _${s}`), ', ')} } = ctx`);
|
|
288
|
+
generator.newline();
|
|
289
|
+
}
|
|
290
|
+
generator.push(`return `);
|
|
291
|
+
generateNode(generator, ast);
|
|
292
|
+
generator.deindent(needIndent);
|
|
293
|
+
generator.push(`}`);
|
|
294
|
+
delete ast.helpers;
|
|
295
|
+
const { code, map } = generator.context();
|
|
296
|
+
return {
|
|
297
|
+
ast,
|
|
298
|
+
code,
|
|
299
|
+
map: map ? map.toJSON() : undefined // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
300
|
+
};
|
|
301
|
+
};
|
|
302
|
+
function getMappingName(node) {
|
|
303
|
+
switch (node.type) {
|
|
304
|
+
case 3 /* NodeTypes.Text */:
|
|
305
|
+
case 9 /* NodeTypes.Literal */:
|
|
306
|
+
case 8 /* NodeTypes.LinkedModifier */:
|
|
307
|
+
case 7 /* NodeTypes.LinkedKey */:
|
|
308
|
+
return node.value;
|
|
309
|
+
case 5 /* NodeTypes.List */:
|
|
310
|
+
return node.index.toString();
|
|
311
|
+
case 4 /* NodeTypes.Named */:
|
|
312
|
+
return node.key;
|
|
313
|
+
default:
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function advancePositionWithSource(pos, source, numberOfCharacters = source.length) {
|
|
318
|
+
let linesCount = 0;
|
|
319
|
+
let lastNewLinePos = -1;
|
|
320
|
+
for (let i = 0; i < numberOfCharacters; i++) {
|
|
321
|
+
if (source.charCodeAt(i) === 10 /* newline char code */) {
|
|
322
|
+
linesCount++;
|
|
323
|
+
lastNewLinePos = i;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
pos.offset += numberOfCharacters;
|
|
327
|
+
pos.line += linesCount;
|
|
328
|
+
pos.column =
|
|
329
|
+
lastNewLinePos === -1
|
|
330
|
+
? pos.column + numberOfCharacters
|
|
331
|
+
: numberOfCharacters - lastNewLinePos;
|
|
332
|
+
return pos;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const ERROR_DOMAIN$2 = 'minifier';
|
|
336
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
337
|
+
function mangle(node) {
|
|
338
|
+
node.t = node.type;
|
|
339
|
+
switch (node.type) {
|
|
340
|
+
case 0 /* NodeTypes.Resource */: {
|
|
341
|
+
const resource = node;
|
|
342
|
+
mangle(resource.body);
|
|
343
|
+
resource.b = resource.body;
|
|
344
|
+
delete resource.body;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
case 1 /* NodeTypes.Plural */: {
|
|
348
|
+
const plural = node;
|
|
349
|
+
const cases = plural.cases;
|
|
350
|
+
for (let i = 0; i < cases.length; i++) {
|
|
351
|
+
mangle(cases[i]);
|
|
352
|
+
}
|
|
353
|
+
plural.c = cases;
|
|
354
|
+
delete plural.cases;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
case 2 /* NodeTypes.Message */: {
|
|
358
|
+
const message = node;
|
|
359
|
+
const items = message.items;
|
|
360
|
+
for (let i = 0; i < items.length; i++) {
|
|
361
|
+
mangle(items[i]);
|
|
362
|
+
}
|
|
363
|
+
message.i = items;
|
|
364
|
+
delete message.items;
|
|
365
|
+
if (message.static) {
|
|
366
|
+
message.s = message.static;
|
|
367
|
+
delete message.static;
|
|
368
|
+
}
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
case 3 /* NodeTypes.Text */:
|
|
372
|
+
case 9 /* NodeTypes.Literal */:
|
|
373
|
+
case 8 /* NodeTypes.LinkedModifier */:
|
|
374
|
+
case 7 /* NodeTypes.LinkedKey */: {
|
|
375
|
+
const valueNode = node;
|
|
376
|
+
if (valueNode.value) {
|
|
377
|
+
valueNode.v = valueNode.value;
|
|
378
|
+
delete valueNode.value;
|
|
379
|
+
}
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
case 6 /* NodeTypes.Linked */: {
|
|
383
|
+
const linked = node;
|
|
384
|
+
mangle(linked.key);
|
|
385
|
+
linked.k = linked.key;
|
|
386
|
+
delete linked.key;
|
|
387
|
+
if (linked.modifier) {
|
|
388
|
+
mangle(linked.modifier);
|
|
389
|
+
linked.m = linked.modifier;
|
|
390
|
+
delete linked.modifier;
|
|
391
|
+
}
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
case 5 /* NodeTypes.List */: {
|
|
395
|
+
const list = node;
|
|
396
|
+
list.i = list.index;
|
|
397
|
+
delete list.index;
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
case 4 /* NodeTypes.Named */: {
|
|
401
|
+
const named = node;
|
|
402
|
+
named.k = named.key;
|
|
403
|
+
delete named.key;
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
default:
|
|
407
|
+
if ((process.env.NODE_ENV !== 'production')) {
|
|
408
|
+
throw createCompileError(CompileErrorCodes.UNHANDLED_MINIFIER_NODE_TYPE, null, {
|
|
409
|
+
domain: ERROR_DOMAIN$2,
|
|
410
|
+
args: [node.type]
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
delete node.type;
|
|
415
|
+
}
|
|
416
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
417
|
+
|
|
418
|
+
function optimize(ast) {
|
|
419
|
+
const body = ast.body;
|
|
420
|
+
if (body.type === 2 /* NodeTypes.Message */) {
|
|
421
|
+
optimizeMessageNode(body);
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
body.cases.forEach(c => optimizeMessageNode(c));
|
|
425
|
+
}
|
|
426
|
+
return ast;
|
|
427
|
+
}
|
|
428
|
+
function optimizeMessageNode(message) {
|
|
429
|
+
if (message.items.length === 1) {
|
|
430
|
+
const item = message.items[0];
|
|
431
|
+
if (item.type === 3 /* NodeTypes.Text */ || item.type === 9 /* NodeTypes.Literal */) {
|
|
432
|
+
message.static = item.value;
|
|
433
|
+
delete item.value; // optimization for size
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
const values = [];
|
|
438
|
+
for (let i = 0; i < message.items.length; i++) {
|
|
439
|
+
const item = message.items[i];
|
|
440
|
+
if (!(item.type === 3 /* NodeTypes.Text */ || item.type === 9 /* NodeTypes.Literal */)) {
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
if (item.value == null) {
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
values.push(item.value);
|
|
447
|
+
}
|
|
448
|
+
if (values.length === message.items.length) {
|
|
449
|
+
message.static = join(values);
|
|
450
|
+
for (let i = 0; i < message.items.length; i++) {
|
|
451
|
+
const item = message.items[i];
|
|
452
|
+
if (item.type === 3 /* NodeTypes.Text */ || item.type === 9 /* NodeTypes.Literal */) {
|
|
453
|
+
delete item.value; // optimization for size
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const CHAR_SP = ' ';
|
|
461
|
+
const CHAR_CR = '\r';
|
|
462
|
+
const CHAR_LF = '\n';
|
|
463
|
+
const CHAR_LS = String.fromCharCode(0x2028);
|
|
464
|
+
const CHAR_PS = String.fromCharCode(0x2029);
|
|
465
|
+
function createScanner(str) {
|
|
466
|
+
const _buf = str;
|
|
467
|
+
let _index = 0;
|
|
468
|
+
let _line = 1;
|
|
469
|
+
let _column = 1;
|
|
470
|
+
let _peekOffset = 0;
|
|
471
|
+
const isCRLF = (index) => _buf[index] === CHAR_CR && _buf[index + 1] === CHAR_LF;
|
|
472
|
+
const isLF = (index) => _buf[index] === CHAR_LF;
|
|
473
|
+
const isPS = (index) => _buf[index] === CHAR_PS;
|
|
474
|
+
const isLS = (index) => _buf[index] === CHAR_LS;
|
|
475
|
+
const isLineEnd = (index) => isCRLF(index) || isLF(index) || isPS(index) || isLS(index);
|
|
476
|
+
const index = () => _index;
|
|
477
|
+
const line = () => _line;
|
|
478
|
+
const column = () => _column;
|
|
479
|
+
const peekOffset = () => _peekOffset;
|
|
480
|
+
const charAt = (offset) => isCRLF(offset) || isPS(offset) || isLS(offset) ? CHAR_LF : _buf[offset];
|
|
481
|
+
const currentChar = () => charAt(_index);
|
|
482
|
+
const currentPeek = () => charAt(_index + _peekOffset);
|
|
483
|
+
function next() {
|
|
484
|
+
_peekOffset = 0;
|
|
485
|
+
if (isLineEnd(_index)) {
|
|
486
|
+
_line++;
|
|
487
|
+
_column = 0;
|
|
488
|
+
}
|
|
489
|
+
if (isCRLF(_index)) {
|
|
490
|
+
_index++;
|
|
491
|
+
}
|
|
492
|
+
_index++;
|
|
493
|
+
_column++;
|
|
494
|
+
return _buf[_index];
|
|
495
|
+
}
|
|
496
|
+
function peek() {
|
|
497
|
+
if (isCRLF(_index + _peekOffset)) {
|
|
498
|
+
_peekOffset++;
|
|
499
|
+
}
|
|
500
|
+
_peekOffset++;
|
|
501
|
+
return _buf[_index + _peekOffset];
|
|
502
|
+
}
|
|
503
|
+
function reset() {
|
|
504
|
+
_index = 0;
|
|
505
|
+
_line = 1;
|
|
506
|
+
_column = 1;
|
|
507
|
+
_peekOffset = 0;
|
|
508
|
+
}
|
|
509
|
+
function resetPeek(offset = 0) {
|
|
510
|
+
_peekOffset = offset;
|
|
511
|
+
}
|
|
512
|
+
function skipToPeek() {
|
|
513
|
+
const target = _index + _peekOffset;
|
|
514
|
+
while (target !== _index) {
|
|
515
|
+
next();
|
|
516
|
+
}
|
|
517
|
+
_peekOffset = 0;
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
index,
|
|
521
|
+
line,
|
|
522
|
+
column,
|
|
523
|
+
peekOffset,
|
|
524
|
+
charAt,
|
|
525
|
+
currentChar,
|
|
526
|
+
currentPeek,
|
|
527
|
+
next,
|
|
528
|
+
peek,
|
|
529
|
+
reset,
|
|
530
|
+
resetPeek,
|
|
531
|
+
skipToPeek
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const EOF = undefined;
|
|
536
|
+
const DOT = '.';
|
|
537
|
+
const LITERAL_DELIMITER = "'";
|
|
538
|
+
const ERROR_DOMAIN$1 = 'tokenizer';
|
|
539
|
+
function createTokenizer(source, options = {}) {
|
|
540
|
+
const location = options.location !== false;
|
|
541
|
+
const _scnr = createScanner(source);
|
|
542
|
+
const currentOffset = () => _scnr.index();
|
|
543
|
+
const currentPosition = () => createPosition(_scnr.line(), _scnr.column(), _scnr.index());
|
|
544
|
+
const _initLoc = currentPosition();
|
|
545
|
+
const _initOffset = currentOffset();
|
|
546
|
+
const _context = {
|
|
547
|
+
currentType: 13 /* TokenTypes.EOF */,
|
|
548
|
+
offset: _initOffset,
|
|
549
|
+
startLoc: _initLoc,
|
|
550
|
+
endLoc: _initLoc,
|
|
551
|
+
lastType: 13 /* TokenTypes.EOF */,
|
|
552
|
+
lastOffset: _initOffset,
|
|
553
|
+
lastStartLoc: _initLoc,
|
|
554
|
+
lastEndLoc: _initLoc,
|
|
555
|
+
braceNest: 0,
|
|
556
|
+
inLinked: false,
|
|
557
|
+
text: ''
|
|
558
|
+
};
|
|
559
|
+
const context = () => _context;
|
|
560
|
+
const { onError } = options;
|
|
561
|
+
function emitError(code, pos, offset, ...args) {
|
|
562
|
+
const ctx = context();
|
|
563
|
+
pos.column += offset;
|
|
564
|
+
pos.offset += offset;
|
|
565
|
+
if (onError) {
|
|
566
|
+
const loc = location ? createLocation(ctx.startLoc, pos) : null;
|
|
567
|
+
const err = createCompileError(code, loc, {
|
|
568
|
+
domain: ERROR_DOMAIN$1,
|
|
569
|
+
args
|
|
570
|
+
});
|
|
571
|
+
onError(err);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function getToken(context, type, value) {
|
|
575
|
+
context.endLoc = currentPosition();
|
|
576
|
+
context.currentType = type;
|
|
577
|
+
const token = { type };
|
|
578
|
+
if (location) {
|
|
579
|
+
token.loc = createLocation(context.startLoc, context.endLoc);
|
|
580
|
+
}
|
|
581
|
+
if (value != null) {
|
|
582
|
+
token.value = value;
|
|
583
|
+
}
|
|
584
|
+
return token;
|
|
585
|
+
}
|
|
586
|
+
const getEndToken = (context) => getToken(context, 13 /* TokenTypes.EOF */);
|
|
587
|
+
function eat(scnr, ch) {
|
|
588
|
+
if (scnr.currentChar() === ch) {
|
|
589
|
+
scnr.next();
|
|
590
|
+
return ch;
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
emitError(CompileErrorCodes.EXPECTED_TOKEN, currentPosition(), 0, ch);
|
|
594
|
+
return '';
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function peekSpaces(scnr) {
|
|
598
|
+
let buf = '';
|
|
599
|
+
while (scnr.currentPeek() === CHAR_SP || scnr.currentPeek() === CHAR_LF) {
|
|
600
|
+
buf += scnr.currentPeek();
|
|
601
|
+
scnr.peek();
|
|
602
|
+
}
|
|
603
|
+
return buf;
|
|
604
|
+
}
|
|
605
|
+
function skipSpaces(scnr) {
|
|
606
|
+
const buf = peekSpaces(scnr);
|
|
607
|
+
scnr.skipToPeek();
|
|
608
|
+
return buf;
|
|
609
|
+
}
|
|
610
|
+
function isIdentifierStart(ch) {
|
|
611
|
+
if (ch === EOF) {
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
const cc = ch.charCodeAt(0);
|
|
615
|
+
return ((cc >= 97 && cc <= 122) || // a-z
|
|
616
|
+
(cc >= 65 && cc <= 90) || // A-Z
|
|
617
|
+
cc === 95 // _
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
function isNumberStart(ch) {
|
|
256
621
|
if (ch === EOF) {
|
|
257
622
|
return false;
|
|
258
623
|
}
|
|
@@ -422,1140 +787,773 @@ function createTokenizer(source, options = {}) {
|
|
|
422
787
|
function takeNamedIdentifierChar(scnr) {
|
|
423
788
|
return takeChar(scnr, isNamedIdentifier);
|
|
424
789
|
}
|
|
425
|
-
function isDigit(ch) {
|
|
426
|
-
const cc = ch.charCodeAt(0);
|
|
427
|
-
return cc >= 48 && cc <= 57; // 0-9
|
|
428
|
-
}
|
|
429
|
-
function takeDigit(scnr) {
|
|
430
|
-
return takeChar(scnr, isDigit);
|
|
431
|
-
}
|
|
432
|
-
function isHexDigit(ch) {
|
|
433
|
-
const cc = ch.charCodeAt(0);
|
|
434
|
-
return ((cc >= 48 && cc <= 57) || // 0-9
|
|
435
|
-
(cc >= 65 && cc <= 70) || // A-F
|
|
436
|
-
(cc >= 97 && cc <= 102)); // a-f
|
|
437
|
-
}
|
|
438
|
-
function takeHexDigit(scnr) {
|
|
439
|
-
return takeChar(scnr, isHexDigit);
|
|
440
|
-
}
|
|
441
|
-
function getDigits(scnr) {
|
|
442
|
-
let ch = '';
|
|
443
|
-
let num = '';
|
|
444
|
-
while ((ch = takeDigit(scnr))) {
|
|
445
|
-
num += ch;
|
|
446
|
-
}
|
|
447
|
-
return num;
|
|
448
|
-
}
|
|
449
|
-
function readText(scnr) {
|
|
450
|
-
let buf = '';
|
|
451
|
-
while (true) {
|
|
452
|
-
const ch = scnr.currentChar();
|
|
453
|
-
if (ch === "{" /* TokenChars.BraceLeft */ ||
|
|
454
|
-
ch === "}" /* TokenChars.BraceRight */ ||
|
|
455
|
-
ch === "@" /* TokenChars.LinkedAlias */ ||
|
|
456
|
-
ch === "|" /* TokenChars.Pipe */ ||
|
|
457
|
-
!ch) {
|
|
458
|
-
break;
|
|
459
|
-
}
|
|
460
|
-
else if (ch === CHAR_SP || ch === CHAR_LF) {
|
|
461
|
-
if (isTextStart(scnr)) {
|
|
462
|
-
buf += ch;
|
|
463
|
-
scnr.next();
|
|
464
|
-
}
|
|
465
|
-
else if (isPluralStart(scnr)) {
|
|
466
|
-
break;
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
buf += ch;
|
|
470
|
-
scnr.next();
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
else {
|
|
474
|
-
buf += ch;
|
|
475
|
-
scnr.next();
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
return buf;
|
|
479
|
-
}
|
|
480
|
-
function readNamedIdentifier(scnr) {
|
|
481
|
-
skipSpaces(scnr);
|
|
482
|
-
let ch = '';
|
|
483
|
-
let name = '';
|
|
484
|
-
while ((ch = takeNamedIdentifierChar(scnr))) {
|
|
485
|
-
name += ch;
|
|
486
|
-
}
|
|
487
|
-
if (scnr.currentChar() === EOF) {
|
|
488
|
-
emitError(CompileErrorCodes.UNTERMINATED_CLOSING_BRACE, currentPosition(), 0);
|
|
489
|
-
}
|
|
490
|
-
return name;
|
|
491
|
-
}
|
|
492
|
-
function readListIdentifier(scnr) {
|
|
493
|
-
skipSpaces(scnr);
|
|
494
|
-
let value = '';
|
|
495
|
-
if (scnr.currentChar() === '-') {
|
|
496
|
-
scnr.next();
|
|
497
|
-
value += `-${getDigits(scnr)}`;
|
|
498
|
-
}
|
|
499
|
-
else {
|
|
500
|
-
value += getDigits(scnr);
|
|
501
|
-
}
|
|
502
|
-
if (scnr.currentChar() === EOF) {
|
|
503
|
-
emitError(CompileErrorCodes.UNTERMINATED_CLOSING_BRACE, currentPosition(), 0);
|
|
504
|
-
}
|
|
505
|
-
return value;
|
|
506
|
-
}
|
|
507
|
-
function isLiteral(ch) {
|
|
508
|
-
return ch !== LITERAL_DELIMITER && ch !== CHAR_LF;
|
|
509
|
-
}
|
|
510
|
-
function readLiteral(scnr) {
|
|
511
|
-
skipSpaces(scnr);
|
|
512
|
-
// eslint-disable-next-line no-useless-escape
|
|
513
|
-
eat(scnr, `\'`);
|
|
514
|
-
let ch = '';
|
|
515
|
-
let literal = '';
|
|
516
|
-
while ((ch = takeChar(scnr, isLiteral))) {
|
|
517
|
-
if (ch === '\\') {
|
|
518
|
-
literal += readEscapeSequence(scnr);
|
|
519
|
-
}
|
|
520
|
-
else {
|
|
521
|
-
literal += ch;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
const current = scnr.currentChar();
|
|
525
|
-
if (current === CHAR_LF || current === EOF) {
|
|
526
|
-
emitError(CompileErrorCodes.UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER, currentPosition(), 0);
|
|
527
|
-
// TODO: Is it correct really?
|
|
528
|
-
if (current === CHAR_LF) {
|
|
529
|
-
scnr.next();
|
|
530
|
-
// eslint-disable-next-line no-useless-escape
|
|
531
|
-
eat(scnr, `\'`);
|
|
532
|
-
}
|
|
533
|
-
return literal;
|
|
534
|
-
}
|
|
535
|
-
// eslint-disable-next-line no-useless-escape
|
|
536
|
-
eat(scnr, `\'`);
|
|
537
|
-
return literal;
|
|
538
|
-
}
|
|
539
|
-
function readEscapeSequence(scnr) {
|
|
540
|
-
const ch = scnr.currentChar();
|
|
541
|
-
switch (ch) {
|
|
542
|
-
case '\\':
|
|
543
|
-
case `\'`: // eslint-disable-line no-useless-escape
|
|
544
|
-
scnr.next();
|
|
545
|
-
return `\\${ch}`;
|
|
546
|
-
case 'u':
|
|
547
|
-
return readUnicodeEscapeSequence(scnr, ch, 4);
|
|
548
|
-
case 'U':
|
|
549
|
-
return readUnicodeEscapeSequence(scnr, ch, 6);
|
|
550
|
-
default:
|
|
551
|
-
emitError(CompileErrorCodes.UNKNOWN_ESCAPE_SEQUENCE, currentPosition(), 0, ch);
|
|
552
|
-
return '';
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
function readUnicodeEscapeSequence(scnr, unicode, digits) {
|
|
556
|
-
eat(scnr, unicode);
|
|
557
|
-
let sequence = '';
|
|
558
|
-
for (let i = 0; i < digits; i++) {
|
|
559
|
-
const ch = takeHexDigit(scnr);
|
|
560
|
-
if (!ch) {
|
|
561
|
-
emitError(CompileErrorCodes.INVALID_UNICODE_ESCAPE_SEQUENCE, currentPosition(), 0, `\\${unicode}${sequence}${scnr.currentChar()}`);
|
|
562
|
-
break;
|
|
563
|
-
}
|
|
564
|
-
sequence += ch;
|
|
565
|
-
}
|
|
566
|
-
return `\\${unicode}${sequence}`;
|
|
567
|
-
}
|
|
568
|
-
function isInvalidIdentifier(ch) {
|
|
569
|
-
return (ch !== "{" /* TokenChars.BraceLeft */ &&
|
|
570
|
-
ch !== "}" /* TokenChars.BraceRight */ &&
|
|
571
|
-
ch !== CHAR_SP &&
|
|
572
|
-
ch !== CHAR_LF);
|
|
790
|
+
function isDigit(ch) {
|
|
791
|
+
const cc = ch.charCodeAt(0);
|
|
792
|
+
return cc >= 48 && cc <= 57; // 0-9
|
|
573
793
|
}
|
|
574
|
-
function
|
|
575
|
-
|
|
576
|
-
let ch = '';
|
|
577
|
-
let identifiers = '';
|
|
578
|
-
while ((ch = takeChar(scnr, isInvalidIdentifier))) {
|
|
579
|
-
identifiers += ch;
|
|
580
|
-
}
|
|
581
|
-
return identifiers;
|
|
794
|
+
function takeDigit(scnr) {
|
|
795
|
+
return takeChar(scnr, isDigit);
|
|
582
796
|
}
|
|
583
|
-
function
|
|
797
|
+
function isHexDigit(ch) {
|
|
798
|
+
const cc = ch.charCodeAt(0);
|
|
799
|
+
return ((cc >= 48 && cc <= 57) || // 0-9
|
|
800
|
+
(cc >= 65 && cc <= 70) || // A-F
|
|
801
|
+
(cc >= 97 && cc <= 102)); // a-f
|
|
802
|
+
}
|
|
803
|
+
function takeHexDigit(scnr) {
|
|
804
|
+
return takeChar(scnr, isHexDigit);
|
|
805
|
+
}
|
|
806
|
+
function getDigits(scnr) {
|
|
584
807
|
let ch = '';
|
|
585
|
-
let
|
|
586
|
-
while ((ch =
|
|
587
|
-
|
|
808
|
+
let num = '';
|
|
809
|
+
while ((ch = takeDigit(scnr))) {
|
|
810
|
+
num += ch;
|
|
588
811
|
}
|
|
589
|
-
return
|
|
812
|
+
return num;
|
|
590
813
|
}
|
|
591
|
-
function
|
|
592
|
-
|
|
814
|
+
function readText(scnr) {
|
|
815
|
+
let buf = '';
|
|
816
|
+
while (true) {
|
|
593
817
|
const ch = scnr.currentChar();
|
|
594
818
|
if (ch === "{" /* TokenChars.BraceLeft */ ||
|
|
819
|
+
ch === "}" /* TokenChars.BraceRight */ ||
|
|
595
820
|
ch === "@" /* TokenChars.LinkedAlias */ ||
|
|
596
821
|
ch === "|" /* TokenChars.Pipe */ ||
|
|
597
|
-
ch === "(" /* TokenChars.ParenLeft */ ||
|
|
598
|
-
ch === ")" /* TokenChars.ParenRight */ ||
|
|
599
822
|
!ch) {
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
else if (ch === CHAR_SP) {
|
|
603
|
-
return buf;
|
|
604
|
-
}
|
|
605
|
-
else if (ch === CHAR_LF || ch === DOT) {
|
|
606
|
-
buf += ch;
|
|
607
|
-
scnr.next();
|
|
608
|
-
return fn(buf);
|
|
609
|
-
}
|
|
610
|
-
else {
|
|
611
|
-
buf += ch;
|
|
612
|
-
scnr.next();
|
|
613
|
-
return fn(buf);
|
|
823
|
+
break;
|
|
614
824
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
skipSpaces(scnr);
|
|
620
|
-
const plural = eat(scnr, "|" /* TokenChars.Pipe */);
|
|
621
|
-
skipSpaces(scnr);
|
|
622
|
-
return plural;
|
|
623
|
-
}
|
|
624
|
-
// TODO: We need refactoring of token parsing ...
|
|
625
|
-
function readTokenInPlaceholder(scnr, context) {
|
|
626
|
-
let token = null;
|
|
627
|
-
const ch = scnr.currentChar();
|
|
628
|
-
switch (ch) {
|
|
629
|
-
case "{" /* TokenChars.BraceLeft */:
|
|
630
|
-
if (context.braceNest >= 1) {
|
|
631
|
-
emitError(CompileErrorCodes.NOT_ALLOW_NEST_PLACEHOLDER, currentPosition(), 0);
|
|
632
|
-
}
|
|
633
|
-
scnr.next();
|
|
634
|
-
token = getToken(context, 2 /* TokenTypes.BraceLeft */, "{" /* TokenChars.BraceLeft */);
|
|
635
|
-
skipSpaces(scnr);
|
|
636
|
-
context.braceNest++;
|
|
637
|
-
return token;
|
|
638
|
-
case "}" /* TokenChars.BraceRight */:
|
|
639
|
-
if (context.braceNest > 0 &&
|
|
640
|
-
context.currentType === 2 /* TokenTypes.BraceLeft */) {
|
|
641
|
-
emitError(CompileErrorCodes.EMPTY_PLACEHOLDER, currentPosition(), 0);
|
|
642
|
-
}
|
|
643
|
-
scnr.next();
|
|
644
|
-
token = getToken(context, 3 /* TokenTypes.BraceRight */, "}" /* TokenChars.BraceRight */);
|
|
645
|
-
context.braceNest--;
|
|
646
|
-
context.braceNest > 0 && skipSpaces(scnr);
|
|
647
|
-
if (context.inLinked && context.braceNest === 0) {
|
|
648
|
-
context.inLinked = false;
|
|
649
|
-
}
|
|
650
|
-
return token;
|
|
651
|
-
case "@" /* TokenChars.LinkedAlias */:
|
|
652
|
-
if (context.braceNest > 0) {
|
|
653
|
-
emitError(CompileErrorCodes.UNTERMINATED_CLOSING_BRACE, currentPosition(), 0);
|
|
654
|
-
}
|
|
655
|
-
token = readTokenInLinked(scnr, context) || getEndToken(context);
|
|
656
|
-
context.braceNest = 0;
|
|
657
|
-
return token;
|
|
658
|
-
default: {
|
|
659
|
-
let validNamedIdentifier = true;
|
|
660
|
-
let validListIdentifier = true;
|
|
661
|
-
let validLiteral = true;
|
|
662
|
-
if (isPluralStart(scnr)) {
|
|
663
|
-
if (context.braceNest > 0) {
|
|
664
|
-
emitError(CompileErrorCodes.UNTERMINATED_CLOSING_BRACE, currentPosition(), 0);
|
|
665
|
-
}
|
|
666
|
-
token = getToken(context, 1 /* TokenTypes.Pipe */, readPlural(scnr));
|
|
667
|
-
// reset
|
|
668
|
-
context.braceNest = 0;
|
|
669
|
-
context.inLinked = false;
|
|
670
|
-
return token;
|
|
671
|
-
}
|
|
672
|
-
if (context.braceNest > 0 &&
|
|
673
|
-
(context.currentType === 4 /* TokenTypes.Named */ ||
|
|
674
|
-
context.currentType === 5 /* TokenTypes.List */ ||
|
|
675
|
-
context.currentType === 6 /* TokenTypes.Literal */)) {
|
|
676
|
-
emitError(CompileErrorCodes.UNTERMINATED_CLOSING_BRACE, currentPosition(), 0);
|
|
677
|
-
context.braceNest = 0;
|
|
678
|
-
return readToken(scnr, context);
|
|
679
|
-
}
|
|
680
|
-
if ((validNamedIdentifier = isNamedIdentifierStart(scnr, context))) {
|
|
681
|
-
token = getToken(context, 4 /* TokenTypes.Named */, readNamedIdentifier(scnr));
|
|
682
|
-
skipSpaces(scnr);
|
|
683
|
-
return token;
|
|
684
|
-
}
|
|
685
|
-
if ((validListIdentifier = isListIdentifierStart(scnr, context))) {
|
|
686
|
-
token = getToken(context, 5 /* TokenTypes.List */, readListIdentifier(scnr));
|
|
687
|
-
skipSpaces(scnr);
|
|
688
|
-
return token;
|
|
825
|
+
else if (ch === CHAR_SP || ch === CHAR_LF) {
|
|
826
|
+
if (isTextStart(scnr)) {
|
|
827
|
+
buf += ch;
|
|
828
|
+
scnr.next();
|
|
689
829
|
}
|
|
690
|
-
if ((
|
|
691
|
-
|
|
692
|
-
skipSpaces(scnr);
|
|
693
|
-
return token;
|
|
830
|
+
else if (isPluralStart(scnr)) {
|
|
831
|
+
break;
|
|
694
832
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
emitError(CompileErrorCodes.INVALID_TOKEN_IN_PLACEHOLDER, currentPosition(), 0, token.value);
|
|
699
|
-
skipSpaces(scnr);
|
|
700
|
-
return token;
|
|
833
|
+
else {
|
|
834
|
+
buf += ch;
|
|
835
|
+
scnr.next();
|
|
701
836
|
}
|
|
702
|
-
break;
|
|
703
837
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
// TODO: We need refactoring of token parsing ...
|
|
708
|
-
function readTokenInLinked(scnr, context) {
|
|
709
|
-
const { currentType } = context;
|
|
710
|
-
let token = null;
|
|
711
|
-
const ch = scnr.currentChar();
|
|
712
|
-
if ((currentType === 7 /* TokenTypes.LinkedAlias */ ||
|
|
713
|
-
currentType === 8 /* TokenTypes.LinkedDot */ ||
|
|
714
|
-
currentType === 11 /* TokenTypes.LinkedModifier */ ||
|
|
715
|
-
currentType === 9 /* TokenTypes.LinkedDelimiter */) &&
|
|
716
|
-
(ch === CHAR_LF || ch === CHAR_SP)) {
|
|
717
|
-
emitError(CompileErrorCodes.INVALID_LINKED_FORMAT, currentPosition(), 0);
|
|
718
|
-
}
|
|
719
|
-
switch (ch) {
|
|
720
|
-
case "@" /* TokenChars.LinkedAlias */:
|
|
721
|
-
scnr.next();
|
|
722
|
-
token = getToken(context, 7 /* TokenTypes.LinkedAlias */, "@" /* TokenChars.LinkedAlias */);
|
|
723
|
-
context.inLinked = true;
|
|
724
|
-
return token;
|
|
725
|
-
case "." /* TokenChars.LinkedDot */:
|
|
726
|
-
skipSpaces(scnr);
|
|
727
|
-
scnr.next();
|
|
728
|
-
return getToken(context, 8 /* TokenTypes.LinkedDot */, "." /* TokenChars.LinkedDot */);
|
|
729
|
-
case ":" /* TokenChars.LinkedDelimiter */:
|
|
730
|
-
skipSpaces(scnr);
|
|
838
|
+
else {
|
|
839
|
+
buf += ch;
|
|
731
840
|
scnr.next();
|
|
732
|
-
|
|
733
|
-
default:
|
|
734
|
-
if (isPluralStart(scnr)) {
|
|
735
|
-
token = getToken(context, 1 /* TokenTypes.Pipe */, readPlural(scnr));
|
|
736
|
-
// reset
|
|
737
|
-
context.braceNest = 0;
|
|
738
|
-
context.inLinked = false;
|
|
739
|
-
return token;
|
|
740
|
-
}
|
|
741
|
-
if (isLinkedDotStart(scnr, context) ||
|
|
742
|
-
isLinkedDelimiterStart(scnr, context)) {
|
|
743
|
-
skipSpaces(scnr);
|
|
744
|
-
return readTokenInLinked(scnr, context);
|
|
745
|
-
}
|
|
746
|
-
if (isLinkedModifierStart(scnr, context)) {
|
|
747
|
-
skipSpaces(scnr);
|
|
748
|
-
return getToken(context, 11 /* TokenTypes.LinkedModifier */, readLinkedModifier(scnr));
|
|
749
|
-
}
|
|
750
|
-
if (isLinkedReferStart(scnr, context)) {
|
|
751
|
-
skipSpaces(scnr);
|
|
752
|
-
if (ch === "{" /* TokenChars.BraceLeft */) {
|
|
753
|
-
// scan the placeholder
|
|
754
|
-
return readTokenInPlaceholder(scnr, context) || token;
|
|
755
|
-
}
|
|
756
|
-
else {
|
|
757
|
-
return getToken(context, 10 /* TokenTypes.LinkedKey */, readLinkedRefer(scnr));
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
if (currentType === 7 /* TokenTypes.LinkedAlias */) {
|
|
761
|
-
emitError(CompileErrorCodes.INVALID_LINKED_FORMAT, currentPosition(), 0);
|
|
762
|
-
}
|
|
763
|
-
context.braceNest = 0;
|
|
764
|
-
context.inLinked = false;
|
|
765
|
-
return readToken(scnr, context);
|
|
841
|
+
}
|
|
766
842
|
}
|
|
843
|
+
return buf;
|
|
767
844
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
let
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
if (context.inLinked) {
|
|
775
|
-
return readTokenInLinked(scnr, context) || getEndToken(context);
|
|
845
|
+
function readNamedIdentifier(scnr) {
|
|
846
|
+
skipSpaces(scnr);
|
|
847
|
+
let ch = '';
|
|
848
|
+
let name = '';
|
|
849
|
+
while ((ch = takeNamedIdentifierChar(scnr))) {
|
|
850
|
+
name += ch;
|
|
776
851
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
case "{" /* TokenChars.BraceLeft */:
|
|
780
|
-
return readTokenInPlaceholder(scnr, context) || getEndToken(context);
|
|
781
|
-
case "}" /* TokenChars.BraceRight */:
|
|
782
|
-
emitError(CompileErrorCodes.UNBALANCED_CLOSING_BRACE, currentPosition(), 0);
|
|
783
|
-
scnr.next();
|
|
784
|
-
return getToken(context, 3 /* TokenTypes.BraceRight */, "}" /* TokenChars.BraceRight */);
|
|
785
|
-
case "@" /* TokenChars.LinkedAlias */:
|
|
786
|
-
return readTokenInLinked(scnr, context) || getEndToken(context);
|
|
787
|
-
default: {
|
|
788
|
-
if (isPluralStart(scnr)) {
|
|
789
|
-
token = getToken(context, 1 /* TokenTypes.Pipe */, readPlural(scnr));
|
|
790
|
-
// reset
|
|
791
|
-
context.braceNest = 0;
|
|
792
|
-
context.inLinked = false;
|
|
793
|
-
return token;
|
|
794
|
-
}
|
|
795
|
-
if (isTextStart(scnr)) {
|
|
796
|
-
return getToken(context, 0 /* TokenTypes.Text */, readText(scnr));
|
|
797
|
-
}
|
|
798
|
-
break;
|
|
799
|
-
}
|
|
852
|
+
if (scnr.currentChar() === EOF) {
|
|
853
|
+
emitError(CompileErrorCodes.UNTERMINATED_CLOSING_BRACE, currentPosition(), 0);
|
|
800
854
|
}
|
|
801
|
-
return
|
|
855
|
+
return name;
|
|
802
856
|
}
|
|
803
|
-
function
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
_context.offset = currentOffset();
|
|
810
|
-
_context.startLoc = currentPosition();
|
|
811
|
-
if (_scnr.currentChar() === EOF) {
|
|
812
|
-
return getToken(_context, 13 /* TokenTypes.EOF */);
|
|
857
|
+
function readListIdentifier(scnr) {
|
|
858
|
+
skipSpaces(scnr);
|
|
859
|
+
let value = '';
|
|
860
|
+
if (scnr.currentChar() === '-') {
|
|
861
|
+
scnr.next();
|
|
862
|
+
value += `-${getDigits(scnr)}`;
|
|
813
863
|
}
|
|
814
|
-
|
|
864
|
+
else {
|
|
865
|
+
value += getDigits(scnr);
|
|
866
|
+
}
|
|
867
|
+
if (scnr.currentChar() === EOF) {
|
|
868
|
+
emitError(CompileErrorCodes.UNTERMINATED_CLOSING_BRACE, currentPosition(), 0);
|
|
869
|
+
}
|
|
870
|
+
return value;
|
|
815
871
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
};
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
const ERROR_DOMAIN$2 = 'parser';
|
|
825
|
-
// Backslash backslash, backslash quote, uHHHH, UHHHHHH.
|
|
826
|
-
const KNOWN_ESCAPES = /(?:\\\\|\\'|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;
|
|
827
|
-
function fromEscapeSequence(match, codePoint4, codePoint6) {
|
|
828
|
-
switch (match) {
|
|
829
|
-
case `\\\\`:
|
|
830
|
-
return `\\`;
|
|
872
|
+
function isLiteral(ch) {
|
|
873
|
+
return ch !== LITERAL_DELIMITER && ch !== CHAR_LF;
|
|
874
|
+
}
|
|
875
|
+
function readLiteral(scnr) {
|
|
876
|
+
skipSpaces(scnr);
|
|
831
877
|
// eslint-disable-next-line no-useless-escape
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
878
|
+
eat(scnr, `\'`);
|
|
879
|
+
let ch = '';
|
|
880
|
+
let literal = '';
|
|
881
|
+
while ((ch = takeChar(scnr, isLiteral))) {
|
|
882
|
+
if (ch === '\\') {
|
|
883
|
+
literal += readEscapeSequence(scnr);
|
|
884
|
+
}
|
|
885
|
+
else {
|
|
886
|
+
literal += ch;
|
|
839
887
|
}
|
|
840
|
-
// invalid ...
|
|
841
|
-
// Replace them with U+FFFD REPLACEMENT CHARACTER.
|
|
842
|
-
return '�';
|
|
843
888
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
const loc = location ? createLocation(start, end) : null;
|
|
855
|
-
const err = createCompileError(code, loc, {
|
|
856
|
-
domain: ERROR_DOMAIN$2,
|
|
857
|
-
args
|
|
858
|
-
});
|
|
859
|
-
onError(err);
|
|
889
|
+
const current = scnr.currentChar();
|
|
890
|
+
if (current === CHAR_LF || current === EOF) {
|
|
891
|
+
emitError(CompileErrorCodes.UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER, currentPosition(), 0);
|
|
892
|
+
// TODO: Is it correct really?
|
|
893
|
+
if (current === CHAR_LF) {
|
|
894
|
+
scnr.next();
|
|
895
|
+
// eslint-disable-next-line no-useless-escape
|
|
896
|
+
eat(scnr, `\'`);
|
|
897
|
+
}
|
|
898
|
+
return literal;
|
|
860
899
|
}
|
|
900
|
+
// eslint-disable-next-line no-useless-escape
|
|
901
|
+
eat(scnr, `\'`);
|
|
902
|
+
return literal;
|
|
861
903
|
}
|
|
862
|
-
function
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
904
|
+
function readEscapeSequence(scnr) {
|
|
905
|
+
const ch = scnr.currentChar();
|
|
906
|
+
switch (ch) {
|
|
907
|
+
case '\\':
|
|
908
|
+
case `\'`: // eslint-disable-line no-useless-escape
|
|
909
|
+
scnr.next();
|
|
910
|
+
return `\\${ch}`;
|
|
911
|
+
case 'u':
|
|
912
|
+
return readUnicodeEscapeSequence(scnr, ch, 4);
|
|
913
|
+
case 'U':
|
|
914
|
+
return readUnicodeEscapeSequence(scnr, ch, 6);
|
|
915
|
+
default:
|
|
916
|
+
emitError(CompileErrorCodes.UNKNOWN_ESCAPE_SEQUENCE, currentPosition(), 0, ch);
|
|
917
|
+
return '';
|
|
868
918
|
}
|
|
869
|
-
return node;
|
|
870
919
|
}
|
|
871
|
-
function
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
920
|
+
function readUnicodeEscapeSequence(scnr, unicode, digits) {
|
|
921
|
+
eat(scnr, unicode);
|
|
922
|
+
let sequence = '';
|
|
923
|
+
for (let i = 0; i < digits; i++) {
|
|
924
|
+
const ch = takeHexDigit(scnr);
|
|
925
|
+
if (!ch) {
|
|
926
|
+
emitError(CompileErrorCodes.INVALID_UNICODE_ESCAPE_SEQUENCE, currentPosition(), 0, `\\${unicode}${sequence}${scnr.currentChar()}`);
|
|
927
|
+
break;
|
|
876
928
|
}
|
|
929
|
+
sequence += ch;
|
|
877
930
|
}
|
|
931
|
+
return `\\${unicode}${sequence}`;
|
|
878
932
|
}
|
|
879
|
-
function
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
return node;
|
|
885
|
-
}
|
|
886
|
-
function parseList(tokenizer, index) {
|
|
887
|
-
const context = tokenizer.context();
|
|
888
|
-
const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
|
|
889
|
-
const node = startNode(5 /* NodeTypes.List */, offset, loc);
|
|
890
|
-
node.index = parseInt(index, 10);
|
|
891
|
-
tokenizer.nextToken(); // skip brach right
|
|
892
|
-
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
893
|
-
return node;
|
|
894
|
-
}
|
|
895
|
-
function parseNamed(tokenizer, key) {
|
|
896
|
-
const context = tokenizer.context();
|
|
897
|
-
const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
|
|
898
|
-
const node = startNode(4 /* NodeTypes.Named */, offset, loc);
|
|
899
|
-
node.key = key;
|
|
900
|
-
tokenizer.nextToken(); // skip brach right
|
|
901
|
-
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
902
|
-
return node;
|
|
903
|
-
}
|
|
904
|
-
function parseLiteral(tokenizer, value) {
|
|
905
|
-
const context = tokenizer.context();
|
|
906
|
-
const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
|
|
907
|
-
const node = startNode(9 /* NodeTypes.Literal */, offset, loc);
|
|
908
|
-
node.value = value.replace(KNOWN_ESCAPES, fromEscapeSequence);
|
|
909
|
-
tokenizer.nextToken(); // skip brach right
|
|
910
|
-
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
911
|
-
return node;
|
|
933
|
+
function isInvalidIdentifier(ch) {
|
|
934
|
+
return (ch !== "{" /* TokenChars.BraceLeft */ &&
|
|
935
|
+
ch !== "}" /* TokenChars.BraceRight */ &&
|
|
936
|
+
ch !== CHAR_SP &&
|
|
937
|
+
ch !== CHAR_LF);
|
|
912
938
|
}
|
|
913
|
-
function
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
// empty modifier
|
|
920
|
-
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_EMPTY_LINKED_MODIFIER, context.lastStartLoc, 0);
|
|
921
|
-
node.value = '';
|
|
922
|
-
endNode(node, offset, loc);
|
|
923
|
-
return {
|
|
924
|
-
nextConsumeToken: token,
|
|
925
|
-
node
|
|
926
|
-
};
|
|
927
|
-
}
|
|
928
|
-
// check token
|
|
929
|
-
if (token.value == null) {
|
|
930
|
-
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
939
|
+
function readInvalidIdentifier(scnr) {
|
|
940
|
+
skipSpaces(scnr);
|
|
941
|
+
let ch = '';
|
|
942
|
+
let identifiers = '';
|
|
943
|
+
while ((ch = takeChar(scnr, isInvalidIdentifier))) {
|
|
944
|
+
identifiers += ch;
|
|
931
945
|
}
|
|
932
|
-
|
|
933
|
-
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
934
|
-
return {
|
|
935
|
-
node
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
function parseLinkedKey(tokenizer, value) {
|
|
939
|
-
const context = tokenizer.context();
|
|
940
|
-
const node = startNode(7 /* NodeTypes.LinkedKey */, context.offset, context.startLoc);
|
|
941
|
-
node.value = value;
|
|
942
|
-
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
943
|
-
return node;
|
|
946
|
+
return identifiers;
|
|
944
947
|
}
|
|
945
|
-
function
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
const parsed = parseLinkedModifier(tokenizer);
|
|
951
|
-
linkedNode.modifier = parsed.node;
|
|
952
|
-
token = parsed.nextConsumeToken || tokenizer.nextToken();
|
|
953
|
-
}
|
|
954
|
-
// asset check token
|
|
955
|
-
if (token.type !== 9 /* TokenTypes.LinkedDelimiter */) {
|
|
956
|
-
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
957
|
-
}
|
|
958
|
-
token = tokenizer.nextToken();
|
|
959
|
-
// skip brace left
|
|
960
|
-
if (token.type === 2 /* TokenTypes.BraceLeft */) {
|
|
961
|
-
token = tokenizer.nextToken();
|
|
948
|
+
function readLinkedModifier(scnr) {
|
|
949
|
+
let ch = '';
|
|
950
|
+
let name = '';
|
|
951
|
+
while ((ch = takeIdentifierChar(scnr))) {
|
|
952
|
+
name += ch;
|
|
962
953
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
954
|
+
return name;
|
|
955
|
+
}
|
|
956
|
+
function readLinkedRefer(scnr) {
|
|
957
|
+
const fn = (buf) => {
|
|
958
|
+
const ch = scnr.currentChar();
|
|
959
|
+
if (ch === "{" /* TokenChars.BraceLeft */ ||
|
|
960
|
+
ch === "@" /* TokenChars.LinkedAlias */ ||
|
|
961
|
+
ch === "|" /* TokenChars.Pipe */ ||
|
|
962
|
+
ch === "(" /* TokenChars.ParenLeft */ ||
|
|
963
|
+
ch === ")" /* TokenChars.ParenRight */ ||
|
|
964
|
+
!ch) {
|
|
965
|
+
return buf;
|
|
966
|
+
}
|
|
967
|
+
else if (ch === CHAR_SP) {
|
|
968
|
+
return buf;
|
|
969
|
+
}
|
|
970
|
+
else if (ch === CHAR_LF || ch === DOT) {
|
|
971
|
+
buf += ch;
|
|
972
|
+
scnr.next();
|
|
973
|
+
return fn(buf);
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
buf += ch;
|
|
977
|
+
scnr.next();
|
|
978
|
+
return fn(buf);
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
return fn('');
|
|
982
|
+
}
|
|
983
|
+
function readPlural(scnr) {
|
|
984
|
+
skipSpaces(scnr);
|
|
985
|
+
const plural = eat(scnr, "|" /* TokenChars.Pipe */);
|
|
986
|
+
skipSpaces(scnr);
|
|
987
|
+
return plural;
|
|
988
|
+
}
|
|
989
|
+
// TODO: We need refactoring of token parsing ...
|
|
990
|
+
function readTokenInPlaceholder(scnr, context) {
|
|
991
|
+
let token = null;
|
|
992
|
+
const ch = scnr.currentChar();
|
|
993
|
+
switch (ch) {
|
|
994
|
+
case "{" /* TokenChars.BraceLeft */:
|
|
995
|
+
if (context.braceNest >= 1) {
|
|
996
|
+
emitError(CompileErrorCodes.NOT_ALLOW_NEST_PLACEHOLDER, currentPosition(), 0);
|
|
967
997
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
998
|
+
scnr.next();
|
|
999
|
+
token = getToken(context, 2 /* TokenTypes.BraceLeft */, "{" /* TokenChars.BraceLeft */);
|
|
1000
|
+
skipSpaces(scnr);
|
|
1001
|
+
context.braceNest++;
|
|
1002
|
+
return token;
|
|
1003
|
+
case "}" /* TokenChars.BraceRight */:
|
|
1004
|
+
if (context.braceNest > 0 &&
|
|
1005
|
+
context.currentType === 2 /* TokenTypes.BraceLeft */) {
|
|
1006
|
+
emitError(CompileErrorCodes.EMPTY_PLACEHOLDER, currentPosition(), 0);
|
|
973
1007
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1008
|
+
scnr.next();
|
|
1009
|
+
token = getToken(context, 3 /* TokenTypes.BraceRight */, "}" /* TokenChars.BraceRight */);
|
|
1010
|
+
context.braceNest--;
|
|
1011
|
+
context.braceNest > 0 && skipSpaces(scnr);
|
|
1012
|
+
if (context.inLinked && context.braceNest === 0) {
|
|
1013
|
+
context.inLinked = false;
|
|
979
1014
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1015
|
+
return token;
|
|
1016
|
+
case "@" /* TokenChars.LinkedAlias */:
|
|
1017
|
+
if (context.braceNest > 0) {
|
|
1018
|
+
emitError(CompileErrorCodes.UNTERMINATED_CLOSING_BRACE, currentPosition(), 0);
|
|
985
1019
|
}
|
|
986
|
-
|
|
987
|
-
|
|
1020
|
+
token = readTokenInLinked(scnr, context) || getEndToken(context);
|
|
1021
|
+
context.braceNest = 0;
|
|
1022
|
+
return token;
|
|
988
1023
|
default: {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
linkedNode.key = emptyLinkedKeyNode;
|
|
996
|
-
endNode(linkedNode, nextContext.offset, nextContext.startLoc);
|
|
997
|
-
return {
|
|
998
|
-
nextConsumeToken: token,
|
|
999
|
-
node: linkedNode
|
|
1000
|
-
};
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
endNode(linkedNode, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
1004
|
-
return {
|
|
1005
|
-
node: linkedNode
|
|
1006
|
-
};
|
|
1007
|
-
}
|
|
1008
|
-
function parseMessage(tokenizer) {
|
|
1009
|
-
const context = tokenizer.context();
|
|
1010
|
-
const startOffset = context.currentType === 1 /* TokenTypes.Pipe */
|
|
1011
|
-
? tokenizer.currentOffset()
|
|
1012
|
-
: context.offset;
|
|
1013
|
-
const startLoc = context.currentType === 1 /* TokenTypes.Pipe */
|
|
1014
|
-
? context.endLoc
|
|
1015
|
-
: context.startLoc;
|
|
1016
|
-
const node = startNode(2 /* NodeTypes.Message */, startOffset, startLoc);
|
|
1017
|
-
node.items = [];
|
|
1018
|
-
let nextToken = null;
|
|
1019
|
-
do {
|
|
1020
|
-
const token = nextToken || tokenizer.nextToken();
|
|
1021
|
-
nextToken = null;
|
|
1022
|
-
switch (token.type) {
|
|
1023
|
-
case 0 /* TokenTypes.Text */:
|
|
1024
|
-
if (token.value == null) {
|
|
1025
|
-
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1026
|
-
}
|
|
1027
|
-
node.items.push(parseText(tokenizer, token.value || ''));
|
|
1028
|
-
break;
|
|
1029
|
-
case 5 /* TokenTypes.List */:
|
|
1030
|
-
if (token.value == null) {
|
|
1031
|
-
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1032
|
-
}
|
|
1033
|
-
node.items.push(parseList(tokenizer, token.value || ''));
|
|
1034
|
-
break;
|
|
1035
|
-
case 4 /* TokenTypes.Named */:
|
|
1036
|
-
if (token.value == null) {
|
|
1037
|
-
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1038
|
-
}
|
|
1039
|
-
node.items.push(parseNamed(tokenizer, token.value || ''));
|
|
1040
|
-
break;
|
|
1041
|
-
case 6 /* TokenTypes.Literal */:
|
|
1042
|
-
if (token.value == null) {
|
|
1043
|
-
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1024
|
+
let validNamedIdentifier = true;
|
|
1025
|
+
let validListIdentifier = true;
|
|
1026
|
+
let validLiteral = true;
|
|
1027
|
+
if (isPluralStart(scnr)) {
|
|
1028
|
+
if (context.braceNest > 0) {
|
|
1029
|
+
emitError(CompileErrorCodes.UNTERMINATED_CLOSING_BRACE, currentPosition(), 0);
|
|
1044
1030
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
nextToken = parsed.nextConsumeToken || null;
|
|
1051
|
-
break;
|
|
1031
|
+
token = getToken(context, 1 /* TokenTypes.Pipe */, readPlural(scnr));
|
|
1032
|
+
// reset
|
|
1033
|
+
context.braceNest = 0;
|
|
1034
|
+
context.inLinked = false;
|
|
1035
|
+
return token;
|
|
1052
1036
|
}
|
|
1037
|
+
if (context.braceNest > 0 &&
|
|
1038
|
+
(context.currentType === 4 /* TokenTypes.Named */ ||
|
|
1039
|
+
context.currentType === 5 /* TokenTypes.List */ ||
|
|
1040
|
+
context.currentType === 6 /* TokenTypes.Literal */)) {
|
|
1041
|
+
emitError(CompileErrorCodes.UNTERMINATED_CLOSING_BRACE, currentPosition(), 0);
|
|
1042
|
+
context.braceNest = 0;
|
|
1043
|
+
return readToken(scnr, context);
|
|
1044
|
+
}
|
|
1045
|
+
if ((validNamedIdentifier = isNamedIdentifierStart(scnr, context))) {
|
|
1046
|
+
token = getToken(context, 4 /* TokenTypes.Named */, readNamedIdentifier(scnr));
|
|
1047
|
+
skipSpaces(scnr);
|
|
1048
|
+
return token;
|
|
1049
|
+
}
|
|
1050
|
+
if ((validListIdentifier = isListIdentifierStart(scnr, context))) {
|
|
1051
|
+
token = getToken(context, 5 /* TokenTypes.List */, readListIdentifier(scnr));
|
|
1052
|
+
skipSpaces(scnr);
|
|
1053
|
+
return token;
|
|
1054
|
+
}
|
|
1055
|
+
if ((validLiteral = isLiteralStart(scnr, context))) {
|
|
1056
|
+
token = getToken(context, 6 /* TokenTypes.Literal */, readLiteral(scnr));
|
|
1057
|
+
skipSpaces(scnr);
|
|
1058
|
+
return token;
|
|
1059
|
+
}
|
|
1060
|
+
if (!validNamedIdentifier && !validListIdentifier && !validLiteral) {
|
|
1061
|
+
// TODO: we should be re-designed invalid cases, when we will extend message syntax near the future ...
|
|
1062
|
+
token = getToken(context, 12 /* TokenTypes.InvalidPlace */, readInvalidIdentifier(scnr));
|
|
1063
|
+
emitError(CompileErrorCodes.INVALID_TOKEN_IN_PLACEHOLDER, currentPosition(), 0, token.value);
|
|
1064
|
+
skipSpaces(scnr);
|
|
1065
|
+
return token;
|
|
1066
|
+
}
|
|
1067
|
+
break;
|
|
1053
1068
|
}
|
|
1054
|
-
} while (context.currentType !== 13 /* TokenTypes.EOF */ &&
|
|
1055
|
-
context.currentType !== 1 /* TokenTypes.Pipe */);
|
|
1056
|
-
// adjust message node loc
|
|
1057
|
-
const endOffset = context.currentType === 1 /* TokenTypes.Pipe */
|
|
1058
|
-
? context.lastOffset
|
|
1059
|
-
: tokenizer.currentOffset();
|
|
1060
|
-
const endLoc = context.currentType === 1 /* TokenTypes.Pipe */
|
|
1061
|
-
? context.lastEndLoc
|
|
1062
|
-
: tokenizer.currentPosition();
|
|
1063
|
-
endNode(node, endOffset, endLoc);
|
|
1064
|
-
return node;
|
|
1065
|
-
}
|
|
1066
|
-
function parsePlural(tokenizer, offset, loc, msgNode) {
|
|
1067
|
-
const context = tokenizer.context();
|
|
1068
|
-
let hasEmptyMessage = msgNode.items.length === 0;
|
|
1069
|
-
const node = startNode(1 /* NodeTypes.Plural */, offset, loc);
|
|
1070
|
-
node.cases = [];
|
|
1071
|
-
node.cases.push(msgNode);
|
|
1072
|
-
do {
|
|
1073
|
-
const msg = parseMessage(tokenizer);
|
|
1074
|
-
if (!hasEmptyMessage) {
|
|
1075
|
-
hasEmptyMessage = msg.items.length === 0;
|
|
1076
|
-
}
|
|
1077
|
-
node.cases.push(msg);
|
|
1078
|
-
} while (context.currentType !== 13 /* TokenTypes.EOF */);
|
|
1079
|
-
if (hasEmptyMessage) {
|
|
1080
|
-
emitError(tokenizer, CompileErrorCodes.MUST_HAVE_MESSAGES_IN_PLURAL, loc, 0);
|
|
1081
1069
|
}
|
|
1082
|
-
|
|
1083
|
-
return node;
|
|
1070
|
+
return token;
|
|
1084
1071
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
const {
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1072
|
+
// TODO: We need refactoring of token parsing ...
|
|
1073
|
+
function readTokenInLinked(scnr, context) {
|
|
1074
|
+
const { currentType } = context;
|
|
1075
|
+
let token = null;
|
|
1076
|
+
const ch = scnr.currentChar();
|
|
1077
|
+
if ((currentType === 7 /* TokenTypes.LinkedAlias */ ||
|
|
1078
|
+
currentType === 8 /* TokenTypes.LinkedDot */ ||
|
|
1079
|
+
currentType === 11 /* TokenTypes.LinkedModifier */ ||
|
|
1080
|
+
currentType === 9 /* TokenTypes.LinkedDelimiter */) &&
|
|
1081
|
+
(ch === CHAR_LF || ch === CHAR_SP)) {
|
|
1082
|
+
emitError(CompileErrorCodes.INVALID_LINKED_FORMAT, currentPosition(), 0);
|
|
1091
1083
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1084
|
+
switch (ch) {
|
|
1085
|
+
case "@" /* TokenChars.LinkedAlias */:
|
|
1086
|
+
scnr.next();
|
|
1087
|
+
token = getToken(context, 7 /* TokenTypes.LinkedAlias */, "@" /* TokenChars.LinkedAlias */);
|
|
1088
|
+
context.inLinked = true;
|
|
1089
|
+
return token;
|
|
1090
|
+
case "." /* TokenChars.LinkedDot */:
|
|
1091
|
+
skipSpaces(scnr);
|
|
1092
|
+
scnr.next();
|
|
1093
|
+
return getToken(context, 8 /* TokenTypes.LinkedDot */, "." /* TokenChars.LinkedDot */);
|
|
1094
|
+
case ":" /* TokenChars.LinkedDelimiter */:
|
|
1095
|
+
skipSpaces(scnr);
|
|
1096
|
+
scnr.next();
|
|
1097
|
+
return getToken(context, 9 /* TokenTypes.LinkedDelimiter */, ":" /* TokenChars.LinkedDelimiter */);
|
|
1098
|
+
default:
|
|
1099
|
+
if (isPluralStart(scnr)) {
|
|
1100
|
+
token = getToken(context, 1 /* TokenTypes.Pipe */, readPlural(scnr));
|
|
1101
|
+
// reset
|
|
1102
|
+
context.braceNest = 0;
|
|
1103
|
+
context.inLinked = false;
|
|
1104
|
+
return token;
|
|
1105
|
+
}
|
|
1106
|
+
if (isLinkedDotStart(scnr, context) ||
|
|
1107
|
+
isLinkedDelimiterStart(scnr, context)) {
|
|
1108
|
+
skipSpaces(scnr);
|
|
1109
|
+
return readTokenInLinked(scnr, context);
|
|
1110
|
+
}
|
|
1111
|
+
if (isLinkedModifierStart(scnr, context)) {
|
|
1112
|
+
skipSpaces(scnr);
|
|
1113
|
+
return getToken(context, 11 /* TokenTypes.LinkedModifier */, readLinkedModifier(scnr));
|
|
1114
|
+
}
|
|
1115
|
+
if (isLinkedReferStart(scnr, context)) {
|
|
1116
|
+
skipSpaces(scnr);
|
|
1117
|
+
if (ch === "{" /* TokenChars.BraceLeft */) {
|
|
1118
|
+
// scan the placeholder
|
|
1119
|
+
return readTokenInPlaceholder(scnr, context) || token;
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
return getToken(context, 10 /* TokenTypes.LinkedKey */, readLinkedRefer(scnr));
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
if (currentType === 7 /* TokenTypes.LinkedAlias */) {
|
|
1126
|
+
emitError(CompileErrorCodes.INVALID_LINKED_FORMAT, currentPosition(), 0);
|
|
1127
|
+
}
|
|
1128
|
+
context.braceNest = 0;
|
|
1129
|
+
context.inLinked = false;
|
|
1130
|
+
return readToken(scnr, context);
|
|
1094
1131
|
}
|
|
1095
1132
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
node.loc.source = source;
|
|
1133
|
+
// TODO: We need refactoring of token parsing ...
|
|
1134
|
+
function readToken(scnr, context) {
|
|
1135
|
+
let token = { type: 13 /* TokenTypes.EOF */ };
|
|
1136
|
+
if (context.braceNest > 0) {
|
|
1137
|
+
return readTokenInPlaceholder(scnr, context) || getEndToken(context);
|
|
1102
1138
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
node.cacheKey = options.onCacheKey(source);
|
|
1139
|
+
if (context.inLinked) {
|
|
1140
|
+
return readTokenInLinked(scnr, context) || getEndToken(context);
|
|
1106
1141
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1142
|
+
const ch = scnr.currentChar();
|
|
1143
|
+
switch (ch) {
|
|
1144
|
+
case "{" /* TokenChars.BraceLeft */:
|
|
1145
|
+
return readTokenInPlaceholder(scnr, context) || getEndToken(context);
|
|
1146
|
+
case "}" /* TokenChars.BraceRight */:
|
|
1147
|
+
emitError(CompileErrorCodes.UNBALANCED_CLOSING_BRACE, currentPosition(), 0);
|
|
1148
|
+
scnr.next();
|
|
1149
|
+
return getToken(context, 3 /* TokenTypes.BraceRight */, "}" /* TokenChars.BraceRight */);
|
|
1150
|
+
case "@" /* TokenChars.LinkedAlias */:
|
|
1151
|
+
return readTokenInLinked(scnr, context) || getEndToken(context);
|
|
1152
|
+
default: {
|
|
1153
|
+
if (isPluralStart(scnr)) {
|
|
1154
|
+
token = getToken(context, 1 /* TokenTypes.Pipe */, readPlural(scnr));
|
|
1155
|
+
// reset
|
|
1156
|
+
context.braceNest = 0;
|
|
1157
|
+
context.inLinked = false;
|
|
1158
|
+
return token;
|
|
1159
|
+
}
|
|
1160
|
+
if (isTextStart(scnr)) {
|
|
1161
|
+
return getToken(context, 0 /* TokenTypes.Text */, readText(scnr));
|
|
1162
|
+
}
|
|
1163
|
+
break;
|
|
1164
|
+
}
|
|
1110
1165
|
}
|
|
1111
|
-
|
|
1112
|
-
return node;
|
|
1113
|
-
}
|
|
1114
|
-
return { parse };
|
|
1115
|
-
}
|
|
1116
|
-
function getTokenCaption(token) {
|
|
1117
|
-
if (token.type === 13 /* TokenTypes.EOF */) {
|
|
1118
|
-
return 'EOF';
|
|
1119
|
-
}
|
|
1120
|
-
const name = (token.value || '').replace(/\r?\n/gu, '\\n');
|
|
1121
|
-
return name.length > 10 ? name.slice(0, 9) + '…' : name;
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
function createTransformer(ast, options = {} // eslint-disable-line
|
|
1125
|
-
) {
|
|
1126
|
-
const _context = {
|
|
1127
|
-
ast,
|
|
1128
|
-
helpers: new Set()
|
|
1129
|
-
};
|
|
1130
|
-
const context = () => _context;
|
|
1131
|
-
const helper = (name) => {
|
|
1132
|
-
_context.helpers.add(name);
|
|
1133
|
-
return name;
|
|
1134
|
-
};
|
|
1135
|
-
return { context, helper };
|
|
1136
|
-
}
|
|
1137
|
-
function traverseNodes(nodes, transformer) {
|
|
1138
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
1139
|
-
traverseNode(nodes[i], transformer);
|
|
1166
|
+
return token;
|
|
1140
1167
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
break;
|
|
1152
|
-
case 6 /* NodeTypes.Linked */: {
|
|
1153
|
-
const linked = node;
|
|
1154
|
-
traverseNode(linked.key, transformer);
|
|
1155
|
-
transformer.helper("linked" /* HelperNameMap.LINKED */);
|
|
1156
|
-
transformer.helper("type" /* HelperNameMap.TYPE */);
|
|
1157
|
-
break;
|
|
1168
|
+
function nextToken() {
|
|
1169
|
+
const { currentType, offset, startLoc, endLoc } = _context;
|
|
1170
|
+
_context.lastType = currentType;
|
|
1171
|
+
_context.lastOffset = offset;
|
|
1172
|
+
_context.lastStartLoc = startLoc;
|
|
1173
|
+
_context.lastEndLoc = endLoc;
|
|
1174
|
+
_context.offset = currentOffset();
|
|
1175
|
+
_context.startLoc = currentPosition();
|
|
1176
|
+
if (_scnr.currentChar() === EOF) {
|
|
1177
|
+
return getToken(_context, 13 /* TokenTypes.EOF */);
|
|
1158
1178
|
}
|
|
1159
|
-
|
|
1160
|
-
transformer.helper("interpolate" /* HelperNameMap.INTERPOLATE */);
|
|
1161
|
-
transformer.helper("list" /* HelperNameMap.LIST */);
|
|
1162
|
-
break;
|
|
1163
|
-
case 4 /* NodeTypes.Named */:
|
|
1164
|
-
transformer.helper("interpolate" /* HelperNameMap.INTERPOLATE */);
|
|
1165
|
-
transformer.helper("named" /* HelperNameMap.NAMED */);
|
|
1166
|
-
break;
|
|
1179
|
+
return readToken(_scnr, _context);
|
|
1167
1180
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
transformer.helper("normalize" /* HelperNameMap.NORMALIZE */);
|
|
1175
|
-
// traverse
|
|
1176
|
-
ast.body && traverseNode(ast.body, transformer);
|
|
1177
|
-
// set meta information
|
|
1178
|
-
const context = transformer.context();
|
|
1179
|
-
ast.helpers = Array.from(context.helpers);
|
|
1181
|
+
return {
|
|
1182
|
+
nextToken,
|
|
1183
|
+
currentOffset,
|
|
1184
|
+
currentPosition,
|
|
1185
|
+
context
|
|
1186
|
+
};
|
|
1180
1187
|
}
|
|
1181
1188
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
delete item.value; // optimization for size
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
else {
|
|
1201
|
-
const values = [];
|
|
1202
|
-
for (let i = 0; i < message.items.length; i++) {
|
|
1203
|
-
const item = message.items[i];
|
|
1204
|
-
if (!(item.type === 3 /* NodeTypes.Text */ || item.type === 9 /* NodeTypes.Literal */)) {
|
|
1205
|
-
break;
|
|
1206
|
-
}
|
|
1207
|
-
if (item.value == null) {
|
|
1208
|
-
break;
|
|
1209
|
-
}
|
|
1210
|
-
values.push(item.value);
|
|
1211
|
-
}
|
|
1212
|
-
if (values.length === message.items.length) {
|
|
1213
|
-
message.static = join(values);
|
|
1214
|
-
for (let i = 0; i < message.items.length; i++) {
|
|
1215
|
-
const item = message.items[i];
|
|
1216
|
-
if (item.type === 3 /* NodeTypes.Text */ || item.type === 9 /* NodeTypes.Literal */) {
|
|
1217
|
-
delete item.value; // optimization for size
|
|
1218
|
-
}
|
|
1189
|
+
const ERROR_DOMAIN = 'parser';
|
|
1190
|
+
// Backslash backslash, backslash quote, uHHHH, UHHHHHH.
|
|
1191
|
+
const KNOWN_ESCAPES = /(?:\\\\|\\'|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;
|
|
1192
|
+
function fromEscapeSequence(match, codePoint4, codePoint6) {
|
|
1193
|
+
switch (match) {
|
|
1194
|
+
case `\\\\`:
|
|
1195
|
+
return `\\`;
|
|
1196
|
+
// eslint-disable-next-line no-useless-escape
|
|
1197
|
+
case `\\\'`:
|
|
1198
|
+
// eslint-disable-next-line no-useless-escape
|
|
1199
|
+
return `\'`;
|
|
1200
|
+
default: {
|
|
1201
|
+
const codePoint = parseInt(codePoint4 || codePoint6, 16);
|
|
1202
|
+
if (codePoint <= 0xd7ff || codePoint >= 0xe000) {
|
|
1203
|
+
return String.fromCodePoint(codePoint);
|
|
1219
1204
|
}
|
|
1205
|
+
// invalid ...
|
|
1206
|
+
// Replace them with U+FFFD REPLACEMENT CHARACTER.
|
|
1207
|
+
return '�';
|
|
1220
1208
|
}
|
|
1221
1209
|
}
|
|
1222
1210
|
}
|
|
1223
|
-
|
|
1224
|
-
const
|
|
1225
|
-
|
|
1226
|
-
function
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
const plural = node;
|
|
1238
|
-
const cases = plural.cases;
|
|
1239
|
-
for (let i = 0; i < cases.length; i++) {
|
|
1240
|
-
minify(cases[i]);
|
|
1241
|
-
}
|
|
1242
|
-
plural.c = cases;
|
|
1243
|
-
delete plural.cases;
|
|
1244
|
-
break;
|
|
1245
|
-
}
|
|
1246
|
-
case 2 /* NodeTypes.Message */: {
|
|
1247
|
-
const message = node;
|
|
1248
|
-
const items = message.items;
|
|
1249
|
-
for (let i = 0; i < items.length; i++) {
|
|
1250
|
-
minify(items[i]);
|
|
1251
|
-
}
|
|
1252
|
-
message.i = items;
|
|
1253
|
-
delete message.items;
|
|
1254
|
-
if (message.static) {
|
|
1255
|
-
message.s = message.static;
|
|
1256
|
-
delete message.static;
|
|
1257
|
-
}
|
|
1258
|
-
break;
|
|
1259
|
-
}
|
|
1260
|
-
case 3 /* NodeTypes.Text */:
|
|
1261
|
-
case 9 /* NodeTypes.Literal */:
|
|
1262
|
-
case 8 /* NodeTypes.LinkedModifier */:
|
|
1263
|
-
case 7 /* NodeTypes.LinkedKey */: {
|
|
1264
|
-
const valueNode = node;
|
|
1265
|
-
if (valueNode.value) {
|
|
1266
|
-
valueNode.v = valueNode.value;
|
|
1267
|
-
delete valueNode.value;
|
|
1268
|
-
}
|
|
1269
|
-
break;
|
|
1270
|
-
}
|
|
1271
|
-
case 6 /* NodeTypes.Linked */: {
|
|
1272
|
-
const linked = node;
|
|
1273
|
-
minify(linked.key);
|
|
1274
|
-
linked.k = linked.key;
|
|
1275
|
-
delete linked.key;
|
|
1276
|
-
if (linked.modifier) {
|
|
1277
|
-
minify(linked.modifier);
|
|
1278
|
-
linked.m = linked.modifier;
|
|
1279
|
-
delete linked.modifier;
|
|
1280
|
-
}
|
|
1281
|
-
break;
|
|
1282
|
-
}
|
|
1283
|
-
case 5 /* NodeTypes.List */: {
|
|
1284
|
-
const list = node;
|
|
1285
|
-
list.i = list.index;
|
|
1286
|
-
delete list.index;
|
|
1287
|
-
break;
|
|
1288
|
-
}
|
|
1289
|
-
case 4 /* NodeTypes.Named */: {
|
|
1290
|
-
const named = node;
|
|
1291
|
-
named.k = named.key;
|
|
1292
|
-
delete named.key;
|
|
1293
|
-
break;
|
|
1211
|
+
function createParser(options = {}) {
|
|
1212
|
+
const location = options.location !== false;
|
|
1213
|
+
const { onError } = options;
|
|
1214
|
+
function emitError(tokenzer, code, start, offset, ...args) {
|
|
1215
|
+
const end = tokenzer.currentPosition();
|
|
1216
|
+
end.offset += offset;
|
|
1217
|
+
end.column += offset;
|
|
1218
|
+
if (onError) {
|
|
1219
|
+
const loc = location ? createLocation(start, end) : null;
|
|
1220
|
+
const err = createCompileError(code, loc, {
|
|
1221
|
+
domain: ERROR_DOMAIN,
|
|
1222
|
+
args
|
|
1223
|
+
});
|
|
1224
|
+
onError(err);
|
|
1294
1225
|
}
|
|
1295
|
-
default:
|
|
1296
|
-
if ((process.env.NODE_ENV !== 'production')) {
|
|
1297
|
-
throw createCompileError(CompileErrorCodes.UNHANDLED_MINIFIER_NODE_TYPE, null, {
|
|
1298
|
-
domain: ERROR_DOMAIN$1,
|
|
1299
|
-
args: [node.type]
|
|
1300
|
-
});
|
|
1301
|
-
}
|
|
1302
1226
|
}
|
|
1303
|
-
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
const { sourceMap, filename, breakLineCode, needIndent: _needIndent } = options;
|
|
1312
|
-
const location = options.location !== false;
|
|
1313
|
-
const _context = {
|
|
1314
|
-
filename,
|
|
1315
|
-
code: '',
|
|
1316
|
-
column: 1,
|
|
1317
|
-
line: 1,
|
|
1318
|
-
offset: 0,
|
|
1319
|
-
map: undefined,
|
|
1320
|
-
breakLineCode,
|
|
1321
|
-
needIndent: _needIndent,
|
|
1322
|
-
indentLevel: 0
|
|
1323
|
-
};
|
|
1324
|
-
if (location && ast.loc) {
|
|
1325
|
-
_context.source = ast.loc.source;
|
|
1227
|
+
function startNode(type, offset, loc) {
|
|
1228
|
+
const node = { type };
|
|
1229
|
+
if (location) {
|
|
1230
|
+
node.start = offset;
|
|
1231
|
+
node.end = offset;
|
|
1232
|
+
node.loc = { start: loc, end: loc };
|
|
1233
|
+
}
|
|
1234
|
+
return node;
|
|
1326
1235
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
addMapping(node.loc.start, getMappingName(node));
|
|
1236
|
+
function endNode(node, offset, pos, type) {
|
|
1237
|
+
if (location) {
|
|
1238
|
+
node.end = offset;
|
|
1239
|
+
if (node.loc) {
|
|
1240
|
+
node.loc.end = pos;
|
|
1333
1241
|
}
|
|
1334
|
-
advancePositionWithSource(_context, code);
|
|
1335
1242
|
}
|
|
1336
1243
|
}
|
|
1337
|
-
function
|
|
1338
|
-
const
|
|
1339
|
-
|
|
1244
|
+
function parseText(tokenizer, value) {
|
|
1245
|
+
const context = tokenizer.context();
|
|
1246
|
+
const node = startNode(3 /* NodeTypes.Text */, context.offset, context.startLoc);
|
|
1247
|
+
node.value = value;
|
|
1248
|
+
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
1249
|
+
return node;
|
|
1340
1250
|
}
|
|
1341
|
-
function
|
|
1342
|
-
const
|
|
1343
|
-
|
|
1251
|
+
function parseList(tokenizer, index) {
|
|
1252
|
+
const context = tokenizer.context();
|
|
1253
|
+
const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
|
|
1254
|
+
const node = startNode(5 /* NodeTypes.List */, offset, loc);
|
|
1255
|
+
node.index = parseInt(index, 10);
|
|
1256
|
+
tokenizer.nextToken(); // skip brach right
|
|
1257
|
+
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
1258
|
+
return node;
|
|
1344
1259
|
}
|
|
1345
|
-
function
|
|
1346
|
-
const
|
|
1347
|
-
|
|
1260
|
+
function parseNamed(tokenizer, key) {
|
|
1261
|
+
const context = tokenizer.context();
|
|
1262
|
+
const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
|
|
1263
|
+
const node = startNode(4 /* NodeTypes.Named */, offset, loc);
|
|
1264
|
+
node.key = key;
|
|
1265
|
+
tokenizer.nextToken(); // skip brach right
|
|
1266
|
+
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
1267
|
+
return node;
|
|
1348
1268
|
}
|
|
1349
|
-
function
|
|
1350
|
-
|
|
1269
|
+
function parseLiteral(tokenizer, value) {
|
|
1270
|
+
const context = tokenizer.context();
|
|
1271
|
+
const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
|
|
1272
|
+
const node = startNode(9 /* NodeTypes.Literal */, offset, loc);
|
|
1273
|
+
node.value = value.replace(KNOWN_ESCAPES, fromEscapeSequence);
|
|
1274
|
+
tokenizer.nextToken(); // skip brach right
|
|
1275
|
+
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
1276
|
+
return node;
|
|
1351
1277
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1278
|
+
function parseLinkedModifier(tokenizer) {
|
|
1279
|
+
const token = tokenizer.nextToken();
|
|
1280
|
+
const context = tokenizer.context();
|
|
1281
|
+
const { lastOffset: offset, lastStartLoc: loc } = context; // get linked dot loc
|
|
1282
|
+
const node = startNode(8 /* NodeTypes.LinkedModifier */, offset, loc);
|
|
1283
|
+
if (token.type !== 11 /* TokenTypes.LinkedModifier */) {
|
|
1284
|
+
// empty modifier
|
|
1285
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_EMPTY_LINKED_MODIFIER, context.lastStartLoc, 0);
|
|
1286
|
+
node.value = '';
|
|
1287
|
+
endNode(node, offset, loc);
|
|
1288
|
+
return {
|
|
1289
|
+
nextConsumeToken: token,
|
|
1290
|
+
node
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
// check token
|
|
1294
|
+
if (token.value == null) {
|
|
1295
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1296
|
+
}
|
|
1297
|
+
node.value = token.value || '';
|
|
1298
|
+
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
1299
|
+
return {
|
|
1300
|
+
node
|
|
1301
|
+
};
|
|
1367
1302
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1303
|
+
function parseLinkedKey(tokenizer, value) {
|
|
1304
|
+
const context = tokenizer.context();
|
|
1305
|
+
const node = startNode(7 /* NodeTypes.LinkedKey */, context.offset, context.startLoc);
|
|
1306
|
+
node.value = value;
|
|
1307
|
+
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
1308
|
+
return node;
|
|
1371
1309
|
}
|
|
1372
|
-
|
|
1373
|
-
context
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1310
|
+
function parseLinked(tokenizer) {
|
|
1311
|
+
const context = tokenizer.context();
|
|
1312
|
+
const linkedNode = startNode(6 /* NodeTypes.Linked */, context.offset, context.startLoc);
|
|
1313
|
+
let token = tokenizer.nextToken();
|
|
1314
|
+
if (token.type === 8 /* TokenTypes.LinkedDot */) {
|
|
1315
|
+
const parsed = parseLinkedModifier(tokenizer);
|
|
1316
|
+
linkedNode.modifier = parsed.node;
|
|
1317
|
+
token = parsed.nextConsumeToken || tokenizer.nextToken();
|
|
1318
|
+
}
|
|
1319
|
+
// asset check token
|
|
1320
|
+
if (token.type !== 9 /* TokenTypes.LinkedDelimiter */) {
|
|
1321
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1322
|
+
}
|
|
1323
|
+
token = tokenizer.nextToken();
|
|
1324
|
+
// skip brace left
|
|
1325
|
+
if (token.type === 2 /* TokenTypes.BraceLeft */) {
|
|
1326
|
+
token = tokenizer.nextToken();
|
|
1327
|
+
}
|
|
1328
|
+
switch (token.type) {
|
|
1329
|
+
case 10 /* TokenTypes.LinkedKey */:
|
|
1330
|
+
if (token.value == null) {
|
|
1331
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1332
|
+
}
|
|
1333
|
+
linkedNode.key = parseLinkedKey(tokenizer, token.value || '');
|
|
1334
|
+
break;
|
|
1335
|
+
case 4 /* TokenTypes.Named */:
|
|
1336
|
+
if (token.value == null) {
|
|
1337
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1338
|
+
}
|
|
1339
|
+
linkedNode.key = parseNamed(tokenizer, token.value || '');
|
|
1340
|
+
break;
|
|
1341
|
+
case 5 /* TokenTypes.List */:
|
|
1342
|
+
if (token.value == null) {
|
|
1343
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1344
|
+
}
|
|
1345
|
+
linkedNode.key = parseList(tokenizer, token.value || '');
|
|
1346
|
+
break;
|
|
1347
|
+
case 6 /* TokenTypes.Literal */:
|
|
1348
|
+
if (token.value == null) {
|
|
1349
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1350
|
+
}
|
|
1351
|
+
linkedNode.key = parseLiteral(tokenizer, token.value || '');
|
|
1352
|
+
break;
|
|
1353
|
+
default: {
|
|
1354
|
+
// empty key
|
|
1355
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_EMPTY_LINKED_KEY, context.lastStartLoc, 0);
|
|
1356
|
+
const nextContext = tokenizer.context();
|
|
1357
|
+
const emptyLinkedKeyNode = startNode(7 /* NodeTypes.LinkedKey */, nextContext.offset, nextContext.startLoc);
|
|
1358
|
+
emptyLinkedKeyNode.value = '';
|
|
1359
|
+
endNode(emptyLinkedKeyNode, nextContext.offset, nextContext.startLoc);
|
|
1360
|
+
linkedNode.key = emptyLinkedKeyNode;
|
|
1361
|
+
endNode(linkedNode, nextContext.offset, nextContext.startLoc);
|
|
1362
|
+
return {
|
|
1363
|
+
nextConsumeToken: token,
|
|
1364
|
+
node: linkedNode
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
endNode(linkedNode, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
1369
|
+
return {
|
|
1370
|
+
node: linkedNode
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
function parseMessage(tokenizer) {
|
|
1374
|
+
const context = tokenizer.context();
|
|
1375
|
+
const startOffset = context.currentType === 1 /* TokenTypes.Pipe */
|
|
1376
|
+
? tokenizer.currentOffset()
|
|
1377
|
+
: context.offset;
|
|
1378
|
+
const startLoc = context.currentType === 1 /* TokenTypes.Pipe */
|
|
1379
|
+
? context.endLoc
|
|
1380
|
+
: context.startLoc;
|
|
1381
|
+
const node = startNode(2 /* NodeTypes.Message */, startOffset, startLoc);
|
|
1382
|
+
node.items = [];
|
|
1383
|
+
let nextToken = null;
|
|
1384
|
+
do {
|
|
1385
|
+
const token = nextToken || tokenizer.nextToken();
|
|
1386
|
+
nextToken = null;
|
|
1387
|
+
switch (token.type) {
|
|
1388
|
+
case 0 /* TokenTypes.Text */:
|
|
1389
|
+
if (token.value == null) {
|
|
1390
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1391
|
+
}
|
|
1392
|
+
node.items.push(parseText(tokenizer, token.value || ''));
|
|
1393
|
+
break;
|
|
1394
|
+
case 5 /* TokenTypes.List */:
|
|
1395
|
+
if (token.value == null) {
|
|
1396
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1397
|
+
}
|
|
1398
|
+
node.items.push(parseList(tokenizer, token.value || ''));
|
|
1399
|
+
break;
|
|
1400
|
+
case 4 /* TokenTypes.Named */:
|
|
1401
|
+
if (token.value == null) {
|
|
1402
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1403
|
+
}
|
|
1404
|
+
node.items.push(parseNamed(tokenizer, token.value || ''));
|
|
1405
|
+
break;
|
|
1406
|
+
case 6 /* TokenTypes.Literal */:
|
|
1407
|
+
if (token.value == null) {
|
|
1408
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
|
|
1409
|
+
}
|
|
1410
|
+
node.items.push(parseLiteral(tokenizer, token.value || ''));
|
|
1411
|
+
break;
|
|
1412
|
+
case 7 /* TokenTypes.LinkedAlias */: {
|
|
1413
|
+
const parsed = parseLinked(tokenizer);
|
|
1414
|
+
node.items.push(parsed.node);
|
|
1415
|
+
nextToken = parsed.nextConsumeToken || null;
|
|
1416
|
+
break;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
} while (context.currentType !== 13 /* TokenTypes.EOF */ &&
|
|
1420
|
+
context.currentType !== 1 /* TokenTypes.Pipe */);
|
|
1421
|
+
// adjust message node loc
|
|
1422
|
+
const endOffset = context.currentType === 1 /* TokenTypes.Pipe */
|
|
1423
|
+
? context.lastOffset
|
|
1424
|
+
: tokenizer.currentOffset();
|
|
1425
|
+
const endLoc = context.currentType === 1 /* TokenTypes.Pipe */
|
|
1426
|
+
? context.lastEndLoc
|
|
1427
|
+
: tokenizer.currentPosition();
|
|
1428
|
+
endNode(node, endOffset, endLoc);
|
|
1429
|
+
return node;
|
|
1390
1430
|
}
|
|
1391
|
-
|
|
1392
|
-
|
|
1431
|
+
function parsePlural(tokenizer, offset, loc, msgNode) {
|
|
1432
|
+
const context = tokenizer.context();
|
|
1433
|
+
let hasEmptyMessage = msgNode.items.length === 0;
|
|
1434
|
+
const node = startNode(1 /* NodeTypes.Plural */, offset, loc);
|
|
1435
|
+
node.cases = [];
|
|
1436
|
+
node.cases.push(msgNode);
|
|
1437
|
+
do {
|
|
1438
|
+
const msg = parseMessage(tokenizer);
|
|
1439
|
+
if (!hasEmptyMessage) {
|
|
1440
|
+
hasEmptyMessage = msg.items.length === 0;
|
|
1441
|
+
}
|
|
1442
|
+
node.cases.push(msg);
|
|
1443
|
+
} while (context.currentType !== 13 /* TokenTypes.EOF */);
|
|
1444
|
+
if (hasEmptyMessage) {
|
|
1445
|
+
emitError(tokenizer, CompileErrorCodes.MUST_HAVE_MESSAGES_IN_PLURAL, loc, 0);
|
|
1446
|
+
}
|
|
1447
|
+
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
1448
|
+
return node;
|
|
1393
1449
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
if (i === length - 1) {
|
|
1404
|
-
break;
|
|
1450
|
+
function parseResource(tokenizer) {
|
|
1451
|
+
const context = tokenizer.context();
|
|
1452
|
+
const { offset, startLoc } = context;
|
|
1453
|
+
const msgNode = parseMessage(tokenizer);
|
|
1454
|
+
if (context.currentType === 13 /* TokenTypes.EOF */) {
|
|
1455
|
+
return msgNode;
|
|
1456
|
+
}
|
|
1457
|
+
else {
|
|
1458
|
+
return parsePlural(tokenizer, offset, startLoc, msgNode);
|
|
1405
1459
|
}
|
|
1406
|
-
generator.push(', ');
|
|
1407
1460
|
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
generator.push(`${helper("plural" /* HelperNameMap.PLURAL */)}([`);
|
|
1415
|
-
generator.indent(needIndent());
|
|
1416
|
-
const length = node.cases.length;
|
|
1417
|
-
for (let i = 0; i < length; i++) {
|
|
1418
|
-
generateNode(generator, node.cases[i]);
|
|
1419
|
-
if (i === length - 1) {
|
|
1420
|
-
break;
|
|
1421
|
-
}
|
|
1422
|
-
generator.push(', ');
|
|
1461
|
+
function parse(source) {
|
|
1462
|
+
const tokenizer = createTokenizer(source, assign({}, options));
|
|
1463
|
+
const context = tokenizer.context();
|
|
1464
|
+
const node = startNode(0 /* NodeTypes.Resource */, context.offset, context.startLoc);
|
|
1465
|
+
if (location && node.loc) {
|
|
1466
|
+
node.loc.source = source;
|
|
1423
1467
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1468
|
+
node.body = parseResource(tokenizer);
|
|
1469
|
+
if (options.onCacheKey) {
|
|
1470
|
+
node.cacheKey = options.onCacheKey(source);
|
|
1471
|
+
}
|
|
1472
|
+
// assert whether achieved to EOF
|
|
1473
|
+
if (context.currentType !== 13 /* TokenTypes.EOF */) {
|
|
1474
|
+
emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, source[context.offset] || '');
|
|
1475
|
+
}
|
|
1476
|
+
endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
|
|
1477
|
+
return node;
|
|
1426
1478
|
}
|
|
1479
|
+
return { parse };
|
|
1427
1480
|
}
|
|
1428
|
-
function
|
|
1429
|
-
if (
|
|
1430
|
-
|
|
1481
|
+
function getTokenCaption(token) {
|
|
1482
|
+
if (token.type === 13 /* TokenTypes.EOF */) {
|
|
1483
|
+
return 'EOF';
|
|
1431
1484
|
}
|
|
1432
|
-
|
|
1433
|
-
|
|
1485
|
+
const name = (token.value || '').replace(/\r?\n/gu, '\\n');
|
|
1486
|
+
return name.length > 10 ? name.slice(0, 9) + '…' : name;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
function createTransformer(ast, options = {} // eslint-disable-line
|
|
1490
|
+
) {
|
|
1491
|
+
const _context = {
|
|
1492
|
+
ast,
|
|
1493
|
+
helpers: new Set()
|
|
1494
|
+
};
|
|
1495
|
+
const context = () => _context;
|
|
1496
|
+
const helper = (name) => {
|
|
1497
|
+
_context.helpers.add(name);
|
|
1498
|
+
return name;
|
|
1499
|
+
};
|
|
1500
|
+
return { context, helper };
|
|
1501
|
+
}
|
|
1502
|
+
function traverseNodes(nodes, transformer) {
|
|
1503
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
1504
|
+
traverseNode(nodes[i], transformer);
|
|
1434
1505
|
}
|
|
1435
1506
|
}
|
|
1436
|
-
function
|
|
1437
|
-
|
|
1507
|
+
function traverseNode(node, transformer) {
|
|
1508
|
+
// TODO: if we need pre-hook of transform, should be implemented to here
|
|
1438
1509
|
switch (node.type) {
|
|
1439
|
-
case 0 /* NodeTypes.Resource */:
|
|
1440
|
-
generateResource(generator, node);
|
|
1441
|
-
break;
|
|
1442
1510
|
case 1 /* NodeTypes.Plural */:
|
|
1443
|
-
|
|
1511
|
+
traverseNodes(node.cases, transformer);
|
|
1512
|
+
transformer.helper("plural" /* HelperNameMap.PLURAL */);
|
|
1444
1513
|
break;
|
|
1445
1514
|
case 2 /* NodeTypes.Message */:
|
|
1446
|
-
|
|
1447
|
-
break;
|
|
1448
|
-
case 6 /* NodeTypes.Linked */:
|
|
1449
|
-
generateLinkedNode(generator, node);
|
|
1450
|
-
break;
|
|
1451
|
-
case 8 /* NodeTypes.LinkedModifier */:
|
|
1452
|
-
generator.push(JSON.stringify(node.value), node);
|
|
1515
|
+
traverseNodes(node.items, transformer);
|
|
1453
1516
|
break;
|
|
1454
|
-
case
|
|
1455
|
-
|
|
1517
|
+
case 6 /* NodeTypes.Linked */: {
|
|
1518
|
+
const linked = node;
|
|
1519
|
+
traverseNode(linked.key, transformer);
|
|
1520
|
+
transformer.helper("linked" /* HelperNameMap.LINKED */);
|
|
1521
|
+
transformer.helper("type" /* HelperNameMap.TYPE */);
|
|
1456
1522
|
break;
|
|
1523
|
+
}
|
|
1457
1524
|
case 5 /* NodeTypes.List */:
|
|
1458
|
-
|
|
1525
|
+
transformer.helper("interpolate" /* HelperNameMap.INTERPOLATE */);
|
|
1526
|
+
transformer.helper("list" /* HelperNameMap.LIST */);
|
|
1459
1527
|
break;
|
|
1460
1528
|
case 4 /* NodeTypes.Named */:
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
case 9 /* NodeTypes.Literal */:
|
|
1464
|
-
generator.push(JSON.stringify(node.value), node);
|
|
1465
|
-
break;
|
|
1466
|
-
case 3 /* NodeTypes.Text */:
|
|
1467
|
-
generator.push(JSON.stringify(node.value), node);
|
|
1529
|
+
transformer.helper("interpolate" /* HelperNameMap.INTERPOLATE */);
|
|
1530
|
+
transformer.helper("named" /* HelperNameMap.NAMED */);
|
|
1468
1531
|
break;
|
|
1469
|
-
default:
|
|
1470
|
-
if ((process.env.NODE_ENV !== 'production')) {
|
|
1471
|
-
throw createCompileError(CompileErrorCodes.UNHANDLED_CODEGEN_NODE_TYPE, null, {
|
|
1472
|
-
domain: ERROR_DOMAIN,
|
|
1473
|
-
args: [node.type]
|
|
1474
|
-
});
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
// generate code from AST
|
|
1479
|
-
const generate = (ast, options = {}) => {
|
|
1480
|
-
const mode = isString(options.mode) ? options.mode : 'normal';
|
|
1481
|
-
const filename = isString(options.filename)
|
|
1482
|
-
? options.filename
|
|
1483
|
-
: 'message.intl';
|
|
1484
|
-
const sourceMap = !!options.sourceMap;
|
|
1485
|
-
// prettier-ignore
|
|
1486
|
-
const breakLineCode = options.breakLineCode != null
|
|
1487
|
-
? options.breakLineCode
|
|
1488
|
-
: mode === 'arrow'
|
|
1489
|
-
? ';'
|
|
1490
|
-
: '\n';
|
|
1491
|
-
const needIndent = options.needIndent ? options.needIndent : mode !== 'arrow';
|
|
1492
|
-
const helpers = ast.helpers || [];
|
|
1493
|
-
const generator = createCodeGenerator(ast, {
|
|
1494
|
-
mode,
|
|
1495
|
-
filename,
|
|
1496
|
-
sourceMap,
|
|
1497
|
-
breakLineCode,
|
|
1498
|
-
needIndent
|
|
1499
|
-
});
|
|
1500
|
-
generator.push(mode === 'normal' ? `function __msg__ (ctx) {` : `(ctx) => {`);
|
|
1501
|
-
generator.indent(needIndent);
|
|
1502
|
-
if (helpers.length > 0) {
|
|
1503
|
-
generator.push(`const { ${join(helpers.map(s => `${s}: _${s}`), ', ')} } = ctx`);
|
|
1504
|
-
generator.newline();
|
|
1505
|
-
}
|
|
1506
|
-
generator.push(`return `);
|
|
1507
|
-
generateNode(generator, ast);
|
|
1508
|
-
generator.deindent(needIndent);
|
|
1509
|
-
generator.push(`}`);
|
|
1510
|
-
delete ast.helpers;
|
|
1511
|
-
const { code, map } = generator.context();
|
|
1512
|
-
return {
|
|
1513
|
-
ast,
|
|
1514
|
-
code,
|
|
1515
|
-
map: map ? map.toJSON() : undefined // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
1516
|
-
};
|
|
1517
|
-
};
|
|
1518
|
-
function getMappingName(node) {
|
|
1519
|
-
switch (node.type) {
|
|
1520
|
-
case 3 /* NodeTypes.Text */:
|
|
1521
|
-
case 9 /* NodeTypes.Literal */:
|
|
1522
|
-
case 8 /* NodeTypes.LinkedModifier */:
|
|
1523
|
-
case 7 /* NodeTypes.LinkedKey */:
|
|
1524
|
-
return node.value;
|
|
1525
|
-
case 5 /* NodeTypes.List */:
|
|
1526
|
-
return node.index.toString();
|
|
1527
|
-
case 4 /* NodeTypes.Named */:
|
|
1528
|
-
return node.key;
|
|
1529
|
-
default:
|
|
1530
|
-
return undefined;
|
|
1531
1532
|
}
|
|
1533
|
+
// TODO: if we need post-hook of transform, should be implemented to here
|
|
1532
1534
|
}
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
pos.line += linesCount;
|
|
1544
|
-
pos.column =
|
|
1545
|
-
lastNewLinePos === -1
|
|
1546
|
-
? pos.column + numberOfCharacters
|
|
1547
|
-
: numberOfCharacters - lastNewLinePos;
|
|
1548
|
-
return pos;
|
|
1535
|
+
// transform AST
|
|
1536
|
+
function transform(ast, options = {} // eslint-disable-line
|
|
1537
|
+
) {
|
|
1538
|
+
const transformer = createTransformer(ast);
|
|
1539
|
+
transformer.helper("normalize" /* HelperNameMap.NORMALIZE */);
|
|
1540
|
+
// traverse
|
|
1541
|
+
ast.body && traverseNode(ast.body, transformer);
|
|
1542
|
+
// set meta information
|
|
1543
|
+
const context = transformer.context();
|
|
1544
|
+
ast.helpers = Array.from(context.helpers);
|
|
1549
1545
|
}
|
|
1550
1546
|
|
|
1551
1547
|
function baseCompile(source, options = {}) {
|
|
1552
1548
|
const assignedOptions = assign({}, options);
|
|
1553
1549
|
const jit = !!assignedOptions.jit;
|
|
1554
|
-
const
|
|
1555
|
-
const
|
|
1550
|
+
const enableMangle = !!assignedOptions.mangle;
|
|
1551
|
+
const enableOptimize = assignedOptions.optimize == null ? true : assignedOptions.optimize;
|
|
1556
1552
|
// parse source codes
|
|
1557
1553
|
const parser = createParser(assignedOptions);
|
|
1558
1554
|
const ast = parser.parse(source);
|
|
1555
|
+
// TODO:
|
|
1556
|
+
// With the introduction of Jit compilation, code generation is no longer necessary. This function may no longer be needed since tree-shaking is not possible.
|
|
1559
1557
|
if (!jit) {
|
|
1560
1558
|
// transform ASTs
|
|
1561
1559
|
transform(ast, assignedOptions);
|
|
@@ -1564,12 +1562,16 @@ function baseCompile(source, options = {}) {
|
|
|
1564
1562
|
}
|
|
1565
1563
|
else {
|
|
1566
1564
|
// optimize ASTs
|
|
1567
|
-
|
|
1565
|
+
enableOptimize && optimize(ast);
|
|
1568
1566
|
// minimize ASTs
|
|
1569
|
-
|
|
1567
|
+
enableMangle && mangle(ast);
|
|
1570
1568
|
// In JIT mode, no ast transform, no code generation.
|
|
1571
1569
|
return { ast, code: '' };
|
|
1572
1570
|
}
|
|
1573
1571
|
}
|
|
1574
1572
|
|
|
1575
|
-
|
|
1573
|
+
// eslint-disable-next-line no-useless-escape
|
|
1574
|
+
const RE_HTML_TAG = /<\/?[\w\s="/.':;#-\/]+>/;
|
|
1575
|
+
const detectHtmlTag = (source) => RE_HTML_TAG.test(source);
|
|
1576
|
+
|
|
1577
|
+
export { COMPILE_ERROR_CODES_EXTEND_POINT, CompileErrorCodes, ERROR_DOMAIN, LOCATION_STUB, baseCompile, createCompileError, createLocation, createParser, createPosition, defaultOnError, detectHtmlTag, errorMessages, mangle, optimize };
|