Skip to content

Commit

Permalink
feat(intl-messageformat-parser): support more concise skeleton, fix #…
Browse files Browse the repository at this point in the history
  • Loading branch information
longlho committed Jan 26, 2021
1 parent 34d68bd commit 6be7988
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 0 deletions.
54 changes: 54 additions & 0 deletions packages/intl-messageformat-parser/src/skeleton.ts
Expand Up @@ -191,34 +191,73 @@ function parseSign(str: string): ExtendedNumberFormatOptions | undefined {
signDisplay: 'auto',
};
case 'sign-accounting':
case '()':
return {
currencySign: 'accounting',
};
case 'sign-always':
case '+!':
return {
signDisplay: 'always',
};
case 'sign-accounting-always':
case '()!':
return {
signDisplay: 'always',
currencySign: 'accounting',
};
case 'sign-except-zero':
case '+?':
return {
signDisplay: 'exceptZero',
};
case 'sign-accounting-except-zero':
case '()?':
return {
signDisplay: 'exceptZero',
currencySign: 'accounting',
};
case 'sign-never':
case '+_':
return {
signDisplay: 'never',
};
}
}

function parseConciseScientificAndEngineeringStem(
stem: string
): ExtendedNumberFormatOptions | undefined {
// Engineering
let result: ExtendedNumberFormatOptions | undefined;
if (stem[0] === 'E' && stem[1] === 'E') {
result = {
notation: 'engineering',
};
stem = stem.slice(2);
} else if (stem[0] === 'E') {
result = {
notation: 'scientific',
};
stem = stem.slice(1);
}
if (result) {
const signDisplay = stem.slice(0, 2);
if (signDisplay === '+!') {
result.signDisplay = 'always';
stem = stem.slice(2);
} else if (signDisplay === '+?') {
result.signDisplay = 'exceptZero';
stem = stem.slice(2);
}
if (!CONCISE_INTEGER_WIDTH_REGEX.test(stem)) {
throw new Error('Malformed concise eng/scientific notation');
}
result.minimumIntegerDigits = stem.length;
}
return result;
}

function parseNotationOptions(opt: string): ExtendedNumberFormatOptions {
const result: ExtendedNumberFormatOptions = {};
const signOpts = parseSign(opt);
Expand All @@ -238,28 +277,37 @@ export function parseNumberSkeleton(
for (const token of tokens) {
switch (token.stem) {
case 'percent':
case '%':
result.style = 'percent';
continue;
case '%x100':
result.style = 'percent';
result.scale = 100;
continue;
case 'currency':
result.style = 'currency';
result.currency = token.options[0];
continue;
case 'group-off':
case ',_':
result.useGrouping = false;
continue;
case 'precision-integer':
case '.':
result.maximumFractionDigits = 0;
continue;
case 'measure-unit':
case 'unit':
result.style = 'unit';
result.unit = icuUnitToEcma(token.options[0]);
continue;
case 'compact-short':
case 'K':
result.notation = 'compact';
result.compactDisplay = 'short';
continue;
case 'compact-long':
case 'KK':
result.notation = 'compact';
result.compactDisplay = 'long';
continue;
Expand Down Expand Up @@ -396,6 +444,12 @@ export function parseNumberSkeleton(
if (signOpts) {
result = {...result, ...signOpts};
}
const conciseScientificAndEngineeringOpts = parseConciseScientificAndEngineeringStem(
token.stem
);
if (conciseScientificAndEngineeringOpts) {
result = {...result, ...conciseScientificAndEngineeringOpts};
}
}
return result;
}
@@ -1,11 +1,86 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`[parseNumberSkeleton] case: "% .##" 1`] = `
Object {
"maximumFractionDigits": 2,
"style": "percent",
}
`;

exports[`[parseNumberSkeleton] case: "%x100" 1`] = `
Object {
"scale": 100,
"style": "percent",
}
`;

exports[`[parseNumberSkeleton] case: "()!" 1`] = `
Object {
"currencySign": "accounting",
"signDisplay": "always",
}
`;

exports[`[parseNumberSkeleton] case: "()" 1`] = `
Object {
"currencySign": "accounting",
}
`;

exports[`[parseNumberSkeleton] case: "()?" 1`] = `
Object {
"currencySign": "accounting",
"signDisplay": "exceptZero",
}
`;

exports[`[parseNumberSkeleton] case: "+!" 1`] = `
Object {
"signDisplay": "always",
}
`;

exports[`[parseNumberSkeleton] case: "+?" 1`] = `
Object {
"signDisplay": "exceptZero",
}
`;

exports[`[parseNumberSkeleton] case: "+_" 1`] = `
Object {
"signDisplay": "never",
}
`;

exports[`[parseNumberSkeleton] case: "000" 1`] = `
Object {
"minimumIntegerDigits": 3,
}
`;

exports[`[parseNumberSkeleton] case: "E+!00" 1`] = `
Object {
"minimumIntegerDigits": 2,
"notation": "scientific",
"signDisplay": "always",
}
`;

exports[`[parseNumberSkeleton] case: "E0" 1`] = `
Object {
"minimumIntegerDigits": 1,
"notation": "scientific",
}
`;

exports[`[parseNumberSkeleton] case: "EE+?000" 1`] = `
Object {
"minimumIntegerDigits": 3,
"notation": "engineering",
"signDisplay": "exceptZero",
}
`;

exports[`[parseNumberSkeleton] case: "compact-long" 1`] = `
Object {
"compactDisplay": "long",
Expand Down
11 changes: 11 additions & 0 deletions packages/intl-messageformat-parser/tests/number_skeleton.test.ts
Expand Up @@ -15,6 +15,7 @@ test.each([

test.each([
'percent .##',
'% .##',
'percent .000*',
'percent .0###',
'percent .00/@##',
Expand All @@ -37,13 +38,23 @@ test.each([
'notation-simple',
'sign-auto',
'sign-always',
'+!',
'sign-never',
'+_',
'sign-accounting',
'()',
'sign-accounting-always',
'()!',
'sign-except-zero',
'+?',
'sign-accounting-except-zero',
'()?',
'000',
'integer-width/*000',
'E0',
'E+!00',
'EE+?000',
'%x100',
])('[parseNumberSkeleton] case: %p', skeleton => {
const ast = pegParse(`{0, number, ::${skeleton}}`);
const el = ast[0];
Expand Down

0 comments on commit 6be7988

Please sign in to comment.