@intlify/message-compiler 9.3.0-beta.9 → 9.4.0

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