From 0b70a1d5773f17373991b1294e3ae618600aae7d Mon Sep 17 00:00:00 2001 From: Ludovico Fischer Date: Mon, 10 Jan 2022 18:05:13 +0100 Subject: [PATCH] fix: preserve brackets around functions Modify the parser to track parenthesized expressions. Keep the parentheses if a function is inside. Fix #113 Fix #115 --- src/__tests__/index.js | 19 +++++++++++++++++++ src/lib/reducer.js | 9 +++++++++ src/lib/stringifier.js | 4 ++++ src/parser.d.ts | 7 ++++++- src/parser.jison | 2 +- types/lib/reducer.d.ts | 3 ++- 6 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/__tests__/index.js b/src/__tests__/index.js index e075300..09df516 100644 --- a/src/__tests__/index.js +++ b/src/__tests__/index.js @@ -735,6 +735,25 @@ test( testValue('calc(100% - calc(10px - 2vw))', 'calc(100% - 10px + 2vw)') ); +test( + 'should preserve division precedence', + testValue( + 'calc(100%/(var(--aspect-ratio)))', + 'calc(100%/(var(--aspect-ratio)))' + ) +); + +test( + 'should preserve division precedence (2)', + testValue( + `calc( + (var(--fluid-screen) - ((var(--fluid-min-width) / 16) * 1rem)) / + ((var(--fluid-max-width) / 16) - (var(--fluid-min-width) / 16)) + )`, + 'calc((var(--fluid-screen) - var(--fluid-min-width)/16*1rem)/(var(--fluid-max-width)/16 - var(--fluid-min-width)/16))' + ) +); + test('precision for calc', testValue('calc(100% / 3 * 3)', '100%')); test( diff --git a/src/lib/reducer.js b/src/lib/reducer.js index 7ad3f6e..8ff6162 100644 --- a/src/lib/reducer.js +++ b/src/lib/reducer.js @@ -100,6 +100,8 @@ function collectAddSubItems(preOperator, node, collected, precision) { collected.push({node: reducedNode, preOperator}); } } + } else if (node.type === 'ParenthesizedExpression') { + collectAddSubItems(preOperator, node.content, collected, precision); } else { collected.push({node, preOperator}); } @@ -286,6 +288,7 @@ function convertNodesUnits(left, right, precision) { /** * @param {import('../parser').CalcNode} node * @param {number} precision + * @return {import('../parser').CalcNode} */ function reduce(node, precision) { if (node.type === "MathExpression") { @@ -305,6 +308,12 @@ function reduce(node, precision) { return node; } + if (node.type === 'ParenthesizedExpression') { + if (node.content.type !== 'Function') { + return reduce(node.content, precision); + } + } + return node; } diff --git a/src/lib/stringifier.js b/src/lib/stringifier.js index 3dda7a4..275c637 100644 --- a/src/lib/stringifier.js +++ b/src/lib/stringifier.js @@ -20,6 +20,8 @@ function round(value, prec) { /** * @param {number | false} prec * @param {import('../parser').CalcNode} node + * + * @return {string} */ function stringify(node, prec) { switch (node.type) { @@ -46,6 +48,8 @@ function stringify(node, prec) { return round(node.value, prec).toString(); case 'Function': return node.value.toString(); + case 'ParenthesizedExpression': + return `(${stringify(node.content, prec)})`; default: return round(node.value, prec) + node.unit; } diff --git a/src/parser.d.ts b/src/parser.d.ts index ede1661..8bdb1b5 100644 --- a/src/parser.d.ts +++ b/src/parser.d.ts @@ -5,6 +5,11 @@ export interface MathExpression { operator: '*' | '+' | '-' | '/'; } +export interface ParenthesizedExpression { + type: 'ParenthesizedExpression'; + content: CalcNode; +} + export interface DimensionExpression { type: | 'LengthValue' @@ -37,7 +42,7 @@ export interface FunctionExpression { export type ValueExpression = DimensionExpression | NumberExpression; -export type CalcNode = MathExpression | ValueExpression | FunctionExpression; +export type CalcNode = MathExpression | ValueExpression | FunctionExpression | ParenthesizedExpression; export interface Parser { parse: (arg: string) => CalcNode; diff --git a/src/parser.jison b/src/parser.jison index eb402b8..90f57cd 100644 --- a/src/parser.jison +++ b/src/parser.jison @@ -74,7 +74,7 @@ expression | math_expression SUB math_expression { $$ = { type: 'MathExpression', operator: $2, left: $1, right: $3 }; } | math_expression MUL math_expression { $$ = { type: 'MathExpression', operator: $2, left: $1, right: $3 }; } | math_expression DIV math_expression { $$ = { type: 'MathExpression', operator: $2, left: $1, right: $3 }; } - | LPAREN math_expression RPAREN { $$ = $2; } + | LPAREN math_expression RPAREN { $$ = { type: 'ParenthesizedExpression', content: $2 }; } | function { $$ = $1; } | dimension { $$ = $1; } | number { $$ = $1; } diff --git a/types/lib/reducer.d.ts b/types/lib/reducer.d.ts index d84aee9..933a841 100644 --- a/types/lib/reducer.d.ts +++ b/types/lib/reducer.d.ts @@ -6,5 +6,6 @@ export type Collectible = { /** * @param {import('../parser').CalcNode} node * @param {number} precision + * @return {import('../parser').CalcNode} */ -declare function reduce(node: import('../parser').CalcNode, precision: number): import("../parser").MathExpression | import("../parser").DimensionExpression | import("../parser").NumberExpression | import("../parser").FunctionExpression; +declare function reduce(node: import('../parser').CalcNode, precision: number): import('../parser').CalcNode;