Glen Whitney 151926c75b
fix: simplify.resolve detects reference loop and throws error (#2405)
* docs: Enhance generation to pick up functions with a prefix

  For example, prior to this commit, docgenerator.js would miss
  simplify.resolve because it is not a direct key of the math
  object.

  Also incorporates any "throws" attributes in the comments into
  the generated documentation, and uses this to document the
  new error-case behavior of simplify.resolve to be added in the next
  commit.

* fix(resolve): Detect and throw errors for reference loops

  Also extends resolve to work inside all node types. Adds tests
  for both changes.

* docs: Add embedded docs for simplify.resolve et al.

  To support finding the embedded doc from the `math.simplify.resolve`
  function itself, required extending the search for objects with
  documentation one level deeper in the `help()` function. Added test for
  this search.

  Also added support for documenting throws in embedded docs.

* refactor(simplify): Move resolve and simplifyCore to top-level

  Also reverts changes searching for docs and embedded docs one level
  down in the naming hierarchy.
  Also splits tests for resolve and simplifyCore into their own files,
  reflecting their new top-level status.

* fix(resolve): Remaining changes as requested

  Also removed a stray blank line inadvertently introduced in
  docgenerator.js

* refactor: Declare resolve and simplifyCore as dependencies of simplify

  ... rather than explicitly loading them, which is unnecessary now that they
  are at top level.

* fix: Add dependencies to factoriesNumber

  Also register simplifyCore as a dependency to rationalize

Co-authored-by: Jos de Jong <wjosdejong@gmail.com>
2022-02-28 09:58:10 +01:00

65 lines
2.4 KiB
JavaScript

// test resolve
import assert from 'assert'
import math from '../../../../src/defaultInstance.js'
import { simplifyAndCompare } from './simplify.test.js'
describe('resolve', function () {
it('should substitute scoped constants', function () {
const sumxy = math.parse('x+y')
const collapsingScope = { x: math.parse('y'), y: math.parse('z') }
assert.strictEqual(
math.resolve(sumxy, { x: 1 }).toString(),
'1 + y'
) // direct
assert.strictEqual(
math.resolve(sumxy, collapsingScope).toString(),
'z + z'
)
assert.strictEqual(
math.resolve(
math.parse('[x,y,1,w]'), collapsingScope).toString(),
'[z, z, 1, w]'
)
simplifyAndCompare('x+y', 'x+y', {}) // operator
simplifyAndCompare('x+y', 'y+1', { x: 1 })
simplifyAndCompare('x+y', 'y+1', { x: math.parse('1') })
simplifyAndCompare('x+y', '3', { x: 1, y: 2 })
simplifyAndCompare('x+x+x', '3*x')
simplifyAndCompare('y', 'x+1', { y: math.parse('1+x') })
simplifyAndCompare('y', '3', { x: 2, y: math.parse('1+x') })
simplifyAndCompare('x+y', '3*x', { y: math.parse('x+x') })
simplifyAndCompare('x+y', '6', { x: 2, y: math.parse('x+x') })
simplifyAndCompare('x+(y+2-1-1)', '6', { x: 2, y: math.parse('x+x') }) // parentheses
simplifyAndCompare('log(x+y)', String(Math.log(6)), { x: 2, y: math.parse('x+x') }) // function
simplifyAndCompare('combinations( ceil(abs(sin(x)) * (y+3)), abs(x) )',
'combinations(ceil(0.9092974268256817 * (y + 3) ), 2)', { x: -2 })
simplifyAndCompare('size(text)[1]', '11', { text: 'hello world' })
})
it('should substitute scoped constants from Map like scopes', function () {
assert.strictEqual(
math.resolve(math.parse('x+y'), new Map([['x', 1]])).toString(), '1 + y'
) // direct
simplifyAndCompare('x+y', 'x+y', new Map()) // operator
simplifyAndCompare('x+y', 'y+1', new Map([['x', 1]]))
simplifyAndCompare('x+y', 'y+1', new Map([['x', math.parse('1')]]))
})
it('should throw an error in case of reference loop', function () {
const sumxy = math.parse('x+y')
assert.throws(
() => math.resolve(sumxy, { x: math.parse('x') }),
/ReferenceError.*\{x\}/)
assert.throws(
() => math.resolve(sumxy, {
y: math.parse('3z'),
z: math.parse('1-x'),
x: math.parse('cos(y)')
}),
/ReferenceError.*\{x, y, z\}/)
})
})