import { parseTokens } from './pattern-parser/parse-tokens.js'; import { parseNumberAsSkeleton } from './pattern-parser/number-as-skeleton.js'; import { PatternError } from './errors.js'; function handleAffix(affixTokens, res, currency, onError, isPrefix) { let inFmt = false; let str = ''; for (const token of affixTokens) { switch (token.char) { case '%': res.unit = { style: token.style }; if (isPrefix) inFmt = true; else str = ''; break; case '¤': if (!currency) { const msg = `The ¤ pattern requires a currency`; onError(new PatternError('¤', msg)); break; } res.unit = { style: 'currency', currency }; switch (token.currency) { case 'iso-code': res.unitWidth = 'unit-width-iso-code'; break; case 'full-name': res.unitWidth = 'unit-width-full-name'; break; case 'narrow': res.unitWidth = 'unit-width-narrow'; break; } if (isPrefix) inFmt = true; else str = ''; break; case '*': // TODO break; case '+': if (!inFmt) str += '+'; break; case "'": if (!inFmt) str += token.str; break; } } return str; } function getNegativeAffix(affixTokens, isPrefix) { let inFmt = false; let str = ''; for (const token of affixTokens) { switch (token.char) { case '%': case '¤': if (isPrefix) inFmt = true; else str = ''; break; case '-': if (!inFmt) str += '-'; break; case "'": if (!inFmt) str += token.str; break; } } return str; } /** * Parse an {@link * http://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns | * ICU NumberFormatter pattern} string into a {@link Skeleton} structure. * * @public * @param src - The pattern string * @param currency - If the pattern includes ¤ tokens, their skeleton * representation requires a three-letter currency code. * @param onError - Called when the parser encounters a syntax error. The * function will still return a {@link Skeleton}, but it will be incomplete * and/or inaccurate. If not defined, the error will be thrown instead. * * @remarks * Unlike the skeleton parser, the pattern parser is not able to return partial * results on error, and will instead throw. Output padding is not supported. * * @example * ```js * import { parseNumberPattern } from '@messageformat/number-skeleton' * * parseNumberPattern('#,##0.00 ¤', 'EUR', console.error) * // { * // group: 'group-auto', * // precision: { * // style: 'precision-fraction', * // minFraction: 2, * // maxFraction: 2 * // }, * // unit: { style: 'currency', currency: 'EUR' } * // } * ``` */ export function parseNumberPattern(src, currency, onError = error => { throw error; }) { const { tokens, negative } = parseTokens(src, onError); const res = parseNumberAsSkeleton(tokens.number, onError); const prefix = handleAffix(tokens.prefix, res, currency, onError, true); const suffix = handleAffix(tokens.suffix, res, currency, onError, false); if (negative) { const negPrefix = getNegativeAffix(negative.prefix, true); const negSuffix = getNegativeAffix(negative.suffix, false); res.affix = { pos: [prefix, suffix], neg: [negPrefix, negSuffix] }; res.sign = 'sign-never'; } else if (prefix || suffix) { res.affix = { pos: [prefix, suffix] }; } return res; }