messageformat-number-skeleton
Version:
A parser & formatter for ICU NumberFormat skeleton strings & patterns
123 lines (122 loc) • 4.38 kB
JavaScript
import { MaskedValueError, PatternError } from '../errors.js';
export function parseNumberAsSkeleton(tokens, onError) {
const res = {};
let hasGroups = false;
let hasExponent = false;
let intOptional = 0;
let intDigits = '';
let decimalPos = -1;
let fracDigits = '';
let fracOptional = 0;
for (let pos = 0; pos < tokens.length; ++pos) {
const token = tokens[pos];
switch (token.char) {
case '#': {
if (decimalPos === -1) {
if (intDigits) {
const msg = 'Pattern has # after integer digits';
onError(new PatternError('#', msg));
}
intOptional += token.width;
}
else {
fracOptional += token.width;
}
break;
}
case '0': {
if (decimalPos === -1) {
intDigits += token.digits;
}
else {
if (fracOptional) {
const msg = 'Pattern has digits after # in fraction';
onError(new PatternError('0', msg));
}
fracDigits += token.digits;
}
break;
}
case '@': {
if (res.precision)
onError(new MaskedValueError('precision', res.precision));
res.precision = {
style: 'precision-fraction',
minSignificant: token.min,
maxSignificant: token.width
};
break;
}
case ',':
hasGroups = true;
break;
case '.':
if (decimalPos === 1) {
const msg = 'Pattern has more than one decimal separator';
onError(new PatternError('.', msg));
}
decimalPos = pos;
break;
case 'E': {
if (hasExponent)
onError(new MaskedValueError('exponent', res.notation));
if (hasGroups) {
const msg = 'Exponential patterns may not contain grouping separators';
onError(new PatternError('E', msg));
}
res.notation = { style: 'scientific' };
if (token.expDigits > 1)
res.notation.expDigits = token.expDigits;
if (token.plus)
res.notation.expSign = 'sign-always';
hasExponent = true;
}
}
}
// imprecise mapping due to paradigm differences
if (hasGroups)
res.group = 'group-auto';
else if (intOptional + intDigits.length > 3)
res.group = 'group-off';
const increment = Number(`${intDigits || '0'}.${fracDigits}`);
if (increment)
res.precision = { style: 'precision-increment', increment };
if (!hasExponent) {
if (intDigits.length > 1)
res.integerWidth = { min: intDigits.length };
if (!res.precision && (fracDigits.length || fracOptional)) {
res.precision = {
style: 'precision-fraction',
minFraction: fracDigits.length,
maxFraction: fracDigits.length + fracOptional
};
}
}
else {
if (!res.precision || increment) {
res.integerWidth = intOptional
? { min: 1, max: intOptional + intDigits.length }
: { min: Math.max(1, intDigits.length) };
}
if (res.precision) {
if (!increment)
res.integerWidth = { min: 1, max: 1 };
}
else {
const dc = intDigits.length + fracDigits.length;
if (decimalPos === -1) {
if (dc > 0)
res.precision = { style: 'precision-fraction', maxSignificant: dc };
}
else {
res.precision = {
style: 'precision-fraction',
maxSignificant: Math.max(1, dc) + fracOptional
};
if (dc > 1)
res.precision.minSignificant = dc;
}
}
}
return res;
}