parse-pattern.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { parseTokens } from './pattern-parser/parse-tokens.js';
  2. import { parseNumberAsSkeleton } from './pattern-parser/number-as-skeleton.js';
  3. import { PatternError } from './errors.js';
  4. function handleAffix(affixTokens, res, currency, onError, isPrefix) {
  5. let inFmt = false;
  6. let str = '';
  7. for (const token of affixTokens) {
  8. switch (token.char) {
  9. case '%':
  10. res.unit = { style: token.style };
  11. if (isPrefix)
  12. inFmt = true;
  13. else
  14. str = '';
  15. break;
  16. case '¤':
  17. if (!currency) {
  18. const msg = `The ¤ pattern requires a currency`;
  19. onError(new PatternError('¤', msg));
  20. break;
  21. }
  22. res.unit = { style: 'currency', currency };
  23. switch (token.currency) {
  24. case 'iso-code':
  25. res.unitWidth = 'unit-width-iso-code';
  26. break;
  27. case 'full-name':
  28. res.unitWidth = 'unit-width-full-name';
  29. break;
  30. case 'narrow':
  31. res.unitWidth = 'unit-width-narrow';
  32. break;
  33. }
  34. if (isPrefix)
  35. inFmt = true;
  36. else
  37. str = '';
  38. break;
  39. case '*':
  40. // TODO
  41. break;
  42. case '+':
  43. if (!inFmt)
  44. str += '+';
  45. break;
  46. case "'":
  47. if (!inFmt)
  48. str += token.str;
  49. break;
  50. }
  51. }
  52. return str;
  53. }
  54. function getNegativeAffix(affixTokens, isPrefix) {
  55. let inFmt = false;
  56. let str = '';
  57. for (const token of affixTokens) {
  58. switch (token.char) {
  59. case '%':
  60. case '¤':
  61. if (isPrefix)
  62. inFmt = true;
  63. else
  64. str = '';
  65. break;
  66. case '-':
  67. if (!inFmt)
  68. str += '-';
  69. break;
  70. case "'":
  71. if (!inFmt)
  72. str += token.str;
  73. break;
  74. }
  75. }
  76. return str;
  77. }
  78. /**
  79. * Parse an {@link
  80. * http://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns |
  81. * ICU NumberFormatter pattern} string into a {@link Skeleton} structure.
  82. *
  83. * @public
  84. * @param src - The pattern string
  85. * @param currency - If the pattern includes ¤ tokens, their skeleton
  86. * representation requires a three-letter currency code.
  87. * @param onError - Called when the parser encounters a syntax error. The
  88. * function will still return a {@link Skeleton}, but it will be incomplete
  89. * and/or inaccurate. If not defined, the error will be thrown instead.
  90. *
  91. * @remarks
  92. * Unlike the skeleton parser, the pattern parser is not able to return partial
  93. * results on error, and will instead throw. Output padding is not supported.
  94. *
  95. * @example
  96. * ```js
  97. * import { parseNumberPattern } from '@messageformat/number-skeleton'
  98. *
  99. * parseNumberPattern('#,##0.00 ¤', 'EUR', console.error)
  100. * // {
  101. * // group: 'group-auto',
  102. * // precision: {
  103. * // style: 'precision-fraction',
  104. * // minFraction: 2,
  105. * // maxFraction: 2
  106. * // },
  107. * // unit: { style: 'currency', currency: 'EUR' }
  108. * // }
  109. * ```
  110. */
  111. export function parseNumberPattern(src, currency, onError = error => {
  112. throw error;
  113. }) {
  114. const { tokens, negative } = parseTokens(src, onError);
  115. const res = parseNumberAsSkeleton(tokens.number, onError);
  116. const prefix = handleAffix(tokens.prefix, res, currency, onError, true);
  117. const suffix = handleAffix(tokens.suffix, res, currency, onError, false);
  118. if (negative) {
  119. const negPrefix = getNegativeAffix(negative.prefix, true);
  120. const negSuffix = getNegativeAffix(negative.suffix, false);
  121. res.affix = { pos: [prefix, suffix], neg: [negPrefix, negSuffix] };
  122. res.sign = 'sign-never';
  123. }
  124. else if (prefix || suffix) {
  125. res.affix = { pos: [prefix, suffix] };
  126. }
  127. return res;
  128. }