版博士V2.0程序
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

message-compiler.global.js 55 KiB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411
  1. /*!
  2. * message-compiler v9.3.0-beta.17
  3. * (c) 2023 kazuya kawaguchi
  4. * Released under the MIT License.
  5. */
  6. var IntlifyMessageCompiler = (function (exports) {
  7. 'use strict';
  8. /**
  9. * Original Utilities
  10. * written by kazuya kawaguchi
  11. */
  12. const RE_ARGS = /\{([0-9a-zA-Z]+)\}/g;
  13. /* eslint-disable */
  14. function format(message, ...args) {
  15. if (args.length === 1 && isObject(args[0])) {
  16. args = args[0];
  17. }
  18. if (!args || !args.hasOwnProperty) {
  19. args = {};
  20. }
  21. return message.replace(RE_ARGS, (match, identifier) => {
  22. return args.hasOwnProperty(identifier) ? args[identifier] : '';
  23. });
  24. }
  25. const assign = Object.assign;
  26. const isString = (val) => typeof val === 'string';
  27. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  28. const isObject = (val) => val !== null && typeof val === 'object';
  29. const CompileErrorCodes = {
  30. // tokenizer error codes
  31. EXPECTED_TOKEN: 1,
  32. INVALID_TOKEN_IN_PLACEHOLDER: 2,
  33. UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER: 3,
  34. UNKNOWN_ESCAPE_SEQUENCE: 4,
  35. INVALID_UNICODE_ESCAPE_SEQUENCE: 5,
  36. UNBALANCED_CLOSING_BRACE: 6,
  37. UNTERMINATED_CLOSING_BRACE: 7,
  38. EMPTY_PLACEHOLDER: 8,
  39. NOT_ALLOW_NEST_PLACEHOLDER: 9,
  40. INVALID_LINKED_FORMAT: 10,
  41. // parser error codes
  42. MUST_HAVE_MESSAGES_IN_PLURAL: 11,
  43. UNEXPECTED_EMPTY_LINKED_MODIFIER: 12,
  44. UNEXPECTED_EMPTY_LINKED_KEY: 13,
  45. UNEXPECTED_LEXICAL_ANALYSIS: 14,
  46. // Special value for higher-order compilers to pick up the last code
  47. // to avoid collision of error codes. This should always be kept as the last
  48. // item.
  49. __EXTEND_POINT__: 15
  50. };
  51. /** @internal */
  52. const errorMessages = {
  53. // tokenizer error messages
  54. [CompileErrorCodes.EXPECTED_TOKEN]: `Expected token: '{0}'`,
  55. [CompileErrorCodes.INVALID_TOKEN_IN_PLACEHOLDER]: `Invalid token in placeholder: '{0}'`,
  56. [CompileErrorCodes.UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER]: `Unterminated single quote in placeholder`,
  57. [CompileErrorCodes.UNKNOWN_ESCAPE_SEQUENCE]: `Unknown escape sequence: \\{0}`,
  58. [CompileErrorCodes.INVALID_UNICODE_ESCAPE_SEQUENCE]: `Invalid unicode escape sequence: {0}`,
  59. [CompileErrorCodes.UNBALANCED_CLOSING_BRACE]: `Unbalanced closing brace`,
  60. [CompileErrorCodes.UNTERMINATED_CLOSING_BRACE]: `Unterminated closing brace`,
  61. [CompileErrorCodes.EMPTY_PLACEHOLDER]: `Empty placeholder`,
  62. [CompileErrorCodes.NOT_ALLOW_NEST_PLACEHOLDER]: `Not allowed nest placeholder`,
  63. [CompileErrorCodes.INVALID_LINKED_FORMAT]: `Invalid linked format`,
  64. // parser error messages
  65. [CompileErrorCodes.MUST_HAVE_MESSAGES_IN_PLURAL]: `Plural must have messages`,
  66. [CompileErrorCodes.UNEXPECTED_EMPTY_LINKED_MODIFIER]: `Unexpected empty linked modifier`,
  67. [CompileErrorCodes.UNEXPECTED_EMPTY_LINKED_KEY]: `Unexpected empty linked key`,
  68. [CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS]: `Unexpected lexical analysis in token: '{0}'`
  69. };
  70. function createCompileError(code, loc, options = {}) {
  71. const { domain, messages, args } = options;
  72. const msg = format((messages || errorMessages)[code] || '', ...(args || []))
  73. ;
  74. const error = new SyntaxError(String(msg));
  75. error.code = code;
  76. if (loc) {
  77. error.location = loc;
  78. }
  79. error.domain = domain;
  80. return error;
  81. }
  82. /** @internal */
  83. function defaultOnError(error) {
  84. throw error;
  85. }
  86. const RE_HTML_TAG = /<\/?[\w\s="/.':;#-\/]+>/;
  87. const detectHtmlTag = (source) => RE_HTML_TAG.test(source);
  88. const LocationStub = {
  89. start: { line: 1, column: 1, offset: 0 },
  90. end: { line: 1, column: 1, offset: 0 }
  91. };
  92. function createPosition(line, column, offset) {
  93. return { line, column, offset };
  94. }
  95. function createLocation(start, end, source) {
  96. const loc = { start, end };
  97. if (source != null) {
  98. loc.source = source;
  99. }
  100. return loc;
  101. }
  102. const CHAR_SP = ' ';
  103. const CHAR_CR = '\r';
  104. const CHAR_LF = '\n';
  105. const CHAR_LS = String.fromCharCode(0x2028);
  106. const CHAR_PS = String.fromCharCode(0x2029);
  107. function createScanner(str) {
  108. const _buf = str;
  109. let _index = 0;
  110. let _line = 1;
  111. let _column = 1;
  112. let _peekOffset = 0;
  113. const isCRLF = (index) => _buf[index] === CHAR_CR && _buf[index + 1] === CHAR_LF;
  114. const isLF = (index) => _buf[index] === CHAR_LF;
  115. const isPS = (index) => _buf[index] === CHAR_PS;
  116. const isLS = (index) => _buf[index] === CHAR_LS;
  117. const isLineEnd = (index) => isCRLF(index) || isLF(index) || isPS(index) || isLS(index);
  118. const index = () => _index;
  119. const line = () => _line;
  120. const column = () => _column;
  121. const peekOffset = () => _peekOffset;
  122. const charAt = (offset) => isCRLF(offset) || isPS(offset) || isLS(offset) ? CHAR_LF : _buf[offset];
  123. const currentChar = () => charAt(_index);
  124. const currentPeek = () => charAt(_index + _peekOffset);
  125. function next() {
  126. _peekOffset = 0;
  127. if (isLineEnd(_index)) {
  128. _line++;
  129. _column = 0;
  130. }
  131. if (isCRLF(_index)) {
  132. _index++;
  133. }
  134. _index++;
  135. _column++;
  136. return _buf[_index];
  137. }
  138. function peek() {
  139. if (isCRLF(_index + _peekOffset)) {
  140. _peekOffset++;
  141. }
  142. _peekOffset++;
  143. return _buf[_index + _peekOffset];
  144. }
  145. function reset() {
  146. _index = 0;
  147. _line = 1;
  148. _column = 1;
  149. _peekOffset = 0;
  150. }
  151. function resetPeek(offset = 0) {
  152. _peekOffset = offset;
  153. }
  154. function skipToPeek() {
  155. const target = _index + _peekOffset;
  156. // eslint-disable-next-line no-unmodified-loop-condition
  157. while (target !== _index) {
  158. next();
  159. }
  160. _peekOffset = 0;
  161. }
  162. return {
  163. index,
  164. line,
  165. column,
  166. peekOffset,
  167. charAt,
  168. currentChar,
  169. currentPeek,
  170. next,
  171. peek,
  172. reset,
  173. resetPeek,
  174. skipToPeek
  175. };
  176. }
  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 /* TokenTypes.EOF */,
  189. offset: _initOffset,
  190. startLoc: _initLoc,
  191. endLoc: _initLoc,
  192. lastType: 14 /* TokenTypes.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 /* TokenTypes.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 /* TokenTypes.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 /* TokenTypes.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 /* TokenTypes.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 /* TokenTypes.LinkedAlias */) {
  302. return false;
  303. }
  304. peekSpaces(scnr);
  305. const ret = scnr.currentPeek() === "." /* TokenChars.LinkedDot */;
  306. scnr.resetPeek();
  307. return ret;
  308. }
  309. function isLinkedModifierStart(scnr, context) {
  310. const { currentType } = context;
  311. if (currentType !== 9 /* TokenTypes.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 /* TokenTypes.LinkedAlias */ ||
  322. currentType === 12 /* TokenTypes.LinkedModifier */)) {
  323. return false;
  324. }
  325. peekSpaces(scnr);
  326. const ret = scnr.currentPeek() === ":" /* TokenChars.LinkedDelimiter */;
  327. scnr.resetPeek();
  328. return ret;
  329. }
  330. function isLinkedReferStart(scnr, context) {
  331. const { currentType } = context;
  332. if (currentType !== 10 /* TokenTypes.LinkedDelimiter */) {
  333. return false;
  334. }
  335. const fn = () => {
  336. const ch = scnr.currentPeek();
  337. if (ch === "{" /* TokenChars.BraceLeft */) {
  338. return isIdentifierStart(scnr.peek());
  339. }
  340. else if (ch === "@" /* TokenChars.LinkedAlias */ ||
  341. ch === "%" /* TokenChars.Modulo */ ||
  342. ch === "|" /* TokenChars.Pipe */ ||
  343. ch === ":" /* TokenChars.LinkedDelimiter */ ||
  344. ch === "." /* TokenChars.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() === "|" /* TokenChars.Pipe */;
  365. scnr.resetPeek();
  366. return ret;
  367. }
  368. function detectModuloStart(scnr) {
  369. const spaces = peekSpaces(scnr);
  370. const ret = scnr.currentPeek() === "%" /* TokenChars.Modulo */ &&
  371. scnr.peek() === "{" /* TokenChars.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 === "{" /* TokenChars.BraceLeft */) {
  382. return prev === "%" /* TokenChars.Modulo */ ? false : hasSpace;
  383. }
  384. else if (ch === "@" /* TokenChars.LinkedAlias */ || !ch) {
  385. return prev === "%" /* TokenChars.Modulo */ ? true : hasSpace;
  386. }
  387. else if (ch === "%" /* TokenChars.Modulo */) {
  388. scnr.peek();
  389. return fn(hasSpace, "%" /* TokenChars.Modulo */, true);
  390. }
  391. else if (ch === "|" /* TokenChars.Pipe */) {
  392. return prev === "%" /* TokenChars.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 !== "%" /* TokenChars.Modulo */) {
  463. emitError(CompileErrorCodes.EXPECTED_TOKEN, currentPosition(), 0, ch);
  464. }
  465. scnr.next();
  466. return "%" /* TokenChars.Modulo */;
  467. }
  468. function readText(scnr) {
  469. let buf = '';
  470. while (true) {
  471. const ch = scnr.currentChar();
  472. if (ch === "{" /* TokenChars.BraceLeft */ ||
  473. ch === "}" /* TokenChars.BraceRight */ ||
  474. ch === "@" /* TokenChars.LinkedAlias */ ||
  475. ch === "|" /* TokenChars.Pipe */ ||
  476. !ch) {
  477. break;
  478. }
  479. else if (ch === "%" /* TokenChars.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 !== "{" /* TokenChars.BraceLeft */ &&
  596. ch !== "}" /* TokenChars.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 === "{" /* TokenChars.BraceLeft */ ||
  616. ch === "%" /* TokenChars.Modulo */ ||
  617. ch === "@" /* TokenChars.LinkedAlias */ ||
  618. ch === "|" /* TokenChars.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, "|" /* TokenChars.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 "{" /* TokenChars.BraceLeft */:
  650. if (context.braceNest >= 1) {
  651. emitError(CompileErrorCodes.NOT_ALLOW_NEST_PLACEHOLDER, currentPosition(), 0);
  652. }
  653. scnr.next();
  654. token = getToken(context, 2 /* TokenTypes.BraceLeft */, "{" /* TokenChars.BraceLeft */);
  655. skipSpaces(scnr);
  656. context.braceNest++;
  657. return token;
  658. case "}" /* TokenChars.BraceRight */:
  659. if (context.braceNest > 0 &&
  660. context.currentType === 2 /* TokenTypes.BraceLeft */) {
  661. emitError(CompileErrorCodes.EMPTY_PLACEHOLDER, currentPosition(), 0);
  662. }
  663. scnr.next();
  664. token = getToken(context, 3 /* TokenTypes.BraceRight */, "}" /* TokenChars.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 "@" /* TokenChars.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 /* TokenTypes.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 /* TokenTypes.Named */ ||
  694. context.currentType === 6 /* TokenTypes.List */ ||
  695. context.currentType === 7 /* TokenTypes.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 /* TokenTypes.Named */, readNamedIdentifier(scnr));
  702. skipSpaces(scnr);
  703. return token;
  704. }
  705. if ((validListIdentifier = isListIdentifierStart(scnr, context))) {
  706. token = getToken(context, 6 /* TokenTypes.List */, readListIdentifier(scnr));
  707. skipSpaces(scnr);
  708. return token;
  709. }
  710. if ((validLiteral = isLiteralStart(scnr, context))) {
  711. token = getToken(context, 7 /* TokenTypes.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 /* TokenTypes.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 /* TokenTypes.LinkedAlias */ ||
  732. currentType === 9 /* TokenTypes.LinkedDot */ ||
  733. currentType === 12 /* TokenTypes.LinkedModifier */ ||
  734. currentType === 10 /* TokenTypes.LinkedDelimiter */) &&
  735. (ch === CHAR_LF || ch === CHAR_SP)) {
  736. emitError(CompileErrorCodes.INVALID_LINKED_FORMAT, currentPosition(), 0);
  737. }
  738. switch (ch) {
  739. case "@" /* TokenChars.LinkedAlias */:
  740. scnr.next();
  741. token = getToken(context, 8 /* TokenTypes.LinkedAlias */, "@" /* TokenChars.LinkedAlias */);
  742. context.inLinked = true;
  743. return token;
  744. case "." /* TokenChars.LinkedDot */:
  745. skipSpaces(scnr);
  746. scnr.next();
  747. return getToken(context, 9 /* TokenTypes.LinkedDot */, "." /* TokenChars.LinkedDot */);
  748. case ":" /* TokenChars.LinkedDelimiter */:
  749. skipSpaces(scnr);
  750. scnr.next();
  751. return getToken(context, 10 /* TokenTypes.LinkedDelimiter */, ":" /* TokenChars.LinkedDelimiter */);
  752. default:
  753. if (isPluralStart(scnr)) {
  754. token = getToken(context, 1 /* TokenTypes.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 /* TokenTypes.LinkedModifier */, readLinkedModifier(scnr));
  768. }
  769. if (isLinkedReferStart(scnr, context)) {
  770. skipSpaces(scnr);
  771. if (ch === "{" /* TokenChars.BraceLeft */) {
  772. // scan the placeholder
  773. return readTokenInPlaceholder(scnr, context) || token;
  774. }
  775. else {
  776. return getToken(context, 11 /* TokenTypes.LinkedKey */, readLinkedRefer(scnr));
  777. }
  778. }
  779. if (currentType === 8 /* TokenTypes.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 /* TokenTypes.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 "{" /* TokenChars.BraceLeft */:
  799. return readTokenInPlaceholder(scnr, context) || getEndToken(context);
  800. case "}" /* TokenChars.BraceRight */:
  801. emitError(CompileErrorCodes.UNBALANCED_CLOSING_BRACE, currentPosition(), 0);
  802. scnr.next();
  803. return getToken(context, 3 /* TokenTypes.BraceRight */, "}" /* TokenChars.BraceRight */);
  804. case "@" /* TokenChars.LinkedAlias */:
  805. return readTokenInLinked(scnr, context) || getEndToken(context);
  806. default:
  807. if (isPluralStart(scnr)) {
  808. token = getToken(context, 1 /* TokenTypes.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 /* TokenTypes.Text */, readText(scnr))
  818. : getToken(context, 4 /* TokenTypes.Modulo */, readModulo(scnr));
  819. }
  820. if (isTextStart(scnr)) {
  821. return getToken(context, 0 /* TokenTypes.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 /* TokenTypes.EOF */);
  837. }
  838. return readToken(_scnr, _context);
  839. }
  840. return {
  841. nextToken,
  842. currentOffset,
  843. currentPosition,
  844. context
  845. };
  846. }
  847. const ERROR_DOMAIN = 'parser';
  848. // Backslash backslash, backslash quote, uHHHH, UHHHHHH.
  849. const KNOWN_ESCAPES = /(?:\\\\|\\'|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;
  850. function fromEscapeSequence(match, codePoint4, codePoint6) {
  851. switch (match) {
  852. case `\\\\`:
  853. return `\\`;
  854. case `\\\'`:
  855. return `\'`;
  856. default: {
  857. const codePoint = parseInt(codePoint4 || codePoint6, 16);
  858. if (codePoint <= 0xd7ff || codePoint >= 0xe000) {
  859. return String.fromCodePoint(codePoint);
  860. }
  861. // invalid ...
  862. // Replace them with U+FFFD REPLACEMENT CHARACTER.
  863. return '�';
  864. }
  865. }
  866. }
  867. function createParser(options = {}) {
  868. const location = options.location !== false;
  869. const { onError } = options;
  870. function emitError(tokenzer, code, start, offset, ...args) {
  871. const end = tokenzer.currentPosition();
  872. end.offset += offset;
  873. end.column += offset;
  874. if (onError) {
  875. const loc = createLocation(start, end);
  876. const err = createCompileError(code, loc, {
  877. domain: ERROR_DOMAIN,
  878. args
  879. });
  880. onError(err);
  881. }
  882. }
  883. function startNode(type, offset, loc) {
  884. const node = {
  885. type,
  886. start: offset,
  887. end: offset
  888. };
  889. if (location) {
  890. node.loc = { start: loc, end: loc };
  891. }
  892. return node;
  893. }
  894. function endNode(node, offset, pos, type) {
  895. node.end = offset;
  896. if (type) {
  897. node.type = type;
  898. }
  899. if (location && node.loc) {
  900. node.loc.end = pos;
  901. }
  902. }
  903. function parseText(tokenizer, value) {
  904. const context = tokenizer.context();
  905. const node = startNode(3 /* NodeTypes.Text */, context.offset, context.startLoc);
  906. node.value = value;
  907. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  908. return node;
  909. }
  910. function parseList(tokenizer, index) {
  911. const context = tokenizer.context();
  912. const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
  913. const node = startNode(5 /* NodeTypes.List */, offset, loc);
  914. node.index = parseInt(index, 10);
  915. tokenizer.nextToken(); // skip brach right
  916. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  917. return node;
  918. }
  919. function parseNamed(tokenizer, key) {
  920. const context = tokenizer.context();
  921. const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
  922. const node = startNode(4 /* NodeTypes.Named */, offset, loc);
  923. node.key = key;
  924. tokenizer.nextToken(); // skip brach right
  925. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  926. return node;
  927. }
  928. function parseLiteral(tokenizer, value) {
  929. const context = tokenizer.context();
  930. const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
  931. const node = startNode(9 /* NodeTypes.Literal */, offset, loc);
  932. node.value = value.replace(KNOWN_ESCAPES, fromEscapeSequence);
  933. tokenizer.nextToken(); // skip brach right
  934. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  935. return node;
  936. }
  937. function parseLinkedModifier(tokenizer) {
  938. const token = tokenizer.nextToken();
  939. const context = tokenizer.context();
  940. const { lastOffset: offset, lastStartLoc: loc } = context; // get linked dot loc
  941. const node = startNode(8 /* NodeTypes.LinkedModifier */, offset, loc);
  942. if (token.type !== 12 /* TokenTypes.LinkedModifier */) {
  943. // empty modifier
  944. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_EMPTY_LINKED_MODIFIER, context.lastStartLoc, 0);
  945. node.value = '';
  946. endNode(node, offset, loc);
  947. return {
  948. nextConsumeToken: token,
  949. node
  950. };
  951. }
  952. // check token
  953. if (token.value == null) {
  954. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
  955. }
  956. node.value = token.value || '';
  957. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  958. return {
  959. node
  960. };
  961. }
  962. function parseLinkedKey(tokenizer, value) {
  963. const context = tokenizer.context();
  964. const node = startNode(7 /* NodeTypes.LinkedKey */, context.offset, context.startLoc);
  965. node.value = value;
  966. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  967. return node;
  968. }
  969. function parseLinked(tokenizer) {
  970. const context = tokenizer.context();
  971. const linkedNode = startNode(6 /* NodeTypes.Linked */, context.offset, context.startLoc);
  972. let token = tokenizer.nextToken();
  973. if (token.type === 9 /* TokenTypes.LinkedDot */) {
  974. const parsed = parseLinkedModifier(tokenizer);
  975. linkedNode.modifier = parsed.node;
  976. token = parsed.nextConsumeToken || tokenizer.nextToken();
  977. }
  978. // asset check token
  979. if (token.type !== 10 /* TokenTypes.LinkedDelimiter */) {
  980. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
  981. }
  982. token = tokenizer.nextToken();
  983. // skip brace left
  984. if (token.type === 2 /* TokenTypes.BraceLeft */) {
  985. token = tokenizer.nextToken();
  986. }
  987. switch (token.type) {
  988. case 11 /* TokenTypes.LinkedKey */:
  989. if (token.value == null) {
  990. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
  991. }
  992. linkedNode.key = parseLinkedKey(tokenizer, token.value || '');
  993. break;
  994. case 5 /* TokenTypes.Named */:
  995. if (token.value == null) {
  996. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
  997. }
  998. linkedNode.key = parseNamed(tokenizer, token.value || '');
  999. break;
  1000. case 6 /* TokenTypes.List */:
  1001. if (token.value == null) {
  1002. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
  1003. }
  1004. linkedNode.key = parseList(tokenizer, token.value || '');
  1005. break;
  1006. case 7 /* TokenTypes.Literal */:
  1007. if (token.value == null) {
  1008. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
  1009. }
  1010. linkedNode.key = parseLiteral(tokenizer, token.value || '');
  1011. break;
  1012. default:
  1013. // empty key
  1014. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_EMPTY_LINKED_KEY, context.lastStartLoc, 0);
  1015. const nextContext = tokenizer.context();
  1016. const emptyLinkedKeyNode = startNode(7 /* NodeTypes.LinkedKey */, nextContext.offset, nextContext.startLoc);
  1017. emptyLinkedKeyNode.value = '';
  1018. endNode(emptyLinkedKeyNode, nextContext.offset, nextContext.startLoc);
  1019. linkedNode.key = emptyLinkedKeyNode;
  1020. endNode(linkedNode, nextContext.offset, nextContext.startLoc);
  1021. return {
  1022. nextConsumeToken: token,
  1023. node: linkedNode
  1024. };
  1025. }
  1026. endNode(linkedNode, tokenizer.currentOffset(), tokenizer.currentPosition());
  1027. return {
  1028. node: linkedNode
  1029. };
  1030. }
  1031. function parseMessage(tokenizer) {
  1032. const context = tokenizer.context();
  1033. const startOffset = context.currentType === 1 /* TokenTypes.Pipe */
  1034. ? tokenizer.currentOffset()
  1035. : context.offset;
  1036. const startLoc = context.currentType === 1 /* TokenTypes.Pipe */
  1037. ? context.endLoc
  1038. : context.startLoc;
  1039. const node = startNode(2 /* NodeTypes.Message */, startOffset, startLoc);
  1040. node.items = [];
  1041. let nextToken = null;
  1042. do {
  1043. const token = nextToken || tokenizer.nextToken();
  1044. nextToken = null;
  1045. switch (token.type) {
  1046. case 0 /* TokenTypes.Text */:
  1047. if (token.value == null) {
  1048. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
  1049. }
  1050. node.items.push(parseText(tokenizer, token.value || ''));
  1051. break;
  1052. case 6 /* TokenTypes.List */:
  1053. if (token.value == null) {
  1054. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
  1055. }
  1056. node.items.push(parseList(tokenizer, token.value || ''));
  1057. break;
  1058. case 5 /* TokenTypes.Named */:
  1059. if (token.value == null) {
  1060. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
  1061. }
  1062. node.items.push(parseNamed(tokenizer, token.value || ''));
  1063. break;
  1064. case 7 /* TokenTypes.Literal */:
  1065. if (token.value == null) {
  1066. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, getTokenCaption(token));
  1067. }
  1068. node.items.push(parseLiteral(tokenizer, token.value || ''));
  1069. break;
  1070. case 8 /* TokenTypes.LinkedAlias */:
  1071. const parsed = parseLinked(tokenizer);
  1072. node.items.push(parsed.node);
  1073. nextToken = parsed.nextConsumeToken || null;
  1074. break;
  1075. }
  1076. } while (context.currentType !== 14 /* TokenTypes.EOF */ &&
  1077. context.currentType !== 1 /* TokenTypes.Pipe */);
  1078. // adjust message node loc
  1079. const endOffset = context.currentType === 1 /* TokenTypes.Pipe */
  1080. ? context.lastOffset
  1081. : tokenizer.currentOffset();
  1082. const endLoc = context.currentType === 1 /* TokenTypes.Pipe */
  1083. ? context.lastEndLoc
  1084. : tokenizer.currentPosition();
  1085. endNode(node, endOffset, endLoc);
  1086. return node;
  1087. }
  1088. function parsePlural(tokenizer, offset, loc, msgNode) {
  1089. const context = tokenizer.context();
  1090. let hasEmptyMessage = msgNode.items.length === 0;
  1091. const node = startNode(1 /* NodeTypes.Plural */, offset, loc);
  1092. node.cases = [];
  1093. node.cases.push(msgNode);
  1094. do {
  1095. const msg = parseMessage(tokenizer);
  1096. if (!hasEmptyMessage) {
  1097. hasEmptyMessage = msg.items.length === 0;
  1098. }
  1099. node.cases.push(msg);
  1100. } while (context.currentType !== 14 /* TokenTypes.EOF */);
  1101. if (hasEmptyMessage) {
  1102. emitError(tokenizer, CompileErrorCodes.MUST_HAVE_MESSAGES_IN_PLURAL, loc, 0);
  1103. }
  1104. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  1105. return node;
  1106. }
  1107. function parseResource(tokenizer) {
  1108. const context = tokenizer.context();
  1109. const { offset, startLoc } = context;
  1110. const msgNode = parseMessage(tokenizer);
  1111. if (context.currentType === 14 /* TokenTypes.EOF */) {
  1112. return msgNode;
  1113. }
  1114. else {
  1115. return parsePlural(tokenizer, offset, startLoc, msgNode);
  1116. }
  1117. }
  1118. function parse(source) {
  1119. const tokenizer = createTokenizer(source, assign({}, options));
  1120. const context = tokenizer.context();
  1121. const node = startNode(0 /* NodeTypes.Resource */, context.offset, context.startLoc);
  1122. if (location && node.loc) {
  1123. node.loc.source = source;
  1124. }
  1125. node.body = parseResource(tokenizer);
  1126. // assert whether achieved to EOF
  1127. if (context.currentType !== 14 /* TokenTypes.EOF */) {
  1128. emitError(tokenizer, CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS, context.lastStartLoc, 0, source[context.offset] || '');
  1129. }
  1130. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  1131. return node;
  1132. }
  1133. return { parse };
  1134. }
  1135. function getTokenCaption(token) {
  1136. if (token.type === 14 /* TokenTypes.EOF */) {
  1137. return 'EOF';
  1138. }
  1139. const name = (token.value || '').replace(/\r?\n/gu, '\\n');
  1140. return name.length > 10 ? name.slice(0, 9) + '…' : name;
  1141. }
  1142. function createTransformer(ast, options = {} // eslint-disable-line
  1143. ) {
  1144. const _context = {
  1145. ast,
  1146. helpers: new Set()
  1147. };
  1148. const context = () => _context;
  1149. const helper = (name) => {
  1150. _context.helpers.add(name);
  1151. return name;
  1152. };
  1153. return { context, helper };
  1154. }
  1155. function traverseNodes(nodes, transformer) {
  1156. for (let i = 0; i < nodes.length; i++) {
  1157. traverseNode(nodes[i], transformer);
  1158. }
  1159. }
  1160. function traverseNode(node, transformer) {
  1161. // TODO: if we need pre-hook of transform, should be implemented to here
  1162. switch (node.type) {
  1163. case 1 /* NodeTypes.Plural */:
  1164. traverseNodes(node.cases, transformer);
  1165. transformer.helper("plural" /* HelperNameMap.PLURAL */);
  1166. break;
  1167. case 2 /* NodeTypes.Message */:
  1168. traverseNodes(node.items, transformer);
  1169. break;
  1170. case 6 /* NodeTypes.Linked */:
  1171. const linked = node;
  1172. traverseNode(linked.key, transformer);
  1173. transformer.helper("linked" /* HelperNameMap.LINKED */);
  1174. transformer.helper("type" /* HelperNameMap.TYPE */);
  1175. break;
  1176. case 5 /* NodeTypes.List */:
  1177. transformer.helper("interpolate" /* HelperNameMap.INTERPOLATE */);
  1178. transformer.helper("list" /* HelperNameMap.LIST */);
  1179. break;
  1180. case 4 /* NodeTypes.Named */:
  1181. transformer.helper("interpolate" /* HelperNameMap.INTERPOLATE */);
  1182. transformer.helper("named" /* HelperNameMap.NAMED */);
  1183. break;
  1184. }
  1185. // TODO: if we need post-hook of transform, should be implemented to here
  1186. }
  1187. // transform AST
  1188. function transform(ast, options = {} // eslint-disable-line
  1189. ) {
  1190. const transformer = createTransformer(ast);
  1191. transformer.helper("normalize" /* HelperNameMap.NORMALIZE */);
  1192. // traverse
  1193. ast.body && traverseNode(ast.body, transformer);
  1194. // set meta information
  1195. const context = transformer.context();
  1196. ast.helpers = Array.from(context.helpers);
  1197. }
  1198. function createCodeGenerator(ast, options) {
  1199. const { sourceMap, filename, breakLineCode, needIndent: _needIndent } = options;
  1200. const _context = {
  1201. source: ast.loc.source,
  1202. filename,
  1203. code: '',
  1204. column: 1,
  1205. line: 1,
  1206. offset: 0,
  1207. map: undefined,
  1208. breakLineCode,
  1209. needIndent: _needIndent,
  1210. indentLevel: 0
  1211. };
  1212. const context = () => _context;
  1213. function push(code, node) {
  1214. _context.code += code;
  1215. }
  1216. function _newline(n, withBreakLine = true) {
  1217. const _breakLineCode = withBreakLine ? breakLineCode : '';
  1218. push(_needIndent ? _breakLineCode + ` `.repeat(n) : _breakLineCode);
  1219. }
  1220. function indent(withNewLine = true) {
  1221. const level = ++_context.indentLevel;
  1222. withNewLine && _newline(level);
  1223. }
  1224. function deindent(withNewLine = true) {
  1225. const level = --_context.indentLevel;
  1226. withNewLine && _newline(level);
  1227. }
  1228. function newline() {
  1229. _newline(_context.indentLevel);
  1230. }
  1231. const helper = (key) => `_${key}`;
  1232. const needIndent = () => _context.needIndent;
  1233. return {
  1234. context,
  1235. push,
  1236. indent,
  1237. deindent,
  1238. newline,
  1239. helper,
  1240. needIndent
  1241. };
  1242. }
  1243. function generateLinkedNode(generator, node) {
  1244. const { helper } = generator;
  1245. generator.push(`${helper("linked" /* HelperNameMap.LINKED */)}(`);
  1246. generateNode(generator, node.key);
  1247. if (node.modifier) {
  1248. generator.push(`, `);
  1249. generateNode(generator, node.modifier);
  1250. generator.push(`, _type`);
  1251. }
  1252. else {
  1253. generator.push(`, undefined, _type`);
  1254. }
  1255. generator.push(`)`);
  1256. }
  1257. function generateMessageNode(generator, node) {
  1258. const { helper, needIndent } = generator;
  1259. generator.push(`${helper("normalize" /* HelperNameMap.NORMALIZE */)}([`);
  1260. generator.indent(needIndent());
  1261. const length = node.items.length;
  1262. for (let i = 0; i < length; i++) {
  1263. generateNode(generator, node.items[i]);
  1264. if (i === length - 1) {
  1265. break;
  1266. }
  1267. generator.push(', ');
  1268. }
  1269. generator.deindent(needIndent());
  1270. generator.push('])');
  1271. }
  1272. function generatePluralNode(generator, node) {
  1273. const { helper, needIndent } = generator;
  1274. if (node.cases.length > 1) {
  1275. generator.push(`${helper("plural" /* HelperNameMap.PLURAL */)}([`);
  1276. generator.indent(needIndent());
  1277. const length = node.cases.length;
  1278. for (let i = 0; i < length; i++) {
  1279. generateNode(generator, node.cases[i]);
  1280. if (i === length - 1) {
  1281. break;
  1282. }
  1283. generator.push(', ');
  1284. }
  1285. generator.deindent(needIndent());
  1286. generator.push(`])`);
  1287. }
  1288. }
  1289. function generateResource(generator, node) {
  1290. if (node.body) {
  1291. generateNode(generator, node.body);
  1292. }
  1293. else {
  1294. generator.push('null');
  1295. }
  1296. }
  1297. function generateNode(generator, node) {
  1298. const { helper } = generator;
  1299. switch (node.type) {
  1300. case 0 /* NodeTypes.Resource */:
  1301. generateResource(generator, node);
  1302. break;
  1303. case 1 /* NodeTypes.Plural */:
  1304. generatePluralNode(generator, node);
  1305. break;
  1306. case 2 /* NodeTypes.Message */:
  1307. generateMessageNode(generator, node);
  1308. break;
  1309. case 6 /* NodeTypes.Linked */:
  1310. generateLinkedNode(generator, node);
  1311. break;
  1312. case 8 /* NodeTypes.LinkedModifier */:
  1313. generator.push(JSON.stringify(node.value), node);
  1314. break;
  1315. case 7 /* NodeTypes.LinkedKey */:
  1316. generator.push(JSON.stringify(node.value), node);
  1317. break;
  1318. case 5 /* NodeTypes.List */:
  1319. generator.push(`${helper("interpolate" /* HelperNameMap.INTERPOLATE */)}(${helper("list" /* HelperNameMap.LIST */)}(${node.index}))`, node);
  1320. break;
  1321. case 4 /* NodeTypes.Named */:
  1322. generator.push(`${helper("interpolate" /* HelperNameMap.INTERPOLATE */)}(${helper("named" /* HelperNameMap.NAMED */)}(${JSON.stringify(node.key)}))`, node);
  1323. break;
  1324. case 9 /* NodeTypes.Literal */:
  1325. generator.push(JSON.stringify(node.value), node);
  1326. break;
  1327. case 3 /* NodeTypes.Text */:
  1328. generator.push(JSON.stringify(node.value), node);
  1329. break;
  1330. default:
  1331. {
  1332. throw new Error(`unhandled codegen node type: ${node.type}`);
  1333. }
  1334. }
  1335. }
  1336. // generate code from AST
  1337. const generate = (ast, options = {} // eslint-disable-line
  1338. ) => {
  1339. const mode = isString(options.mode) ? options.mode : 'normal';
  1340. const filename = isString(options.filename)
  1341. ? options.filename
  1342. : 'message.intl';
  1343. const sourceMap = !!options.sourceMap;
  1344. // prettier-ignore
  1345. const breakLineCode = options.breakLineCode != null
  1346. ? options.breakLineCode
  1347. : mode === 'arrow'
  1348. ? ';'
  1349. : '\n';
  1350. const needIndent = options.needIndent ? options.needIndent : mode !== 'arrow';
  1351. const helpers = ast.helpers || [];
  1352. const generator = createCodeGenerator(ast, {
  1353. mode,
  1354. filename,
  1355. sourceMap,
  1356. breakLineCode,
  1357. needIndent
  1358. });
  1359. generator.push(mode === 'normal' ? `function __msg__ (ctx) {` : `(ctx) => {`);
  1360. generator.indent(needIndent);
  1361. if (helpers.length > 0) {
  1362. generator.push(`const { ${helpers.map(s => `${s}: _${s}`).join(', ')} } = ctx`);
  1363. generator.newline();
  1364. }
  1365. generator.push(`return `);
  1366. generateNode(generator, ast);
  1367. generator.deindent(needIndent);
  1368. generator.push(`}`);
  1369. const { code, map } = generator.context();
  1370. return {
  1371. ast,
  1372. code,
  1373. map: map ? map.toJSON() : undefined // eslint-disable-line @typescript-eslint/no-explicit-any
  1374. };
  1375. };
  1376. function baseCompile(source, options = {}) {
  1377. const assignedOptions = assign({}, options);
  1378. // parse source codes
  1379. const parser = createParser(assignedOptions);
  1380. const ast = parser.parse(source);
  1381. // transform ASTs
  1382. transform(ast, assignedOptions);
  1383. // generate javascript codes
  1384. return generate(ast, assignedOptions);
  1385. }
  1386. exports.CompileErrorCodes = CompileErrorCodes;
  1387. exports.ERROR_DOMAIN = ERROR_DOMAIN;
  1388. exports.LocationStub = LocationStub;
  1389. exports.baseCompile = baseCompile;
  1390. exports.createCompileError = createCompileError;
  1391. exports.createLocation = createLocation;
  1392. exports.createParser = createParser;
  1393. exports.createPosition = createPosition;
  1394. exports.defaultOnError = defaultOnError;
  1395. exports.detectHtmlTag = detectHtmlTag;
  1396. exports.errorMessages = errorMessages;
  1397. return exports;
  1398. })({});