Skip to content

Commit

Permalink
Refactor mathWithTransform into a LazyMap. Follow-up of #2264
Browse files Browse the repository at this point in the history
  • Loading branch information
josdejong committed Jul 7, 2021
1 parent 7390a09 commit 6d3588e
Show file tree
Hide file tree
Showing 27 changed files with 155 additions and 79 deletions.
8 changes: 3 additions & 5 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,9 @@ function completer (text) {

// math functions and constants
const ignore = ['expr', 'type']
for (const func in math.expression.mathWithTransform) {
if (hasOwnProperty(math.expression.mathWithTransform, func)) {
if (func.indexOf(keyword) === 0 && ignore.indexOf(func) === -1) {
matches.push(func)
}
for (const key of math.expression.mathWithTransform.keys()) {
if (key.indexOf(keyword) === 0 && ignore.indexOf(key) === -1) {
matches.push(key)
}
}

Expand Down
8 changes: 5 additions & 3 deletions src/core/create.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import './../utils/polyfills.js'
import { LazyMap } from '../utils/map.js'
import { deepFlatten, isLegacyFactory, values } from '../utils/object.js'
import * as emitter from './../utils/emitter.js'
import { importFactory } from './function/import.js'
Expand Down Expand Up @@ -145,11 +146,12 @@ export function create (factories, config) {
// load config function and apply provided config
math.config = configFactory(configInternal, math.emit)

const mathWithTransform = new LazyMap()
mathWithTransform.set('config', math.config)

math.expression = {
transform: {},
mathWithTransform: {
config: math.config
}
mathWithTransform
}

// cached factories and instances used by function load
Expand Down
12 changes: 6 additions & 6 deletions src/core/function/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,23 +183,23 @@ export function importFactory (typed, load, math, importedFactories) {
if (value && typeof value.transform === 'function') {
math.expression.transform[name] = value.transform
if (allowedInExpressions(name)) {
math.expression.mathWithTransform[name] = value.transform
math.expression.mathWithTransform.set(name, value.transform)
}
} else {
// remove existing transform
delete math.expression.transform[name]
if (allowedInExpressions(name)) {
math.expression.mathWithTransform[name] = value
math.expression.mathWithTransform.set(name, value)
}
}
}

function _deleteTransform (name) {
delete math.expression.transform[name]
if (allowedInExpressions(name)) {
math.expression.mathWithTransform[name] = math[name]
math.expression.mathWithTransform.set(name, math[name])
} else {
delete math.expression.mathWithTransform[name]
math.expression.mathWithTransform.delete(name)
}
}

Expand Down Expand Up @@ -302,7 +302,7 @@ export function importFactory (typed, load, math, importedFactories) {
_deleteTransform(name)
} else {
if (isTransformFunctionFactory(factory) || factoryAllowedInExpressions(factory)) {
lazy(math.expression.mathWithTransform, name, () => namespace[name])
math.expression.mathWithTransform.setLazy(name, () => namespace[name])
}
}
} else {
Expand All @@ -313,7 +313,7 @@ export function importFactory (typed, load, math, importedFactories) {
_deleteTransform(name)
} else {
if (isTransformFunctionFactory(factory) || factoryAllowedInExpressions(factory)) {
lazy(math.expression.mathWithTransform, name, () => namespace[name])
math.expression.mathWithTransform.setLazy(name, () => namespace[name])
}
}
}
Expand Down
8 changes: 3 additions & 5 deletions src/expression/function/help.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { factory } from '../../utils/factory.js'
import { getSafeProperty } from '../../utils/customs.js'
import { factory } from '../../utils/factory.js'
import { embeddedDocs } from '../embeddedDocs/embeddedDocs.js'
import { hasOwnProperty } from '../../utils/object.js'

const name = 'help'
const dependencies = ['typed', 'mathWithTransform', 'Help']
Expand All @@ -27,13 +26,12 @@ export const createHelp = /* #__PURE__ */ factory(name, dependencies, ({ typed,
*/
return typed(name, {
any: function (search) {
let prop
let searchName = search

if (typeof search !== 'string') {
for (prop in mathWithTransform) {
for (const prop of mathWithTransform.keys()) {
// search in functions and constants
if (hasOwnProperty(mathWithTransform, prop) && (search === mathWithTransform[prop])) {
if (search === mathWithTransform.get(prop)) {
searchName = prop
break
}
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/AccessorNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const createAccessorNode = /* #__PURE__ */ factory(name, dependencies, ({
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/ArrayNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const createArrayNode = /* #__PURE__ */ factory(name, dependencies, ({ No
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/BlockNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const createBlockNode = /* #__PURE__ */ factory(name, dependencies, ({ Re
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/ConditionalNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const createConditionalNode = /* #__PURE__ */ factory(name, dependencies,
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/ConstantNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const createConstantNode = /* #__PURE__ */ factory(name, dependencies, ({
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/FunctionAssignmentNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const createFunctionAssignmentNode = /* #__PURE__ */ factory(name, depend
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/FunctionNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const createFunctionNode = /* #__PURE__ */ factory(name, dependencies, ({
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/IndexNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const createIndexNode = /* #__PURE__ */ factory(name, dependencies, ({ Ra
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
11 changes: 4 additions & 7 deletions src/expression/node/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,16 @@ export const createNode = /* #__PURE__ */ factory(name, dependencies, ({ mathWit

Node.prototype.comment = ''

// Wrap the mathWithTransform object in a map
const math = createMap(mathWithTransform)

/**
* Compile the node into an optimized, evauatable JavaScript function
* @return {{evaluate: function([Object])}} object
* Compile the node into an optimized, evaluable JavaScript function
* @return {{evaluate: function(Object?)}} object
* Returns an object with a function 'evaluate',
* which can be invoked as expr.evaluate([scope: Object]),
* where scope is an optional object with
* variables.
*/
Node.prototype.compile = function () {
const expr = this._compile(math, {})
const expr = this._compile(mathWithTransform, {})
const args = {}
const context = null

Expand All @@ -64,7 +61,7 @@ export const createNode = /* #__PURE__ */ factory(name, dependencies, ({ mathWit
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/ObjectNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const createObjectNode = /* #__PURE__ */ factory(name, dependencies, ({ N
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/OperatorNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const createOperatorNode = /* #__PURE__ */ factory(name, dependencies, ({
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/ParenthesisNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const createParenthesisNode = /* #__PURE__ */ factory(name, dependencies,
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/RangeNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const createRangeNode = /* #__PURE__ */ factory(name, dependencies, ({ No
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/RelationalNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const createRelationalNode = /* #__PURE__ */ factory(name, dependencies,
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
2 changes: 1 addition & 1 deletion src/expression/node/SymbolNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const createSymbolNode = /* #__PURE__ */ factory(name, dependencies, ({ m
* Compile a node into a JavaScript function.
* This basically pre-calculates as much as possible and only leaves open
* calculations which depend on a dynamic scope with variables.
* @param {Object} math Math.js namespace with functions and constants.
* @param {Map} math Math.js namespace with functions and constants.
* @param {Object} argNames An object with argument names as key and `true`
* as value. Used in the SymbolNode to optimize
* for arguments from user assigned functions
Expand Down
6 changes: 3 additions & 3 deletions src/function/algebra/simplify/simplifyConstant.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const createSimplifyConstant = /* #__PURE__ */ factory(name, dependencies

function _eval (fnname, args, options) {
try {
return _toNumber(mathWithTransform[fnname].apply(null, args), options)
return _toNumber(mathWithTransform.get(fnname).apply(null, args), options)
} catch (ignore) {
// sometimes the implicit type conversion causes the evaluation to fail, so we'll try again after removing Fractions
args = args.map(function (x) {
Expand All @@ -47,7 +47,7 @@ export const createSimplifyConstant = /* #__PURE__ */ factory(name, dependencies
}
return x
})
return _toNumber(mathWithTransform[fnname].apply(null, args), options)
return _toNumber(mathWithTransform.get(fnname).apply(null, args), options)
}
}

Expand Down Expand Up @@ -181,7 +181,7 @@ export const createSimplifyConstant = /* #__PURE__ */ factory(name, dependencies
}
return node
case 'FunctionNode':
if (mathWithTransform[node.name] && mathWithTransform[node.name].rawArgs) {
if (mathWithTransform.has(node.name) && mathWithTransform.get(node.name).rawArgs) {
return node
}
{
Expand Down
69 changes: 68 additions & 1 deletion src/utils/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,73 @@ export class ObjectWrappingMap {
}
}

/**
* A lazy map implementation
*
* instead of setting a value, a value resolver can be set using `LazyMap.setLazy`.
* The value will be resolved the first time it is requested, and is resolved only once.
*/
export class LazyMap {
constructor () {
this.values = new Map()
this.valueResolvers = new Map()
}

keys () {
return this.valueResolvers.keys()
}

/**
* @param {string} key
* @returns {*}
*/
get (key) {
if (!this.values.has(key)) {
if (this.valueResolvers.has(key)) {
const resolver = this.valueResolvers.get(key)
const value = resolver()
this.values.set(key, value)
}
}

return this.values.get(key)
}

/**
* @param {string} key
* @param {*} value
* @returns {LazyMap}
*/
set (key, value) {
this.values.set(key, value)
this.valueResolvers.set(key, () => value)
return this
}

/**
* @param {string} key
* @param {function() : *} resolver
* @returns {LazyMap}
*/
setLazy (key, resolver) {
if (typeof resolver !== 'function') {
throw new TypeError('Value resolver must be a function')
}

this.values.delete(key)
this.valueResolvers.set(key, resolver)
return this
}

/**
* @param {string} key
* @returns {boolean}
*/
has (key) {
return this.valueResolvers.has(key)
}
}

/**
* Creates an empty map, or whatever your platform's polyfill is.
*
Expand All @@ -44,7 +111,7 @@ export function createEmptyMap () {
/**
* Creates a Map from the given object.
*
* @param { Map | { [key: string]: unknown } | undefined } mapOrObject
* @param { Map | Object<string, *> } [mapOrObject]
* @returns
*/
export function createMap (mapOrObject) {
Expand Down
Loading

0 comments on commit 6d3588e

Please sign in to comment.