diff --git a/HISTORY.md b/HISTORY.md index deb73875db..d14bb6f120 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,7 @@ # unpublished changes since 15.2.0 +- Fix: #3620 allow parentheses around exponents in unit parsing. - Docs: fix the browser example `rocket_trajectory_optimization.html` (#3654). Thanks @dvd101x. diff --git a/src/type/unit/Unit.js b/src/type/unit/Unit.js index 87498385b0..8e4da0195e 100644 --- a/src/type/unit/Unit.js +++ b/src/type/unit/Unit.js @@ -357,11 +357,24 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({ skipWhitespace() if (parseCharacter('^')) { skipWhitespace() + const hasParentheses = parseCharacter('(') + if (hasParentheses) { + skipWhitespace() + } + const p = parseNumber() if (p === null) { // No valid number found for the power! throw new SyntaxError('In "' + str + '", "^" must be followed by a floating-point number') } + + if (hasParentheses) { + skipWhitespace() + if (!parseCharacter(')')) { + throw new SyntaxError('Unmatched "(" in "' + text + '"') + } + } + power *= p } diff --git a/test/unit-tests/type/unit/Unit.test.js b/test/unit-tests/type/unit/Unit.test.js index b8a2fe96a5..7ec1a3dfaf 100644 --- a/test/unit-tests/type/unit/Unit.test.js +++ b/test/unit-tests/type/unit/Unit.test.js @@ -915,6 +915,33 @@ describe('Unit', function () { assert.strictEqual(unit1.units[0].power, 1) }) + it('should parse units with parenthesized powers correctly', function () { + let unit1 = math.unit('m ^ (1)') + assert.strictEqual(unit1.equals(math.unit('m')), true) + assert.strictEqual(unit1.units[0].unit.name, 'm') + assert.strictEqual(unit1.units[0].power, 1) + + unit1 = Unit.parse('m^(-2)') + assert.strictEqual(unit1.units[0].unit.name, 'm') + assert.strictEqual(unit1.units[0].power, -2) + + unit1 = Unit.parse('s^(0.5)') + assert.strictEqual(unit1.units[0].unit.name, 's') + assert.strictEqual(unit1.units[0].power, 0.5) + + unit1 = Unit.parse('m ^ ( 2 )') + assert.strictEqual(unit1.units[0].unit.name, 'm') + assert.strictEqual(unit1.units[0].power, 2) + + unit1 = Unit.parse('m^2') + assert.strictEqual(unit1.units[0].unit.name, 'm') + assert.strictEqual(unit1.units[0].power, 2) + + unit1 = Unit.parse('m^-2') + assert.strictEqual(unit1.units[0].unit.name, 'm') + assert.strictEqual(unit1.units[0].power, -2) + }) + it('should parse expressions with nested parentheses correctly', function () { let unit1 = Unit.parse('8.314 kg (m^2 / (s^2 / (K^-1 / mol)))') approxEqual(unit1.value, 8.314) @@ -975,6 +1002,8 @@ describe('Unit', function () { assert.throws(function () { Unit.parse('/meter') }, /Unexpected "\/"/) assert.throws(function () { Unit.parse('1 */ s') }, /Unexpected "\/"/) assert.throws(function () { Unit.parse('45 kg 34 m') }, /Unexpected "3"/) + assert.throws(function () { Unit.parse('m^()') }, SyntaxError) + assert.throws(function () { Unit.parse('m^(2') }, SyntaxError) }) it('should throw an exception when parsing an invalid type of argument', function () {