diff --git a/src/expression/node/AccessorNode.js b/src/expression/node/AccessorNode.js
index b3c289986c..2d028c51fb 100644
--- a/src/expression/node/AccessorNode.js
+++ b/src/expression/node/AccessorNode.js
@@ -16,10 +16,11 @@ import { accessFactory } from './utils/access.js'
const name = 'AccessorNode'
const dependencies = [
'subset',
+ 'fromJSON',
'Node'
]
-export const createAccessorNode = /* #__PURE__ */ factory(name, dependencies, ({ subset, Node }) => {
+export const createAccessorNode = /* #__PURE__ */ factory(name, dependencies, ({ subset, Node, fromJSON }) => {
const access = accessFactory({ subset })
/**
@@ -191,7 +192,7 @@ export const createAccessorNode = /* #__PURE__ */ factory(name, dependencies, ({
toJSON () {
return {
mathjs: name,
- object: this.object,
+ object: this.object?.toJSON(),
index: this.index
}
}
@@ -205,7 +206,7 @@ export const createAccessorNode = /* #__PURE__ */ factory(name, dependencies, ({
* @returns {AccessorNode}
*/
static fromJSON (json) {
- return new AccessorNode(json.object, json.index)
+ return new AccessorNode(fromJSON(json.object), json.index)
}
}
diff --git a/src/expression/node/ArrayNode.js b/src/expression/node/ArrayNode.js
index 800e536029..783af6471e 100644
--- a/src/expression/node/ArrayNode.js
+++ b/src/expression/node/ArrayNode.js
@@ -4,10 +4,11 @@ import { factory } from '../../utils/factory.js'
const name = 'ArrayNode'
const dependencies = [
- 'Node'
+ 'Node',
+ 'fromJSON'
]
-export const createArrayNode = /* #__PURE__ */ factory(name, dependencies, ({ Node }) => {
+export const createArrayNode = /* #__PURE__ */ factory(name, dependencies, ({ Node, fromJSON }) => {
class ArrayNode extends Node {
/**
* @constructor ArrayNode
@@ -117,7 +118,7 @@ export const createArrayNode = /* #__PURE__ */ factory(name, dependencies, ({ No
toJSON () {
return {
mathjs: name,
- items: this.items
+ items: this.items?.map(i => i.toJSON())
}
}
@@ -129,7 +130,7 @@ export const createArrayNode = /* #__PURE__ */ factory(name, dependencies, ({ No
* @returns {ArrayNode}
*/
static fromJSON (json) {
- return new ArrayNode(json.items)
+ return new ArrayNode(json.items.map(fromJSON))
}
/**
diff --git a/src/expression/node/AssignmentNode.js b/src/expression/node/AssignmentNode.js
index 87805f110f..f11a1578f1 100644
--- a/src/expression/node/AssignmentNode.js
+++ b/src/expression/node/AssignmentNode.js
@@ -9,10 +9,11 @@ const name = 'AssignmentNode'
const dependencies = [
'subset',
'?matrix', // FIXME: should not be needed at all, should be handled by subset
- 'Node'
+ 'Node',
+ 'fromJSON'
]
-export const createAssignmentNode = /* #__PURE__ */ factory(name, dependencies, ({ subset, matrix, Node }) => {
+export const createAssignmentNode = /* #__PURE__ */ factory(name, dependencies, ({ subset, matrix, Node, fromJSON }) => {
const access = accessFactory({ subset })
const assign = assignFactory({ subset, matrix })
@@ -258,9 +259,9 @@ export const createAssignmentNode = /* #__PURE__ */ factory(name, dependencies,
toJSON () {
return {
mathjs: name,
- object: this.object,
- index: this.index,
- value: this.value
+ object: this.object?.toJSON(),
+ index: this.index?.toJSON(),
+ value: this.value?.toJSON()
}
}
@@ -273,7 +274,7 @@ export const createAssignmentNode = /* #__PURE__ */ factory(name, dependencies,
* @returns {AssignmentNode}
*/
static fromJSON (json) {
- return new AssignmentNode(json.object, json.index, json.value)
+ return new AssignmentNode(fromJSON(json.object), fromJSON(json.index), fromJSON(json.value))
}
/**
diff --git a/src/expression/node/BlockNode.js b/src/expression/node/BlockNode.js
index 75b041d916..ed936f9a1e 100644
--- a/src/expression/node/BlockNode.js
+++ b/src/expression/node/BlockNode.js
@@ -3,184 +3,202 @@ import { forEach, map } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'BlockNode'
-const dependencies = [
- 'ResultSet',
- 'Node'
-]
-
-export const createBlockNode = /* #__PURE__ */ factory(name, dependencies, ({ ResultSet, Node }) => {
- class BlockNode extends Node {
- /**
- * @constructor BlockNode
- * @extends {Node}
- * Holds a set with blocks
- * @param {Array.<{node: Node} | {node: Node, visible: boolean}>} blocks
- * An array with blocks, where a block is constructed as an
- * Object with properties block, which is a Node, and visible,
- * which is a boolean. The property visible is optional and
- * is true by default
- */
- constructor (blocks) {
- super()
- // validate input, copy blocks
- if (!Array.isArray(blocks)) throw new Error('Array expected')
- this.blocks = blocks.map(function (block) {
- const node = block && block.node
- const visible = block &&
- block.visible !== undefined
- ? block.visible
- : true
-
- if (!isNode(node)) throw new TypeError('Property "node" must be a Node')
- if (typeof visible !== 'boolean') { throw new TypeError('Property "visible" must be a boolean') }
-
- return { node, visible }
- })
- }
+const dependencies = ['ResultSet', 'Node', 'fromJSON']
+
+export const createBlockNode = /* #__PURE__ */ factory(
+ name,
+ dependencies,
+ ({ ResultSet, Node, fromJSON }) => {
+ class BlockNode extends Node {
+ /**
+ * @constructor BlockNode
+ * @extends {Node}
+ * Holds a set with blocks
+ * @param {Array.<{node: Node} | {node: Node, visible: boolean}>} blocks
+ * An array with blocks, where a block is constructed as an
+ * Object with properties block, which is a Node, and visible,
+ * which is a boolean. The property visible is optional and
+ * is true by default
+ */
+ constructor (blocks) {
+ super()
+ // validate input, copy blocks
+ if (!Array.isArray(blocks)) throw new Error('Array expected')
+ this.blocks = blocks.map(function (block) {
+ const node = block && block.node
+ const visible =
+ block && block.visible !== undefined ? block.visible : true
+
+ if (!isNode(node)) { throw new TypeError('Property "node" must be a Node') }
+ if (typeof visible !== 'boolean') {
+ throw new TypeError('Property "visible" must be a boolean')
+ }
- static name = name
- get type () { return name }
- get isBlockNode () { return true }
-
- /**
- * 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 {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
- * (see FunctionAssignmentNode) or special symbols
- * like `end` (see IndexNode).
- * @return {function} Returns a function which can be called like:
- * evalNode(scope: Object, args: Object, context: *)
- */
- _compile (math, argNames) {
- const evalBlocks = map(this.blocks, function (block) {
- return {
- evaluate: block.node._compile(math, argNames),
- visible: block.visible
- }
- })
+ return { node, visible }
+ })
+ }
+
+ static name = name
+ get type () {
+ return name
+ }
- return function evalBlockNodes (scope, args, context) {
- const results = []
+ get isBlockNode () {
+ return true
+ }
- forEach(evalBlocks, function evalBlockNode (block) {
- const result = block.evaluate(scope, args, context)
- if (block.visible) {
- results.push(result)
+ /**
+ * 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 {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
+ * (see FunctionAssignmentNode) or special symbols
+ * like `end` (see IndexNode).
+ * @return {function} Returns a function which can be called like:
+ * evalNode(scope: Object, args: Object, context: *)
+ */
+ _compile (math, argNames) {
+ const evalBlocks = map(this.blocks, function (block) {
+ return {
+ evaluate: block.node._compile(math, argNames),
+ visible: block.visible
}
})
- return new ResultSet(results)
- }
- }
+ return function evalBlockNodes (scope, args, context) {
+ const results = []
- /**
- * Execute a callback for each of the child blocks of this node
- * @param {function(child: Node, path: string, parent: Node)} callback
- */
- forEach (callback) {
- for (let i = 0; i < this.blocks.length; i++) {
- callback(this.blocks[i].node, 'blocks[' + i + '].node', this)
+ forEach(evalBlocks, function evalBlockNode (block) {
+ const result = block.evaluate(scope, args, context)
+ if (block.visible) {
+ results.push(result)
+ }
+ })
+
+ return new ResultSet(results)
+ }
}
- }
- /**
- * Create a new BlockNode whose children are the results of calling
- * the provided callback function for each child of the original node.
- * @param {function(child: Node, path: string, parent: Node): Node} callback
- * @returns {BlockNode} Returns a transformed copy of the node
- */
- map (callback) {
- const blocks = []
- for (let i = 0; i < this.blocks.length; i++) {
- const block = this.blocks[i]
- const node = this._ifNode(
- callback(block.node, 'blocks[' + i + '].node', this))
- blocks[i] = {
- node,
- visible: block.visible
+ /**
+ * Execute a callback for each of the child blocks of this node
+ * @param {function(child: Node, path: string, parent: Node)} callback
+ */
+ forEach (callback) {
+ for (let i = 0; i < this.blocks.length; i++) {
+ callback(this.blocks[i].node, 'blocks[' + i + '].node', this)
}
}
- return new BlockNode(blocks)
- }
- /**
- * Create a clone of this node, a shallow copy
- * @return {BlockNode}
- */
- clone () {
- const blocks = this.blocks.map(function (block) {
- return {
- node: block.node,
- visible: block.visible
+ /**
+ * Create a new BlockNode whose children are the results of calling
+ * the provided callback function for each child of the original node.
+ * @param {function(child: Node, path: string, parent: Node): Node} callback
+ * @returns {BlockNode} Returns a transformed copy of the node
+ */
+ map (callback) {
+ const blocks = []
+ for (let i = 0; i < this.blocks.length; i++) {
+ const block = this.blocks[i]
+ const node = this._ifNode(
+ callback(block.node, 'blocks[' + i + '].node', this)
+ )
+ blocks[i] = {
+ node,
+ visible: block.visible
+ }
}
- })
+ return new BlockNode(blocks)
+ }
- return new BlockNode(blocks)
- }
+ /**
+ * Create a clone of this node, a shallow copy
+ * @return {BlockNode}
+ */
+ clone () {
+ const blocks = this.blocks.map(function (block) {
+ return {
+ node: block.node,
+ visible: block.visible
+ }
+ })
- /**
- * Get string representation
- * @param {Object} options
- * @return {string} str
- * @override
- */
- _toString (options) {
- return this.blocks.map(function (param) {
- return param.node.toString(options) + (param.visible ? '' : ';')
- }).join('\n')
- }
+ return new BlockNode(blocks)
+ }
- /**
- * Get a JSON representation of the node
- * @returns {Object}
- */
- toJSON () {
- return {
- mathjs: name,
- blocks: this.blocks
+ /**
+ * Get string representation
+ * @param {Object} options
+ * @return {string} str
+ * @override
+ */
+ _toString (options) {
+ return this.blocks
+ .map(function (param) {
+ return param.node.toString(options) + (param.visible ? '' : ';')
+ })
+ .join('\n')
}
- }
- /**
- * Instantiate an BlockNode from its JSON representation
- * @param {Object} json
- * An object structured like
- * `{"mathjs": "BlockNode", blocks: [{node: ..., visible: false}, ...]}`,
- * where mathjs is optional
- * @returns {BlockNode}
- */
- static fromJSON (json) {
- return new BlockNode(json.blocks)
- }
+ /**
+ * Get a JSON representation of the node
+ * @returns {Object}
+ */
+ toJSON () {
+ return {
+ mathjs: name,
+ blocks: this.blocks.map((b) => ({ ...b, node: b.node.toJSON() }))
+ }
+ }
- /**
- * Get HTML representation
- * @param {Object} options
- * @return {string} str
- * @override
- */
- toHTML (options) {
- return this.blocks.map(function (param) {
- return param.node.toHTML(options) +
- (param.visible ? '' : ';')
- }).join('
')
- }
+ /**
+ * Instantiate an BlockNode from its JSON representation
+ * @param {Object} json
+ * An object structured like
+ * `{"mathjs": "BlockNode", blocks: [{node: ..., visible: false}, ...]}`,
+ * where mathjs is optional
+ * @returns {BlockNode}
+ */
+ static fromJSON (json) {
+ return new BlockNode(
+ json.blocks.map((b) => ({ ...b, node: fromJSON(b.node) }))
+ )
+ }
+
+ /**
+ * Get HTML representation
+ * @param {Object} options
+ * @return {string} str
+ * @override
+ */
+ toHTML (options) {
+ return this.blocks
+ .map(function (param) {
+ return (
+ param.node.toHTML(options) +
+ (param.visible ? '' : ';')
+ )
+ })
+ .join('
')
+ }
- /**
- * Get LaTeX representation
- * @param {Object} options
- * @return {string} str
- */
- _toTex (options) {
- return this.blocks.map(function (param) {
- return param.node.toTex(options) + (param.visible ? '' : ';')
- }).join('\\;\\;\n')
+ /**
+ * Get LaTeX representation
+ * @param {Object} options
+ * @return {string} str
+ */
+ _toTex (options) {
+ return this.blocks
+ .map(function (param) {
+ return param.node.toTex(options) + (param.visible ? '' : ';')
+ })
+ .join('\\;\\;\n')
+ }
}
- }
- return BlockNode
-}, { isClass: true, isNode: true })
+ return BlockNode
+ },
+ { isClass: true, isNode: true }
+)
diff --git a/src/expression/node/ConditionalNode.js b/src/expression/node/ConditionalNode.js
index 121f4df6b4..c7abc8322f 100644
--- a/src/expression/node/ConditionalNode.js
+++ b/src/expression/node/ConditionalNode.js
@@ -4,10 +4,11 @@ import { getPrecedence } from '../operators.js'
const name = 'ConditionalNode'
const dependencies = [
- 'Node'
+ 'Node',
+ 'fromJSON'
]
-export const createConditionalNode = /* #__PURE__ */ factory(name, dependencies, ({ Node }) => {
+export const createConditionalNode = /* #__PURE__ */ factory(name, dependencies, ({ Node, fromJSON }) => {
/**
* Test whether a condition is met
* @param {*} condition
@@ -176,9 +177,9 @@ export const createConditionalNode = /* #__PURE__ */ factory(name, dependencies,
toJSON () {
return {
mathjs: name,
- condition: this.condition,
- trueExpr: this.trueExpr,
- falseExpr: this.falseExpr
+ condition: this.condition?.toJSON(),
+ trueExpr: this.trueExpr?.toJSON(),
+ falseExpr: this.falseExpr?.toJSON()
}
}
@@ -196,7 +197,7 @@ export const createConditionalNode = /* #__PURE__ */ factory(name, dependencies,
* @returns {ConditionalNode}
*/
static fromJSON (json) {
- return new ConditionalNode(json.condition, json.trueExpr, json.falseExpr)
+ return new ConditionalNode(fromJSON(json.condition), fromJSON(json.trueExpr), fromJSON(json.falseExpr))
}
/**
diff --git a/src/expression/node/FunctionAssignmentNode.js b/src/expression/node/FunctionAssignmentNode.js
index 4adce75daf..21b4c0381b 100644
--- a/src/expression/node/FunctionAssignmentNode.js
+++ b/src/expression/node/FunctionAssignmentNode.js
@@ -10,10 +10,11 @@ import { factory } from '../../utils/factory.js'
const name = 'FunctionAssignmentNode'
const dependencies = [
'typed',
- 'Node'
+ 'Node',
+ 'fromJSON'
]
-export const createFunctionAssignmentNode = /* #__PURE__ */ factory(name, dependencies, ({ typed, Node }) => {
+export const createFunctionAssignmentNode = /* #__PURE__ */ factory(name, dependencies, ({ typed, Node, fromJSON }) => {
/**
* Is parenthesis needed?
* @param {Node} node
@@ -174,7 +175,7 @@ export const createFunctionAssignmentNode = /* #__PURE__ */ factory(name, depend
type: types[index]
}
}),
- expr: this.expr
+ expr: this.expr?.toJSON()
}
}
@@ -190,7 +191,7 @@ export const createFunctionAssignmentNode = /* #__PURE__ */ factory(name, depend
* @returns {FunctionAssignmentNode}
*/
static fromJSON (json) {
- return new FunctionAssignmentNode(json.name, json.params, json.expr)
+ return new FunctionAssignmentNode(json.name, json.params, fromJSON(json.expr))
}
/**
diff --git a/src/expression/node/FunctionNode.js b/src/expression/node/FunctionNode.js
index c873bff1c7..98f21cd576 100644
--- a/src/expression/node/FunctionNode.js
+++ b/src/expression/node/FunctionNode.js
@@ -10,10 +10,11 @@ const name = 'FunctionNode'
const dependencies = [
'math',
'Node',
- 'SymbolNode'
+ 'SymbolNode',
+ 'fromJSON'
]
-export const createFunctionNode = /* #__PURE__ */ factory(name, dependencies, ({ math, Node, SymbolNode }) => {
+export const createFunctionNode = /* #__PURE__ */ factory(name, dependencies, ({ math, Node, SymbolNode, fromJSON }) => {
/* format to fixed length */
const strin = entity => format(entity, { truncate: 78 })
@@ -376,8 +377,8 @@ export const createFunctionNode = /* #__PURE__ */ factory(name, dependencies, ({
toJSON () {
return {
mathjs: name,
- fn: this.fn,
- args: this.args
+ fn: this.fn?.toJSON(),
+ args: this.args?.map(a => a.toJSON())
}
}
@@ -389,7 +390,7 @@ export const createFunctionNode = /* #__PURE__ */ factory(name, dependencies, ({
* @returns {FunctionNode}
*/
static fromJSON = function (json) {
- return new FunctionNode(json.fn, json.args)
+ return new FunctionNode(fromJSON(json.fn), json.args?.map(fromJSON))
}
/**
diff --git a/src/expression/node/IndexNode.js b/src/expression/node/IndexNode.js
index bf4186bbc1..9b04a56fca 100644
--- a/src/expression/node/IndexNode.js
+++ b/src/expression/node/IndexNode.js
@@ -7,10 +7,11 @@ import { escape } from '../../utils/string.js'
const name = 'IndexNode'
const dependencies = [
'Node',
- 'size'
+ 'size',
+ 'fromJSON'
]
-export const createIndexNode = /* #__PURE__ */ factory(name, dependencies, ({ Node, size }) => {
+export const createIndexNode = /* #__PURE__ */ factory(name, dependencies, ({ Node, size, fromJSON }) => {
class IndexNode extends Node {
/**
* @constructor IndexNode
@@ -182,7 +183,7 @@ export const createIndexNode = /* #__PURE__ */ factory(name, dependencies, ({ No
toJSON () {
return {
mathjs: name,
- dimensions: this.dimensions,
+ dimensions: this.dimensions?.map(d => d.toJSON()),
dotNotation: this.dotNotation
}
}
@@ -196,7 +197,7 @@ export const createIndexNode = /* #__PURE__ */ factory(name, dependencies, ({ No
* @returns {IndexNode}
*/
static fromJSON (json) {
- return new IndexNode(json.dimensions, json.dotNotation)
+ return new IndexNode(json.dimensions?.map(fromJSON), json.dotNotation)
}
/**
diff --git a/src/expression/node/ObjectNode.js b/src/expression/node/ObjectNode.js
index 99c8b5b0dc..4c209d7c10 100644
--- a/src/expression/node/ObjectNode.js
+++ b/src/expression/node/ObjectNode.js
@@ -6,10 +6,11 @@ import { factory } from '../../utils/factory.js'
const name = 'ObjectNode'
const dependencies = [
- 'Node'
+ 'Node',
+ 'fromJSON'
]
-export const createObjectNode = /* #__PURE__ */ factory(name, dependencies, ({ Node }) => {
+export const createObjectNode = /* #__PURE__ */ factory(name, dependencies, ({ Node, fromJSON }) => {
class ObjectNode extends Node {
/**
* @constructor ObjectNode
@@ -148,7 +149,7 @@ export const createObjectNode = /* #__PURE__ */ factory(name, dependencies, ({ N
toJSON () {
return {
mathjs: name,
- properties: this.properties
+ properties: Object.keys(this.properties).reduce((object, prop) => ({ ...object, [prop]: this.properties[prop]?.toJSON() }), {})
}
}
@@ -160,7 +161,7 @@ export const createObjectNode = /* #__PURE__ */ factory(name, dependencies, ({ N
* @returns {ObjectNode}
*/
static fromJSON (json) {
- return new ObjectNode(json.properties)
+ return new ObjectNode(Object.keys(json.properties).reduce((object, prop) => ({ ...object, [prop]: fromJSON(json.properties[prop]) }), {}))
}
/**
diff --git a/src/expression/node/OperatorNode.js b/src/expression/node/OperatorNode.js
index 21eeeb744c..915af1d409 100644
--- a/src/expression/node/OperatorNode.js
+++ b/src/expression/node/OperatorNode.js
@@ -8,10 +8,11 @@ import { factory } from '../../utils/factory.js'
const name = 'OperatorNode'
const dependencies = [
- 'Node'
+ 'Node',
+ 'fromJSON'
]
-export const createOperatorNode = /* #__PURE__ */ factory(name, dependencies, ({ Node }) => {
+export const createOperatorNode = /* #__PURE__ */ factory(name, dependencies, ({ Node, fromJSON }) => {
/**
* Returns true if the expression starts with a constant, under
* the current parenthesization:
@@ -463,7 +464,7 @@ export const createOperatorNode = /* #__PURE__ */ factory(name, dependencies, ({
mathjs: name,
op: this.op,
fn: this.fn,
- args: this.args,
+ args: (this.args || []).map(a => a.toJSON()),
implicit: this.implicit,
isPercentage: this.isPercentage
}
@@ -484,7 +485,8 @@ export const createOperatorNode = /* #__PURE__ */ factory(name, dependencies, ({
*/
static fromJSON (json) {
return new OperatorNode(
- json.op, json.fn, json.args, json.implicit, json.isPercentage)
+ json.op, json.fn, json.args?.map(fromJSON), json.implicit, json.isPercentage
+ )
}
/**
diff --git a/src/expression/node/ParenthesisNode.js b/src/expression/node/ParenthesisNode.js
index 3a6848d3d4..17e18a3bdc 100644
--- a/src/expression/node/ParenthesisNode.js
+++ b/src/expression/node/ParenthesisNode.js
@@ -3,10 +3,11 @@ import { factory } from '../../utils/factory.js'
const name = 'ParenthesisNode'
const dependencies = [
- 'Node'
+ 'Node',
+ 'fromJSON'
]
-export const createParenthesisNode = /* #__PURE__ */ factory(name, dependencies, ({ Node }) => {
+export const createParenthesisNode = /* #__PURE__ */ factory(name, dependencies, ({ Node, fromJSON }) => {
class ParenthesisNode extends Node {
/**
* @constructor ParenthesisNode
@@ -102,7 +103,7 @@ export const createParenthesisNode = /* #__PURE__ */ factory(name, dependencies,
* @returns {Object}
*/
toJSON () {
- return { mathjs: name, content: this.content }
+ return { mathjs: name, content: this.content?.toJSON() }
}
/**
@@ -113,7 +114,7 @@ export const createParenthesisNode = /* #__PURE__ */ factory(name, dependencies,
* @returns {ParenthesisNode}
*/
static fromJSON (json) {
- return new ParenthesisNode(json.content)
+ return new ParenthesisNode(fromJSON(json.content))
}
/**
diff --git a/src/expression/node/RangeNode.js b/src/expression/node/RangeNode.js
index dd814d058a..e673a4f20d 100644
--- a/src/expression/node/RangeNode.js
+++ b/src/expression/node/RangeNode.js
@@ -4,10 +4,11 @@ import { getPrecedence } from '../operators.js'
const name = 'RangeNode'
const dependencies = [
- 'Node'
+ 'Node',
+ 'fromJSON'
]
-export const createRangeNode = /* #__PURE__ */ factory(name, dependencies, ({ Node }) => {
+export const createRangeNode = /* #__PURE__ */ factory(name, dependencies, ({ Node, fromJSON }) => {
/**
* Calculate the necessary parentheses
* @param {Node} node
@@ -194,9 +195,9 @@ export const createRangeNode = /* #__PURE__ */ factory(name, dependencies, ({ No
toJSON () {
return {
mathjs: name,
- start: this.start,
- end: this.end,
- step: this.step
+ start: this.start?.toJSON(),
+ end: this.end?.toJSON(),
+ step: this.step?.toJSON()
}
}
@@ -209,7 +210,7 @@ export const createRangeNode = /* #__PURE__ */ factory(name, dependencies, ({ No
* @returns {RangeNode}
*/
static fromJSON (json) {
- return new RangeNode(json.start, json.end, json.step)
+ return new RangeNode(fromJSON(json.start), fromJSON(json.end), fromJSON(json.step))
}
/**
diff --git a/src/expression/node/RelationalNode.js b/src/expression/node/RelationalNode.js
index 66dffea1c0..aaccabb08c 100644
--- a/src/expression/node/RelationalNode.js
+++ b/src/expression/node/RelationalNode.js
@@ -6,10 +6,11 @@ import { factory } from '../../utils/factory.js'
const name = 'RelationalNode'
const dependencies = [
- 'Node'
+ 'Node',
+ 'fromJSON'
]
-export const createRelationalNode = /* #__PURE__ */ factory(name, dependencies, ({ Node }) => {
+export const createRelationalNode = /* #__PURE__ */ factory(name, dependencies, ({ Node, fromJSON }) => {
const operatorMap = {
equal: '==',
unequal: '!=',
@@ -149,7 +150,7 @@ export const createRelationalNode = /* #__PURE__ */ factory(name, dependencies,
return {
mathjs: name,
conditionals: this.conditionals,
- params: this.params
+ params: this.params?.map(c => c.toJSON())
}
}
@@ -162,7 +163,7 @@ export const createRelationalNode = /* #__PURE__ */ factory(name, dependencies,
* @returns {RelationalNode}
*/
static fromJSON (json) {
- return new RelationalNode(json.conditionals, json.params)
+ return new RelationalNode(json.conditionals, json.params.map(fromJSON))
}
/**
diff --git a/src/factoriesAny.js b/src/factoriesAny.js
index 2ffc291c23..208454a322 100644
--- a/src/factoriesAny.js
+++ b/src/factoriesAny.js
@@ -1,3 +1,4 @@
+export { createfromJSON } from './json/fromJSON.js'
export { createTyped } from './core/function/typed.js'
export { createResultSet } from './type/resultset/ResultSet.js'
export { createBigNumberClass } from './type/bignumber/BigNumber.js'
diff --git a/src/factoriesNumber.js b/src/factoriesNumber.js
index 9ae85a9b40..bb681a4e0d 100644
--- a/src/factoriesNumber.js
+++ b/src/factoriesNumber.js
@@ -324,6 +324,7 @@ export { createIsPrime } from './function/utils/isPrime.js'
export { createNumeric } from './function/utils/numeric.js'
// json
+export { createfromJSON } from './json/fromJSON.js'
export { createReviver } from './json/reviver.js'
export { createReplacer } from './json/replacer.js'
diff --git a/src/json/fromJSON.js b/src/json/fromJSON.js
new file mode 100644
index 0000000000..e12531191b
--- /dev/null
+++ b/src/json/fromJSON.js
@@ -0,0 +1,18 @@
+import { factory } from '../utils/factory.js'
+
+const name = 'fromJSON'
+const dependencies = [
+ 'classes'
+]
+
+export const createfromJSON = /* #__PURE__ */ factory(name, dependencies, ({ classes }) => {
+ return function fromJSON (json) {
+ const constructor = classes[json && json.mathjs]
+
+ if (constructor && typeof constructor.fromJSON === 'function') {
+ return constructor.fromJSON(json)
+ }
+
+ return json
+ }
+})
diff --git a/test/unit-tests/expression/node/AccessorNode.test.js b/test/unit-tests/expression/node/AccessorNode.test.js
index 10b21cf9f8..e1cd5eaba9 100644
--- a/test/unit-tests/expression/node/AccessorNode.test.js
+++ b/test/unit-tests/expression/node/AccessorNode.test.js
@@ -527,7 +527,7 @@ describe('AccessorNode', function () {
assert.deepStrictEqual(json, {
mathjs: 'AccessorNode',
index: node.index,
- object: a
+ object: a.toJSON()
})
const parsed = AccessorNode.fromJSON(json)
diff --git a/test/unit-tests/expression/node/ArrayNode.test.js b/test/unit-tests/expression/node/ArrayNode.test.js
index c27f55cb52..dcadc87c1a 100644
--- a/test/unit-tests/expression/node/ArrayNode.test.js
+++ b/test/unit-tests/expression/node/ArrayNode.test.js
@@ -284,7 +284,7 @@ describe('ArrayNode', function () {
assert.deepStrictEqual(json, {
mathjs: 'ArrayNode',
- items: [b, c]
+ items: [b.toJSON(), c.toJSON()]
})
const parsed = ArrayNode.fromJSON(json)
diff --git a/test/unit-tests/expression/node/AssignmentNode.test.js b/test/unit-tests/expression/node/AssignmentNode.test.js
index b48a26bd45..49631fbe76 100644
--- a/test/unit-tests/expression/node/AssignmentNode.test.js
+++ b/test/unit-tests/expression/node/AssignmentNode.test.js
@@ -519,9 +519,9 @@ describe('AssignmentNode', function () {
assert.deepStrictEqual(json, {
mathjs: 'AssignmentNode',
- index: node.index,
- object: a,
- value: d
+ index: node.index.toJSON(),
+ object: a.toJSON(),
+ value: d.toJSON()
})
const parsed = AssignmentNode.fromJSON(json)
diff --git a/test/unit-tests/expression/node/BlockNode.test.js b/test/unit-tests/expression/node/BlockNode.test.js
index f856f1332c..ffcfec1db0 100644
--- a/test/unit-tests/expression/node/BlockNode.test.js
+++ b/test/unit-tests/expression/node/BlockNode.test.js
@@ -304,7 +304,7 @@ describe('BlockNode', function () {
assert.deepStrictEqual(json, {
mathjs: 'BlockNode',
- blocks: [bBlock, cBlock]
+ blocks: [{ node: b.toJSON(), visible: false }, { node: c.toJSON(), visible: true }]
})
const parsed = BlockNode.fromJSON(json)
diff --git a/test/unit-tests/expression/node/ConditionalNode.test.js b/test/unit-tests/expression/node/ConditionalNode.test.js
index f8cd321989..54dffc36e2 100644
--- a/test/unit-tests/expression/node/ConditionalNode.test.js
+++ b/test/unit-tests/expression/node/ConditionalNode.test.js
@@ -310,9 +310,9 @@ describe('ConditionalNode', function () {
assert.deepStrictEqual(json, {
mathjs: 'ConditionalNode',
- condition: a,
- trueExpr: b,
- falseExpr: c
+ condition: a.toJSON(),
+ trueExpr: b.toJSON(),
+ falseExpr: c.toJSON()
})
const parsed = ConditionalNode.fromJSON(json)
diff --git a/test/unit-tests/expression/node/FunctionAssignmentNode.test.js b/test/unit-tests/expression/node/FunctionAssignmentNode.test.js
index 35f6f5ece2..e9dc6e6533 100644
--- a/test/unit-tests/expression/node/FunctionAssignmentNode.test.js
+++ b/test/unit-tests/expression/node/FunctionAssignmentNode.test.js
@@ -434,7 +434,7 @@ describe('FunctionAssignmentNode', function () {
{ name: 'x', type: 'number' },
{ name: 'y', type: 'any' }
],
- expr
+ expr: expr.toJSON()
})
const parsed = FunctionAssignmentNode.fromJSON(json)
diff --git a/test/unit-tests/expression/node/FunctionNode.test.js b/test/unit-tests/expression/node/FunctionNode.test.js
index b294e1c4fc..6c084c5fc7 100644
--- a/test/unit-tests/expression/node/FunctionNode.test.js
+++ b/test/unit-tests/expression/node/FunctionNode.test.js
@@ -466,8 +466,8 @@ describe('FunctionNode', function () {
assert.deepStrictEqual(json, {
mathjs: 'FunctionNode',
- fn: a,
- args: [b, c]
+ fn: a.toJSON(),
+ args: [b.toJSON(), c.toJSON()]
})
const parsed = FunctionNode.fromJSON(json)
diff --git a/test/unit-tests/expression/node/IndexNode.test.js b/test/unit-tests/expression/node/IndexNode.test.js
index 6ecb9b6916..9968453989 100644
--- a/test/unit-tests/expression/node/IndexNode.test.js
+++ b/test/unit-tests/expression/node/IndexNode.test.js
@@ -232,7 +232,7 @@ describe('IndexNode', function () {
assert.deepStrictEqual(json, {
mathjs: 'IndexNode',
- dimensions: [prop],
+ dimensions: [prop.toJSON()],
dotNotation: true
})
diff --git a/test/unit-tests/expression/node/ObjectNode.test.js b/test/unit-tests/expression/node/ObjectNode.test.js
index f94f41e06d..8b01638148 100644
--- a/test/unit-tests/expression/node/ObjectNode.test.js
+++ b/test/unit-tests/expression/node/ObjectNode.test.js
@@ -266,7 +266,7 @@ describe('ObjectNode', function () {
assert.deepStrictEqual(json, {
mathjs: 'ObjectNode',
- properties: { b, c }
+ properties: { b: b.toJSON(), c: c.toJSON() }
})
const parsed = ObjectNode.fromJSON(json)
diff --git a/test/unit-tests/expression/node/OperatorNode.test.js b/test/unit-tests/expression/node/OperatorNode.test.js
index 11de90715f..606dbb5902 100644
--- a/test/unit-tests/expression/node/OperatorNode.test.js
+++ b/test/unit-tests/expression/node/OperatorNode.test.js
@@ -589,7 +589,7 @@ describe('OperatorNode', function () {
mathjs: 'OperatorNode',
op: '+',
fn: 'add',
- args: [one, two],
+ args: [one.toJSON(), two.toJSON()],
implicit: true,
isPercentage: false
})
diff --git a/test/unit-tests/expression/node/ParenthesisNode.test.js b/test/unit-tests/expression/node/ParenthesisNode.test.js
index b5ae673582..5c489ea545 100644
--- a/test/unit-tests/expression/node/ParenthesisNode.test.js
+++ b/test/unit-tests/expression/node/ParenthesisNode.test.js
@@ -173,7 +173,7 @@ describe('ParenthesisNode', function () {
assert.deepStrictEqual(json, {
mathjs: 'ParenthesisNode',
- content: b
+ content: b.toJSON()
})
const parsed = ParenthesisNode.fromJSON(json)
diff --git a/test/unit-tests/expression/node/RangeNode.test.js b/test/unit-tests/expression/node/RangeNode.test.js
index 00e07f20ec..d7ade39a01 100644
--- a/test/unit-tests/expression/node/RangeNode.test.js
+++ b/test/unit-tests/expression/node/RangeNode.test.js
@@ -327,9 +327,9 @@ describe('RangeNode', function () {
assert.deepStrictEqual(json, {
mathjs: 'RangeNode',
- start: a,
- end: b,
- step: c
+ start: a.toJSON(),
+ end: b.toJSON(),
+ step: c.toJSON()
})
const parsed = RangeNode.fromJSON(json)
diff --git a/test/unit-tests/expression/node/RelationalNode.test.js b/test/unit-tests/expression/node/RelationalNode.test.js
index 3f67550d60..f4f76eb99b 100644
--- a/test/unit-tests/expression/node/RelationalNode.test.js
+++ b/test/unit-tests/expression/node/RelationalNode.test.js
@@ -230,7 +230,7 @@ describe('RelationalNode', function () {
assert.deepStrictEqual(json, {
mathjs: 'RelationalNode',
conditionals: ['smaller', 'smaller'],
- params: [one, x, three]
+ params: [one.toJSON(), x.toJSON(), three.toJSON()]
})
const parsed = RelationalNode.fromJSON(json)
diff --git a/test/unit-tests/json/fromJSON.test.js b/test/unit-tests/json/fromJSON.test.js
new file mode 100644
index 0000000000..cbe9c4cebb
--- /dev/null
+++ b/test/unit-tests/json/fromJSON.test.js
@@ -0,0 +1,33 @@
+import assert from 'assert'
+import math from '../../../src/defaultInstance.js'
+const fromJSON = math.fromJSON
+
+describe('fromJSON', function () {
+ it('output JSON for a node', function () {
+ const node = new math.OperatorNode('+', 'add', [
+ new math.SymbolNode('x'),
+ new math.ConstantNode(2)
+ ])
+
+ assert.deepStrictEqual(
+ fromJSON({
+ args: [
+ {
+ mathjs: 'SymbolNode',
+ name: 'x'
+ },
+ {
+ mathjs: 'ConstantNode',
+ value: 2
+ }
+ ],
+ fn: 'add',
+ implicit: false,
+ isPercentage: false,
+ mathjs: 'OperatorNode',
+ op: '+'
+ }),
+ node
+ )
+ })
+})