diff --git a/CHANGELOG.md b/CHANGELOG.md index aeea8dd511..f6e152b489 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed +- Fixed parsing of consecutive percent operators, such as `=5%%`. [#1773](https://github.com/handsontable/hyperformula/pull/1773) - Fixed the `AVERAGEIF` function returning a division-by-zero error when the calculated average was `0`. [#1733](https://github.com/handsontable/hyperformula/pull/1733) - Fixed the localized names of `VSTACK` and `HSTACK` in 14 language packs to match Microsoft Excel. [#1748](https://github.com/handsontable/hyperformula/pull/1748) - Fixed the MAXPOOL and MEDIANPOOL functions throwing an uncaught `TypeError` instead of returning the `#VALUE!` error when the range dimensions are not a whole multiple of the window size and the stride. [#1718](https://github.com/handsontable/hyperformula/pull/1718) diff --git a/docs/guide/types-of-operators.md b/docs/guide/types-of-operators.md index 39f9ef9f55..403678e778 100644 --- a/docs/guide/types-of-operators.md +++ b/docs/guide/types-of-operators.md @@ -38,7 +38,11 @@ returns the negative value of that number. | :--- | :--- | :--- | :--- | | - | Unary minus | -a | Returns the negative of its argument. | | + | Unary plus | +a | Returns the positive of its argument. | -| % | Percent | a% | Calculate the percent of an argument. | +| % | Percent | a% | Divides its argument by 100. | + +HyperFormula supports consecutive percent operators. Each `%` divides the +preceding result by `100`. For example, `=5%%` is equivalent to `=(5%)%` +and returns `0.0005`, while `=5%%%` returns `0.000005`. ## Binary arithmetic operators diff --git a/src/parser/FormulaParser.ts b/src/parser/FormulaParser.ts index 82c70e1c1d..21eb2a043e 100644 --- a/src/parser/FormulaParser.ts +++ b/src/parser/FormulaParser.ts @@ -561,18 +561,18 @@ export class FormulaParser extends EmbeddedActionsParser { ])) as Ast }) + /** + * Rule for atomic expressions followed by zero or more percent operators. + */ private rightUnaryOpAtomicExpression: AstRule = this.RULE('rightUnaryOpAtomicExpression', () => { - const positiveAtomicExpression = this.SUBRULE(this.positiveAtomicExpression) - - const percentage = this.OPTION(() => { - return this.CONSUME(PercentOp) - }) as Maybe + const operand = this.SUBRULE(this.positiveAtomicExpression) + const percentOps: ExtendedToken[] = [] - if (percentage) { - return buildPercentOpAst(positiveAtomicExpression, percentage.leadingWhitespace) - } + this.MANY(() => { + percentOps.push(this.CONSUME(PercentOp) as ExtendedToken) + }) - return positiveAtomicExpression + return percentOps.reduce((ast, op) => buildPercentOpAst(ast, op.leadingWhitespace), operand) }) /**