Skip to content

Commit

Permalink
Merge branch 'develop' into v14
Browse files Browse the repository at this point in the history
  • Loading branch information
josdejong committed Nov 13, 2024
2 parents d0f8b2b + 7b8e86f commit 6cf8a27
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 135 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -254,5 +254,6 @@ Vas Sudanagunta <[email protected]>
Brooks Smith <[email protected]>
Jmar L. Pineda <[email protected]>
gauravchawhan <[email protected]>
Neeraj Kumawat <[email protected]>

# Generated by tools/update-authors.js
11 changes: 9 additions & 2 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
# History

# unpublished changes since 13.2.0
# 2024-11-13, 13.2.2

- Fix: #1455 implicit multiplication of a fraction with unit `in` is incorrect
(#3315). Thanks @nkumawat34.

# 2024-11-06, 13.2.1

- Update to the latest version of `complex.js`.
- Fix `Index.dimension(dim)` accepting non-numeric input.
- Fix `Index.dimension(dim)` accepting non-numeric input.
- Fix: #3290 should validate variables names in method `Parser.set` (#3308).
Thanks @nkumawat34.

# 2024-10-02, 13.2.0

Expand Down
261 changes: 137 additions & 124 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mathjs",
"version": "13.2.0",
"version": "13.2.2",
"description": "Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with different data types like numbers, big numbers, complex numbers, fractions, units, and matrices.",
"author": "Jos de Jong <[email protected]> (https://github.com/josdejong)",
"homepage": "https://mathjs.org",
Expand Down Expand Up @@ -91,7 +91,7 @@
"sinon": "19.0.2",
"sylvester": "0.0.21",
"ts-node": "10.9.2",
"typescript": "5.6.3",
"typescript": "5.5.4",
"webpack": "5.96.1",
"zeros": "1.0.0"
},
Expand Down
24 changes: 22 additions & 2 deletions src/expression/Parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { factory } from '../utils/factory.js'
import { createEmptyMap, toObject } from '../utils/map.js'

const name = 'Parser'
const dependencies = ['evaluate']
const dependencies = ['evaluate', 'parse']

export const createParserClass = /* #__PURE__ */ factory(name, dependencies, ({ evaluate }) => {
export const createParserClass = /* #__PURE__ */ factory(name, dependencies, ({ evaluate, parse }) => {
/**
* @constructor Parser
* Parser contains methods to evaluate or parse expressions, and has a number
Expand Down Expand Up @@ -112,12 +112,32 @@ export const createParserClass = /* #__PURE__ */ factory(name, dependencies, ({
return this.scope
}

function isValidVariableName (name) {
if (name.length === 0) { return false }

for (let i = 0; i < name.length; i++) {
const cPrev = name.charAt(i - 1)
const c = name.charAt(i)
const cNext = name.charAt(i + 1)
const valid = parse.isAlpha(c, cPrev, cNext) || (i > 0 && parse.isDigit(c))

if (!valid) {
return false
}
}

return true
}

/**
* Set a symbol (a function or variable) by name from the parsers scope.
* @param {string} name
* @param {* | undefined} value
*/
Parser.prototype.set = function (name, value) {
if (!isValidVariableName(name)) {
throw new Error(`Invalid variable name: '${name}'. Variable names must follow the specified rules.`)
}
this.scope.set(name, value)
return value
}
Expand Down
2 changes: 1 addition & 1 deletion src/expression/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,7 @@ export const createParse = /* #__PURE__ */ factory(name, dependencies, ({
getTokenSkipNewline(state)

// Match the "symbol" part of the pattern, or a left parenthesis
if (state.tokenType === TOKENTYPE.SYMBOL || state.token === '(') {
if (state.tokenType === TOKENTYPE.SYMBOL || state.token === '(' || state.token === 'in') {
// We've matched the pattern "number / number symbol".
// Rewind once and build the "number / number" node; the symbol will be consumed later
Object.assign(state, tokenStates.pop())
Expand Down
2 changes: 1 addition & 1 deletion src/version.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export const version = '13.2.0'
export const version = '13.2.2'
// Note: This file is automatically generated when building math.js.
// Changes made in this file will be overwritten.
6 changes: 3 additions & 3 deletions test/browser-test-config/lambdatest-karma.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ module.exports = function (config) {
browserDisconnectTolerance: 1,
browserNoActivityTimeout: 90000,

concurrency: 1,
logLevel: config.LOG_DEBUG,
concurrency: Infinity,
logLevel: config.LOG_INFO,

browsers: Object.keys(customLaunchers),
customLaunchers,

singleRun: true,
autoWatch: true
autoWatch: false
}))
}
18 changes: 18 additions & 0 deletions test/unit-tests/expression/Parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,24 @@ describe('parser', function () {
assert.strictEqual(parser.evaluate('pi'), Math.PI)
})

it('should validate variable names', function () {
const parser = new Parser()

// Valid variable names
assert.strictEqual(parser.set('validVar', 42), 42)
assert.strictEqual(parser.evaluate('validVar'), 42)
assert.strictEqual(parser.set('_underscoreVar', 10), 10)
assert.strictEqual(parser.evaluate('_underscoreVar'), 10)
assert.strictEqual(parser.set('var123', 100), 100)
assert.strictEqual(parser.evaluate('var123'), 100)

// Invalid variable names
assert.throws(() => parser.set('123var', 5), /Invalid variable name/)
assert.throws(() => parser.set('var-with-hyphen', 5), /Invalid variable name/)
assert.throws(() => parser.set('var with space', 5), /Invalid variable name/)
assert.throws(() => parser.set('@specialChar', 5), /Invalid variable name/)
})

describe('security', function () {
it('should return undefined when accessing what appears to be inherited properties', function () {
try {
Expand Down
9 changes: 9 additions & 0 deletions test/unit-tests/expression/parse.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1531,6 +1531,15 @@ describe('parse', function () {
assert.strictEqual(parseAndStringifyWithParens('8.314 J/mol K'), '(8.314 J) / (mol K)')
})

it('should handle precedence with implicit multiplication, division, and the "in" operator', function () {
assert.strictEqual(parseAndStringifyWithParens('1/2 in'), '(1 / 2) in')
assert.strictEqual(parseAndStringifyWithParens('1/2 kg'), '(1 / 2) kg')
assert.strictEqual(parseAndStringifyWithParens('3 kg in lb'), '(3 kg) in lb')
assert.strictEqual(parseAndStringifyWithParens('2 m / 1 s'), '(2 m) / (1 s)')
assert.strictEqual(parseAndStringifyWithParens('5 / 10 in'), '(5 / 10) in')
assert.strictEqual(parseAndStringifyWithParens('10 lb + 1/2 lb'), '(10 lb) + ((1 / 2) lb)')
})

it('should throw an error when having an implicit multiplication between two numbers', function () {
assert.throws(function () { math.parse('2 3') }, /Unexpected part "3"/)
assert.throws(function () { math.parse('2 * 3 4') }, /Unexpected part "4"/)
Expand Down

0 comments on commit 6cf8a27

Please sign in to comment.