feat:node-modules

This commit is contained in:
houjunxiang
2025-11-24 10:26:18 +08:00
parent 753766893b
commit 8a3e48d856
8825 changed files with 567399 additions and 1 deletions

View File

@@ -0,0 +1,189 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { isAccessorNode, isArrayNode, isConstantNode, isFunctionNode, isIndexNode, isNode, isObjectNode, isParenthesisNode, isSymbolNode } from '../../utils/is.js';
import { getSafeProperty } from '../../utils/customs.js';
import { factory } from '../../utils/factory.js';
import { accessFactory } from './utils/access.js';
var name = 'AccessorNode';
var dependencies = ['subset', 'Node'];
export var createAccessorNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
subset,
Node
} = _ref;
var access = accessFactory({
subset
});
/**
* Are parenthesis needed?
* @private
*/
function needParenthesis(node) {
// TODO: maybe make a method on the nodes which tells whether they need parenthesis?
return !(isAccessorNode(node) || isArrayNode(node) || isConstantNode(node) || isFunctionNode(node) || isObjectNode(node) || isParenthesisNode(node) || isSymbolNode(node));
}
class AccessorNode extends Node {
/**
* @constructor AccessorNode
* @extends {Node}
* Access an object property or get a matrix subset
*
* @param {Node} object The object from which to retrieve
* a property or subset.
* @param {IndexNode} index IndexNode containing ranges
*/
constructor(object, index) {
super();
if (!isNode(object)) {
throw new TypeError('Node expected for parameter "object"');
}
if (!isIndexNode(index)) {
throw new TypeError('IndexNode expected for parameter "index"');
}
this.object = object;
this.index = index;
}
// readonly property name
get name() {
if (this.index) {
return this.index.isObjectProperty() ? this.index.getObjectProperty() : '';
} else {
return this.object.name || '';
}
}
get type() {
return name;
}
get isAccessorNode() {
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) {
var evalObject = this.object._compile(math, argNames);
var evalIndex = this.index._compile(math, argNames);
if (this.index.isObjectProperty()) {
var prop = this.index.getObjectProperty();
return function evalAccessorNode(scope, args, context) {
// get a property from an object evaluated using the scope.
return getSafeProperty(evalObject(scope, args, context), prop);
};
} else {
return function evalAccessorNode(scope, args, context) {
var object = evalObject(scope, args, context);
// we pass just object here instead of context:
var index = evalIndex(scope, args, object);
return access(object, index);
};
}
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
callback(this.object, 'object', this);
callback(this.index, 'index', this);
}
/**
* Create a new AccessorNode 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 {AccessorNode} Returns a transformed copy of the node
*/
map(callback) {
return new AccessorNode(this._ifNode(callback(this.object, 'object', this)), this._ifNode(callback(this.index, 'index', this)));
}
/**
* Create a clone of this node, a shallow copy
* @return {AccessorNode}
*/
clone() {
return new AccessorNode(this.object, this.index);
}
/**
* Get string representation
* @param {Object} options
* @return {string}
*/
_toString(options) {
var object = this.object.toString(options);
if (needParenthesis(this.object)) {
object = '(' + object + ')';
}
return object + this.index.toString(options);
}
/**
* Get HTML representation
* @param {Object} options
* @return {string}
*/
_toHTML(options) {
var object = this.object.toHTML(options);
if (needParenthesis(this.object)) {
object = '<span class="math-parenthesis math-round-parenthesis">(</span>' + object + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
return object + this.index.toHTML(options);
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string}
*/
_toTex(options) {
var object = this.object.toTex(options);
if (needParenthesis(this.object)) {
object = '\\left(\' + object + \'\\right)';
}
return object + this.index.toTex(options);
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
object: this.object,
index: this.index
};
}
/**
* Instantiate an AccessorNode from its JSON representation
* @param {Object} json
* An object structured like
* `{"mathjs": "AccessorNode", object: ..., index: ...}`,
* where mathjs is optional
* @returns {AccessorNode}
*/
static fromJSON(json) {
return new AccessorNode(json.object, json.index);
}
}
_defineProperty(AccessorNode, "name", name);
return AccessorNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,176 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { isArrayNode, isNode } from '../../utils/is.js';
import { map } from '../../utils/array.js';
import { factory } from '../../utils/factory.js';
var name = 'ArrayNode';
var dependencies = ['Node'];
export var createArrayNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
Node
} = _ref;
class ArrayNode extends Node {
/**
* @constructor ArrayNode
* @extends {Node}
* Holds an 1-dimensional array with items
* @param {Node[]} [items] 1 dimensional array with items
*/
constructor(items) {
super();
this.items = items || [];
// validate input
if (!Array.isArray(this.items) || !this.items.every(isNode)) {
throw new TypeError('Array containing Nodes expected');
}
}
get type() {
return name;
}
get isArrayNode() {
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) {
var evalItems = map(this.items, function (item) {
return item._compile(math, argNames);
});
var asMatrix = math.config.matrix !== 'Array';
if (asMatrix) {
var matrix = math.matrix;
return function evalArrayNode(scope, args, context) {
return matrix(map(evalItems, function (evalItem) {
return evalItem(scope, args, context);
}));
};
} else {
return function evalArrayNode(scope, args, context) {
return map(evalItems, function (evalItem) {
return evalItem(scope, args, context);
});
};
}
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
for (var i = 0; i < this.items.length; i++) {
var node = this.items[i];
callback(node, 'items[' + i + ']', this);
}
}
/**
* Create a new ArrayNode 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 {ArrayNode} Returns a transformed copy of the node
*/
map(callback) {
var items = [];
for (var i = 0; i < this.items.length; i++) {
items[i] = this._ifNode(callback(this.items[i], 'items[' + i + ']', this));
}
return new ArrayNode(items);
}
/**
* Create a clone of this node, a shallow copy
* @return {ArrayNode}
*/
clone() {
return new ArrayNode(this.items.slice(0));
}
/**
* Get string representation
* @param {Object} options
* @return {string} str
* @override
*/
_toString(options) {
var items = this.items.map(function (node) {
return node.toString(options);
});
return '[' + items.join(', ') + ']';
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
items: this.items
};
}
/**
* Instantiate an ArrayNode from its JSON representation
* @param {Object} json An object structured like
* `{"mathjs": "ArrayNode", items: [...]}`,
* where mathjs is optional
* @returns {ArrayNode}
*/
static fromJSON(json) {
return new ArrayNode(json.items);
}
/**
* Get HTML representation
* @param {Object} options
* @return {string} str
* @override
*/
_toHTML(options) {
var items = this.items.map(function (node) {
return node.toHTML(options);
});
return '<span class="math-parenthesis math-square-parenthesis">[</span>' + items.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-square-parenthesis">]</span>';
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string} str
*/
_toTex(options) {
function itemsToTex(items, nested) {
var mixedItems = items.some(isArrayNode) && !items.every(isArrayNode);
var itemsFormRow = nested || mixedItems;
var itemSep = itemsFormRow ? '&' : '\\\\';
var itemsTex = items.map(function (node) {
if (node.items) {
return itemsToTex(node.items, !nested);
} else {
return node.toTex(options);
}
}).join(itemSep);
return mixedItems || !itemsFormRow || itemsFormRow && !nested ? '\\begin{bmatrix}' + itemsTex + '\\end{bmatrix}' : itemsTex;
}
return itemsToTex(this.items, false);
}
}
_defineProperty(ArrayNode, "name", name);
return ArrayNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,306 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { isAccessorNode, isIndexNode, isNode, isSymbolNode } from '../../utils/is.js';
import { getSafeProperty, setSafeProperty } from '../../utils/customs.js';
import { factory } from '../../utils/factory.js';
import { accessFactory } from './utils/access.js';
import { assignFactory } from './utils/assign.js';
import { getPrecedence } from '../operators.js';
var name = 'AssignmentNode';
var dependencies = ['subset', '?matrix',
// FIXME: should not be needed at all, should be handled by subset
'Node'];
export var createAssignmentNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
subset,
matrix,
Node
} = _ref;
var access = accessFactory({
subset
});
var assign = assignFactory({
subset,
matrix
});
/*
* Is parenthesis needed?
* @param {node} node
* @param {string} [parenthesis='keep']
* @param {string} implicit
* @private
*/
function needParenthesis(node, parenthesis, implicit) {
if (!parenthesis) {
parenthesis = 'keep';
}
var precedence = getPrecedence(node, parenthesis, implicit);
var exprPrecedence = getPrecedence(node.value, parenthesis, implicit);
return parenthesis === 'all' || exprPrecedence !== null && exprPrecedence <= precedence;
}
class AssignmentNode extends Node {
/**
* @constructor AssignmentNode
* @extends {Node}
*
* Define a symbol, like `a=3.2`, update a property like `a.b=3.2`, or
* replace a subset of a matrix like `A[2,2]=42`.
*
* Syntax:
*
* new AssignmentNode(symbol, value)
* new AssignmentNode(object, index, value)
*
* Usage:
*
* new AssignmentNode(new SymbolNode('a'), new ConstantNode(2)) // a=2
* new AssignmentNode(new SymbolNode('a'),
* new IndexNode('b'),
* new ConstantNode(2)) // a.b=2
* new AssignmentNode(new SymbolNode('a'),
* new IndexNode(1, 2),
* new ConstantNode(3)) // a[1,2]=3
*
* @param {SymbolNode | AccessorNode} object
* Object on which to assign a value
* @param {IndexNode} [index=null]
* Index, property name or matrix index. Optional. If not provided
* and `object` is a SymbolNode, the property is assigned to the
* global scope.
* @param {Node} value
* The value to be assigned
*/
constructor(object, index, value) {
super();
this.object = object;
this.index = value ? index : null;
this.value = value || index;
// validate input
if (!isSymbolNode(object) && !isAccessorNode(object)) {
throw new TypeError('SymbolNode or AccessorNode expected as "object"');
}
if (isSymbolNode(object) && object.name === 'end') {
throw new Error('Cannot assign to symbol "end"');
}
if (this.index && !isIndexNode(this.index)) {
// index is optional
throw new TypeError('IndexNode expected as "index"');
}
if (!isNode(this.value)) {
throw new TypeError('Node expected as "value"');
}
}
// class name for typing purposes:
// readonly property name
get name() {
if (this.index) {
return this.index.isObjectProperty() ? this.index.getObjectProperty() : '';
} else {
return this.object.name || '';
}
}
get type() {
return name;
}
get isAssignmentNode() {
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) {
var evalObject = this.object._compile(math, argNames);
var evalIndex = this.index ? this.index._compile(math, argNames) : null;
var evalValue = this.value._compile(math, argNames);
var name = this.object.name;
if (!this.index) {
// apply a variable to the scope, for example `a=2`
if (!isSymbolNode(this.object)) {
throw new TypeError('SymbolNode expected as object');
}
return function evalAssignmentNode(scope, args, context) {
var value = evalValue(scope, args, context);
scope.set(name, value);
return value;
};
} else if (this.index.isObjectProperty()) {
// apply an object property for example `a.b=2`
var prop = this.index.getObjectProperty();
return function evalAssignmentNode(scope, args, context) {
var object = evalObject(scope, args, context);
var value = evalValue(scope, args, context);
setSafeProperty(object, prop, value);
return value;
};
} else if (isSymbolNode(this.object)) {
// update a matrix subset, for example `a[2]=3`
return function evalAssignmentNode(scope, args, context) {
var childObject = evalObject(scope, args, context);
var value = evalValue(scope, args, context);
// Important: we pass childObject instead of context:
var index = evalIndex(scope, args, childObject);
scope.set(name, assign(childObject, index, value));
return value;
};
} else {
// isAccessorNode(node.object) === true
// update a matrix subset, for example `a.b[2]=3`
// we will not use the compile function of the AccessorNode, but
// compile it ourselves here as we need the parent object of the
// AccessorNode:
// wee need to apply the updated object to parent object
var evalParentObject = this.object.object._compile(math, argNames);
if (this.object.index.isObjectProperty()) {
var parentProp = this.object.index.getObjectProperty();
return function evalAssignmentNode(scope, args, context) {
var parent = evalParentObject(scope, args, context);
var childObject = getSafeProperty(parent, parentProp);
// Important: we pass childObject instead of context:
var index = evalIndex(scope, args, childObject);
var value = evalValue(scope, args, context);
setSafeProperty(parent, parentProp, assign(childObject, index, value));
return value;
};
} else {
// if some parameters use the 'end' parameter, we need to calculate
// the size
var evalParentIndex = this.object.index._compile(math, argNames);
return function evalAssignmentNode(scope, args, context) {
var parent = evalParentObject(scope, args, context);
// Important: we pass parent instead of context:
var parentIndex = evalParentIndex(scope, args, parent);
var childObject = access(parent, parentIndex);
// Important: we pass childObject instead of context
var index = evalIndex(scope, args, childObject);
var value = evalValue(scope, args, context);
assign(parent, parentIndex, assign(childObject, index, value));
return value;
};
}
}
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
callback(this.object, 'object', this);
if (this.index) {
callback(this.index, 'index', this);
}
callback(this.value, 'value', this);
}
/**
* Create a new AssignmentNode 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 {AssignmentNode} Returns a transformed copy of the node
*/
map(callback) {
var object = this._ifNode(callback(this.object, 'object', this));
var index = this.index ? this._ifNode(callback(this.index, 'index', this)) : null;
var value = this._ifNode(callback(this.value, 'value', this));
return new AssignmentNode(object, index, value);
}
/**
* Create a clone of this node, a shallow copy
* @return {AssignmentNode}
*/
clone() {
return new AssignmentNode(this.object, this.index, this.value);
}
/**
* Get string representation
* @param {Object} options
* @return {string}
*/
_toString(options) {
var object = this.object.toString(options);
var index = this.index ? this.index.toString(options) : '';
var value = this.value.toString(options);
if (needParenthesis(this, options && options.parenthesis, options && options.implicit)) {
value = '(' + value + ')';
}
return object + index + ' = ' + value;
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
object: this.object,
index: this.index,
value: this.value
};
}
/**
* Instantiate an AssignmentNode from its JSON representation
* @param {Object} json
* An object structured like
* `{"mathjs": "AssignmentNode", object: ..., index: ..., value: ...}`,
* where mathjs is optional
* @returns {AssignmentNode}
*/
static fromJSON(json) {
return new AssignmentNode(json.object, json.index, json.value);
}
/**
* Get HTML representation
* @param {Object} options
* @return {string}
*/
_toHTML(options) {
var object = this.object.toHTML(options);
var index = this.index ? this.index.toHTML(options) : '';
var value = this.value.toHTML(options);
if (needParenthesis(this, options && options.parenthesis, options && options.implicit)) {
value = '<span class="math-paranthesis math-round-parenthesis">(</span>' + value + '<span class="math-paranthesis math-round-parenthesis">)</span>';
}
return object + index + '<span class="math-operator math-assignment-operator ' + 'math-variable-assignment-operator math-binary-operator">=</span>' + value;
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string}
*/
_toTex(options) {
var object = this.object.toTex(options);
var index = this.index ? this.index.toTex(options) : '';
var value = this.value.toTex(options);
if (needParenthesis(this, options && options.parenthesis, options && options.implicit)) {
value = "\\left(".concat(value, "\\right)");
}
return object + index + '=' + value;
}
}
_defineProperty(AssignmentNode, "name", name);
return AssignmentNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,185 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { isNode } from '../../utils/is.js';
import { forEach, map } from '../../utils/array.js';
import { factory } from '../../utils/factory.js';
var name = 'BlockNode';
var dependencies = ['ResultSet', 'Node'];
export var createBlockNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
ResultSet,
Node
} = _ref;
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) {
var node = block && block.node;
var 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
};
});
}
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) {
var evalBlocks = map(this.blocks, function (block) {
return {
evaluate: block.node._compile(math, argNames),
visible: block.visible
};
});
return function evalBlockNodes(scope, args, context) {
var results = [];
forEach(evalBlocks, function evalBlockNode(block) {
var result = block.evaluate(scope, args, context);
if (block.visible) {
results.push(result);
}
});
return new ResultSet(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 (var i = 0; i < this.blocks.length; i++) {
callback(this.blocks[i].node, 'blocks[' + i + '].node', this);
}
}
/**
* 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) {
var blocks = [];
for (var i = 0; i < this.blocks.length; i++) {
var block = this.blocks[i];
var node = this._ifNode(callback(block.node, 'blocks[' + i + '].node', this));
blocks[i] = {
node,
visible: block.visible
};
}
return new BlockNode(blocks);
}
/**
* Create a clone of this node, a shallow copy
* @return {BlockNode}
*/
clone() {
var blocks = this.blocks.map(function (block) {
return {
node: block.node,
visible: block.visible
};
});
return new BlockNode(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');
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
blocks: this.blocks
};
}
/**
* 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 HTML representation
* @param {Object} options
* @return {string} str
* @override
*/
_toHTML(options) {
return this.blocks.map(function (param) {
return param.node.toHTML(options) + (param.visible ? '' : '<span class="math-separator">;</span>');
}).join('<span class="math-separator"><br /></span>');
}
/**
* 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');
}
}
_defineProperty(BlockNode, "name", name);
return BlockNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,225 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { isBigNumber, isComplex, isNode, isUnit, typeOf } from '../../utils/is.js';
import { factory } from '../../utils/factory.js';
import { getPrecedence } from '../operators.js';
var name = 'ConditionalNode';
var dependencies = ['Node'];
export var createConditionalNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
Node
} = _ref;
/**
* Test whether a condition is met
* @param {*} condition
* @returns {boolean} true if condition is true or non-zero, else false
*/
function testCondition(condition) {
if (typeof condition === 'number' || typeof condition === 'boolean' || typeof condition === 'string') {
return !!condition;
}
if (condition) {
if (isBigNumber(condition)) {
return !condition.isZero();
}
if (isComplex(condition)) {
return !!(condition.re || condition.im);
}
if (isUnit(condition)) {
return !!condition.value;
}
}
if (condition === null || condition === undefined) {
return false;
}
throw new TypeError('Unsupported type of condition "' + typeOf(condition) + '"');
}
class ConditionalNode extends Node {
/**
* A lazy evaluating conditional operator: 'condition ? trueExpr : falseExpr'
*
* @param {Node} condition Condition, must result in a boolean
* @param {Node} trueExpr Expression evaluated when condition is true
* @param {Node} falseExpr Expression evaluated when condition is true
*
* @constructor ConditionalNode
* @extends {Node}
*/
constructor(condition, trueExpr, falseExpr) {
super();
if (!isNode(condition)) {
throw new TypeError('Parameter condition must be a Node');
}
if (!isNode(trueExpr)) {
throw new TypeError('Parameter trueExpr must be a Node');
}
if (!isNode(falseExpr)) {
throw new TypeError('Parameter falseExpr must be a Node');
}
this.condition = condition;
this.trueExpr = trueExpr;
this.falseExpr = falseExpr;
}
get type() {
return name;
}
get isConditionalNode() {
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) {
var evalCondition = this.condition._compile(math, argNames);
var evalTrueExpr = this.trueExpr._compile(math, argNames);
var evalFalseExpr = this.falseExpr._compile(math, argNames);
return function evalConditionalNode(scope, args, context) {
return testCondition(evalCondition(scope, args, context)) ? evalTrueExpr(scope, args, context) : evalFalseExpr(scope, args, context);
};
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
callback(this.condition, 'condition', this);
callback(this.trueExpr, 'trueExpr', this);
callback(this.falseExpr, 'falseExpr', this);
}
/**
* Create a new ConditionalNode 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 {ConditionalNode} Returns a transformed copy of the node
*/
map(callback) {
return new ConditionalNode(this._ifNode(callback(this.condition, 'condition', this)), this._ifNode(callback(this.trueExpr, 'trueExpr', this)), this._ifNode(callback(this.falseExpr, 'falseExpr', this)));
}
/**
* Create a clone of this node, a shallow copy
* @return {ConditionalNode}
*/
clone() {
return new ConditionalNode(this.condition, this.trueExpr, this.falseExpr);
}
/**
* Get string representation
* @param {Object} options
* @return {string} str
*/
_toString(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var precedence = getPrecedence(this, parenthesis, options && options.implicit);
// Enclose Arguments in parentheses if they are an OperatorNode
// or have lower or equal precedence
// NOTE: enclosing all OperatorNodes in parentheses is a decision
// purely based on aesthetics and readability
var condition = this.condition.toString(options);
var conditionPrecedence = getPrecedence(this.condition, parenthesis, options && options.implicit);
if (parenthesis === 'all' || this.condition.type === 'OperatorNode' || conditionPrecedence !== null && conditionPrecedence <= precedence) {
condition = '(' + condition + ')';
}
var trueExpr = this.trueExpr.toString(options);
var truePrecedence = getPrecedence(this.trueExpr, parenthesis, options && options.implicit);
if (parenthesis === 'all' || this.trueExpr.type === 'OperatorNode' || truePrecedence !== null && truePrecedence <= precedence) {
trueExpr = '(' + trueExpr + ')';
}
var falseExpr = this.falseExpr.toString(options);
var falsePrecedence = getPrecedence(this.falseExpr, parenthesis, options && options.implicit);
if (parenthesis === 'all' || this.falseExpr.type === 'OperatorNode' || falsePrecedence !== null && falsePrecedence <= precedence) {
falseExpr = '(' + falseExpr + ')';
}
return condition + ' ? ' + trueExpr + ' : ' + falseExpr;
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
condition: this.condition,
trueExpr: this.trueExpr,
falseExpr: this.falseExpr
};
}
/**
* Instantiate an ConditionalNode from its JSON representation
* @param {Object} json
* An object structured like
* ```
* {"mathjs": "ConditionalNode",
* "condition": ...,
* "trueExpr": ...,
* "falseExpr": ...}
* ```
* where mathjs is optional
* @returns {ConditionalNode}
*/
static fromJSON(json) {
return new ConditionalNode(json.condition, json.trueExpr, json.falseExpr);
}
/**
* Get HTML representation
* @param {Object} options
* @return {string} str
*/
_toHTML(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var precedence = getPrecedence(this, parenthesis, options && options.implicit);
// Enclose Arguments in parentheses if they are an OperatorNode
// or have lower or equal precedence
// NOTE: enclosing all OperatorNodes in parentheses is a decision
// purely based on aesthetics and readability
var condition = this.condition.toHTML(options);
var conditionPrecedence = getPrecedence(this.condition, parenthesis, options && options.implicit);
if (parenthesis === 'all' || this.condition.type === 'OperatorNode' || conditionPrecedence !== null && conditionPrecedence <= precedence) {
condition = '<span class="math-parenthesis math-round-parenthesis">(</span>' + condition + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
var trueExpr = this.trueExpr.toHTML(options);
var truePrecedence = getPrecedence(this.trueExpr, parenthesis, options && options.implicit);
if (parenthesis === 'all' || this.trueExpr.type === 'OperatorNode' || truePrecedence !== null && truePrecedence <= precedence) {
trueExpr = '<span class="math-parenthesis math-round-parenthesis">(</span>' + trueExpr + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
var falseExpr = this.falseExpr.toHTML(options);
var falsePrecedence = getPrecedence(this.falseExpr, parenthesis, options && options.implicit);
if (parenthesis === 'all' || this.falseExpr.type === 'OperatorNode' || falsePrecedence !== null && falsePrecedence <= precedence) {
falseExpr = '<span class="math-parenthesis math-round-parenthesis">(</span>' + falseExpr + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
return condition + '<span class="math-operator math-conditional-operator">?</span>' + trueExpr + '<span class="math-operator math-conditional-operator">:</span>' + falseExpr;
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string} str
*/
_toTex(options) {
return '\\begin{cases} {' + this.trueExpr.toTex(options) + '}, &\\quad{\\text{if }\\;' + this.condition.toTex(options) + '}\\\\{' + this.falseExpr.toTex(options) + '}, &\\quad{\\text{otherwise}}\\end{cases}';
}
}
_defineProperty(ConditionalNode, "name", name);
return ConditionalNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,179 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { format } from '../../utils/string.js';
import { typeOf } from '../../utils/is.js';
import { escapeLatex } from '../../utils/latex.js';
import { factory } from '../../utils/factory.js';
var name = 'ConstantNode';
var dependencies = ['Node'];
export var createConstantNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
Node
} = _ref;
class ConstantNode extends Node {
/**
* A ConstantNode holds a constant value like a number or string.
*
* Usage:
*
* new ConstantNode(2.3)
* new ConstantNode('hello')
*
* @param {*} value Value can be any type (number, BigNumber, bigint, string, ...)
* @constructor ConstantNode
* @extends {Node}
*/
constructor(value) {
super();
this.value = value;
}
get type() {
return name;
}
get isConstantNode() {
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) {
var value = this.value;
return function evalConstantNode() {
return value;
};
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
// nothing to do, we don't have any children
}
/**
* Create a new ConstantNode with children produced by the given callback.
* Trivial because there are no children.
* @param {function(child: Node, path: string, parent: Node) : Node} callback
* @returns {ConstantNode} Returns a clone of the node
*/
map(callback) {
return this.clone();
}
/**
* Create a clone of this node, a shallow copy
* @return {ConstantNode}
*/
clone() {
return new ConstantNode(this.value);
}
/**
* Get string representation
* @param {Object} options
* @return {string} str
*/
_toString(options) {
return format(this.value, options);
}
/**
* Get HTML representation
* @param {Object} options
* @return {string} str
*/
_toHTML(options) {
var value = this._toString(options);
switch (typeOf(this.value)) {
case 'number':
case 'bigint':
case 'BigNumber':
case 'Fraction':
return '<span class="math-number">' + value + '</span>';
case 'string':
return '<span class="math-string">' + value + '</span>';
case 'boolean':
return '<span class="math-boolean">' + value + '</span>';
case 'null':
return '<span class="math-null-symbol">' + value + '</span>';
case 'undefined':
return '<span class="math-undefined">' + value + '</span>';
default:
return '<span class="math-symbol">' + value + '</span>';
}
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
value: this.value
};
}
/**
* Instantiate a ConstantNode from its JSON representation
* @param {Object} json An object structured like
* `{"mathjs": "SymbolNode", value: 2.3}`,
* where mathjs is optional
* @returns {ConstantNode}
*/
static fromJSON(json) {
return new ConstantNode(json.value);
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string} str
*/
_toTex(options) {
var value = this._toString(options);
var type = typeOf(this.value);
switch (type) {
case 'string':
return '\\mathtt{' + escapeLatex(value) + '}';
case 'number':
case 'BigNumber':
{
var finite = type === 'BigNumber' ? this.value.isFinite() : isFinite(this.value);
if (!finite) {
return this.value.valueOf() < 0 ? '-\\infty' : '\\infty';
}
var index = value.toLowerCase().indexOf('e');
if (index !== -1) {
return value.substring(0, index) + '\\cdot10^{' + value.substring(index + 1) + '}';
}
return value;
}
case 'bigint':
{
return value.toString();
}
case 'Fraction':
return this.value.toLatex();
default:
return value;
}
}
}
_defineProperty(ConstantNode, "name", name);
return ConstantNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,235 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { isNode } from '../../utils/is.js';
import { keywords } from '../keywords.js';
import { escape } from '../../utils/string.js';
import { forEach, join } from '../../utils/array.js';
import { toSymbol } from '../../utils/latex.js';
import { getPrecedence } from '../operators.js';
import { factory } from '../../utils/factory.js';
var name = 'FunctionAssignmentNode';
var dependencies = ['typed', 'Node'];
export var createFunctionAssignmentNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
typed,
Node
} = _ref;
/**
* Is parenthesis needed?
* @param {Node} node
* @param {Object} parenthesis
* @param {string} implicit
* @private
*/
function needParenthesis(node, parenthesis, implicit) {
var precedence = getPrecedence(node, parenthesis, implicit);
var exprPrecedence = getPrecedence(node.expr, parenthesis, implicit);
return parenthesis === 'all' || exprPrecedence !== null && exprPrecedence <= precedence;
}
class FunctionAssignmentNode extends Node {
/**
* @constructor FunctionAssignmentNode
* @extends {Node}
* Function assignment
*
* @param {string} name Function name
* @param {string[] | Array.<{name: string, type: string}>} params
* Array with function parameter names, or an
* array with objects containing the name
* and type of the parameter
* @param {Node} expr The function expression
*/
constructor(name, params, expr) {
super();
// validate input
if (typeof name !== 'string') {
throw new TypeError('String expected for parameter "name"');
}
if (!Array.isArray(params)) {
throw new TypeError('Array containing strings or objects expected for parameter "params"');
}
if (!isNode(expr)) {
throw new TypeError('Node expected for parameter "expr"');
}
if (keywords.has(name)) {
throw new Error('Illegal function name, "' + name + '" is a reserved keyword');
}
var paramNames = new Set();
for (var param of params) {
var _name = typeof param === 'string' ? param : param.name;
if (paramNames.has(_name)) {
throw new Error("Duplicate parameter name \"".concat(_name, "\""));
} else {
paramNames.add(_name);
}
}
this.name = name;
this.params = params.map(function (param) {
return param && param.name || param;
});
this.types = params.map(function (param) {
return param && param.type || 'any';
});
this.expr = expr;
}
get type() {
return name;
}
get isFunctionAssignmentNode() {
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) {
var childArgNames = Object.create(argNames);
forEach(this.params, function (param) {
childArgNames[param] = true;
});
// compile the function expression with the child args
var evalExpr = this.expr._compile(math, childArgNames);
var name = this.name;
var params = this.params;
var signature = join(this.types, ',');
var syntax = name + '(' + join(this.params, ', ') + ')';
return function evalFunctionAssignmentNode(scope, args, context) {
var signatures = {};
signatures[signature] = function () {
var childArgs = Object.create(args);
for (var i = 0; i < params.length; i++) {
childArgs[params[i]] = arguments[i];
}
return evalExpr(scope, childArgs, context);
};
var fn = typed(name, signatures);
fn.syntax = syntax;
scope.set(name, fn);
return fn;
};
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
callback(this.expr, 'expr', this);
}
/**
* Create a new FunctionAssignmentNode 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 {FunctionAssignmentNode} Returns a transformed copy of the node
*/
map(callback) {
var expr = this._ifNode(callback(this.expr, 'expr', this));
return new FunctionAssignmentNode(this.name, this.params.slice(0), expr);
}
/**
* Create a clone of this node, a shallow copy
* @return {FunctionAssignmentNode}
*/
clone() {
return new FunctionAssignmentNode(this.name, this.params.slice(0), this.expr);
}
/**
* get string representation
* @param {Object} options
* @return {string} str
*/
_toString(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var expr = this.expr.toString(options);
if (needParenthesis(this, parenthesis, options && options.implicit)) {
expr = '(' + expr + ')';
}
return this.name + '(' + this.params.join(', ') + ') = ' + expr;
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
var types = this.types;
return {
mathjs: name,
name: this.name,
params: this.params.map(function (param, index) {
return {
name: param,
type: types[index]
};
}),
expr: this.expr
};
}
/**
* Instantiate an FunctionAssignmentNode from its JSON representation
* @param {Object} json
* An object structured like
* ```
* {"mathjs": "FunctionAssignmentNode",
* name: ..., params: ..., expr: ...}
* ```
* where mathjs is optional
* @returns {FunctionAssignmentNode}
*/
static fromJSON(json) {
return new FunctionAssignmentNode(json.name, json.params, json.expr);
}
/**
* get HTML representation
* @param {Object} options
* @return {string} str
*/
_toHTML(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var params = [];
for (var i = 0; i < this.params.length; i++) {
params.push('<span class="math-symbol math-parameter">' + escape(this.params[i]) + '</span>');
}
var expr = this.expr.toHTML(options);
if (needParenthesis(this, parenthesis, options && options.implicit)) {
expr = '<span class="math-parenthesis math-round-parenthesis">(</span>' + expr + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
return '<span class="math-function">' + escape(this.name) + '</span>' + '<span class="math-parenthesis math-round-parenthesis">(</span>' + params.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-round-parenthesis">)</span>' + '<span class="math-operator math-assignment-operator ' + 'math-variable-assignment-operator math-binary-operator">=</span>' + expr;
}
/**
* get LaTeX representation
* @param {Object} options
* @return {string} str
*/
_toTex(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var expr = this.expr.toTex(options);
if (needParenthesis(this, parenthesis, options && options.implicit)) {
expr = "\\left(".concat(expr, "\\right)");
}
return '\\mathrm{' + this.name + '}\\left(' + this.params.map(toSymbol).join(',') + '\\right)=' + expr;
}
}
_defineProperty(FunctionAssignmentNode, "name", name);
return FunctionAssignmentNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,479 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { isAccessorNode, isFunctionAssignmentNode, isIndexNode, isNode, isSymbolNode } from '../../utils/is.js';
import { escape, format } from '../../utils/string.js';
import { hasOwnProperty } from '../../utils/object.js';
import { getSafeProperty, getSafeMethod } from '../../utils/customs.js';
import { createSubScope } from '../../utils/scope.js';
import { factory } from '../../utils/factory.js';
import { defaultTemplate, latexFunctions } from '../../utils/latex.js';
var name = 'FunctionNode';
var dependencies = ['math', 'Node', 'SymbolNode'];
export var createFunctionNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var _FunctionNode;
var {
math,
Node,
SymbolNode
} = _ref;
/* format to fixed length */
var strin = entity => format(entity, {
truncate: 78
});
/*
* Expand a LaTeX template
*
* @param {string} template
* @param {Node} node
* @param {Object} options
* @private
**/
function expandTemplate(template, node, options) {
var latex = '';
// Match everything of the form ${identifier} or ${identifier[2]} or $$
// while submatching identifier and 2 (in the second case)
var regex = /\$(?:\{([a-z_][a-z_0-9]*)(?:\[([0-9]+)\])?\}|\$)/gi;
var inputPos = 0; // position in the input string
var match;
while ((match = regex.exec(template)) !== null) {
// go through all matches
// add everything in front of the match to the LaTeX string
latex += template.substring(inputPos, match.index);
inputPos = match.index;
if (match[0] === '$$') {
// escaped dollar sign
latex += '$';
inputPos++;
} else {
// template parameter
inputPos += match[0].length;
var property = node[match[1]];
if (!property) {
throw new ReferenceError('Template: Property ' + match[1] + ' does not exist.');
}
if (match[2] === undefined) {
// no square brackets
switch (typeof property) {
case 'string':
latex += property;
break;
case 'object':
if (isNode(property)) {
latex += property.toTex(options);
} else if (Array.isArray(property)) {
// make array of Nodes into comma separated list
latex += property.map(function (arg, index) {
if (isNode(arg)) {
return arg.toTex(options);
}
throw new TypeError('Template: ' + match[1] + '[' + index + '] is not a Node.');
}).join(',');
} else {
throw new TypeError('Template: ' + match[1] + ' has to be a Node, String or array of Nodes');
}
break;
default:
throw new TypeError('Template: ' + match[1] + ' has to be a Node, String or array of Nodes');
}
} else {
// with square brackets
if (isNode(property[match[2]] && property[match[2]])) {
latex += property[match[2]].toTex(options);
} else {
throw new TypeError('Template: ' + match[1] + '[' + match[2] + '] is not a Node.');
}
}
}
}
latex += template.slice(inputPos); // append rest of the template
return latex;
}
class FunctionNode extends Node {
/**
* @constructor FunctionNode
* @extends {./Node}
* invoke a list with arguments on a node
* @param {./Node | string} fn
* Item resolving to a function on which to invoke
* the arguments, typically a SymbolNode or AccessorNode
* @param {./Node[]} args
*/
constructor(fn, args) {
super();
if (typeof fn === 'string') {
fn = new SymbolNode(fn);
}
// validate input
if (!isNode(fn)) throw new TypeError('Node expected as parameter "fn"');
if (!Array.isArray(args) || !args.every(isNode)) {
throw new TypeError('Array containing Nodes expected for parameter "args"');
}
this.fn = fn;
this.args = args || [];
}
// readonly property name
get name() {
return this.fn.name || '';
}
get type() {
return name;
}
get isFunctionNode() {
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) {
// compile arguments
var evalArgs = this.args.map(arg => arg._compile(math, argNames));
if (isSymbolNode(this.fn)) {
var _name = this.fn.name;
if (!argNames[_name]) {
// we can statically determine whether the function
// has the rawArgs property
var fn = _name in math ? getSafeProperty(math, _name) : undefined;
var isRaw = typeof fn === 'function' && fn.rawArgs === true;
var resolveFn = scope => {
var value;
if (scope.has(_name)) {
value = scope.get(_name);
} else if (_name in math) {
value = getSafeProperty(math, _name);
} else {
return FunctionNode.onUndefinedFunction(_name);
}
if (typeof value === 'function') {
return value;
}
throw new TypeError("'".concat(_name, "' is not a function; its value is:\n ").concat(strin(value)));
};
if (isRaw) {
// pass unevaluated parameters (nodes) to the function
// "raw" evaluation
var rawArgs = this.args;
return function evalFunctionNode(scope, args, context) {
var fn = resolveFn(scope);
// the original function can be overwritten in the scope with a non-rawArgs function
if (fn.rawArgs === true) {
return fn(rawArgs, math, createSubScope(scope, args));
} else {
// "regular" evaluation
var values = evalArgs.map(evalArg => evalArg(scope, args, context));
return fn(...values);
}
};
} else {
// "regular" evaluation
switch (evalArgs.length) {
case 0:
return function evalFunctionNode(scope, args, context) {
var fn = resolveFn(scope);
return fn();
};
case 1:
return function evalFunctionNode(scope, args, context) {
var fn = resolveFn(scope);
var evalArg0 = evalArgs[0];
return fn(evalArg0(scope, args, context));
};
case 2:
return function evalFunctionNode(scope, args, context) {
var fn = resolveFn(scope);
var evalArg0 = evalArgs[0];
var evalArg1 = evalArgs[1];
return fn(evalArg0(scope, args, context), evalArg1(scope, args, context));
};
default:
return function evalFunctionNode(scope, args, context) {
var fn = resolveFn(scope);
var values = evalArgs.map(evalArg => evalArg(scope, args, context));
return fn(...values);
};
}
}
} else {
// the function symbol is an argName
var _rawArgs = this.args;
return function evalFunctionNode(scope, args, context) {
var fn = getSafeProperty(args, _name);
if (typeof fn !== 'function') {
throw new TypeError("Argument '".concat(_name, "' was not a function; received: ").concat(strin(fn)));
}
if (fn.rawArgs) {
// "Raw" evaluation
return fn(_rawArgs, math, createSubScope(scope, args));
} else {
var values = evalArgs.map(evalArg => evalArg(scope, args, context));
return fn.apply(fn, values);
}
};
}
} else if (isAccessorNode(this.fn) && isIndexNode(this.fn.index) && this.fn.index.isObjectProperty()) {
// execute the function with the right context:
// the object of the AccessorNode
var evalObject = this.fn.object._compile(math, argNames);
var prop = this.fn.index.getObjectProperty();
var _rawArgs2 = this.args;
return function evalFunctionNode(scope, args, context) {
var object = evalObject(scope, args, context);
var fn = getSafeMethod(object, prop);
if (fn !== null && fn !== void 0 && fn.rawArgs) {
// "Raw" evaluation
return fn(_rawArgs2, math, createSubScope(scope, args));
} else {
// "regular" evaluation
var values = evalArgs.map(evalArg => evalArg(scope, args, context));
return fn.apply(object, values);
}
};
} else {
// node.fn.isAccessorNode && !node.fn.index.isObjectProperty()
// we have to dynamically determine whether the function has the
// rawArgs property
var fnExpr = this.fn.toString();
var evalFn = this.fn._compile(math, argNames);
var _rawArgs3 = this.args;
return function evalFunctionNode(scope, args, context) {
var fn = evalFn(scope, args, context);
if (typeof fn !== 'function') {
throw new TypeError("Expression '".concat(fnExpr, "' did not evaluate to a function; value is:") + "\n ".concat(strin(fn)));
}
if (fn.rawArgs) {
// "Raw" evaluation
return fn(_rawArgs3, math, createSubScope(scope, args));
} else {
// "regular" evaluation
var values = evalArgs.map(evalArg => evalArg(scope, args, context));
return fn.apply(fn, values);
}
};
}
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
callback(this.fn, 'fn', this);
for (var i = 0; i < this.args.length; i++) {
callback(this.args[i], 'args[' + i + ']', this);
}
}
/**
* Create a new FunctionNode 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 {FunctionNode} Returns a transformed copy of the node
*/
map(callback) {
var fn = this._ifNode(callback(this.fn, 'fn', this));
var args = [];
for (var i = 0; i < this.args.length; i++) {
args[i] = this._ifNode(callback(this.args[i], 'args[' + i + ']', this));
}
return new FunctionNode(fn, args);
}
/**
* Create a clone of this node, a shallow copy
* @return {FunctionNode}
*/
clone() {
return new FunctionNode(this.fn, this.args.slice(0));
}
/**
* Throws an error 'Undefined function {name}'
* @param {string} name
*/
/**
* Get string representation. (wrapper function)
* This overrides parts of Node's toString function.
* If callback is an object containing callbacks, it
* calls the correct callback for the current node,
* otherwise it falls back to calling Node's toString
* function.
*
* @param {Object} options
* @return {string} str
* @override
*/
toString(options) {
var customString;
var name = this.fn.toString(options);
if (options && typeof options.handler === 'object' && hasOwnProperty(options.handler, name)) {
// callback is a map of callback functions
customString = options.handler[name](this, options);
}
if (typeof customString !== 'undefined') {
return customString;
}
// fall back to Node's toString
return super.toString(options);
}
/**
* Get string representation
* @param {Object} options
* @return {string} str
*/
_toString(options) {
var args = this.args.map(function (arg) {
return arg.toString(options);
});
var fn = isFunctionAssignmentNode(this.fn) ? '(' + this.fn.toString(options) + ')' : this.fn.toString(options);
// format the arguments like "add(2, 4.2)"
return fn + '(' + args.join(', ') + ')';
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
fn: this.fn,
args: this.args
};
}
/**
* Instantiate an AssignmentNode from its JSON representation
* @param {Object} json An object structured like
* `{"mathjs": "FunctionNode", fn: ..., args: ...}`,
* where mathjs is optional
* @returns {FunctionNode}
*/
/**
* Get HTML representation
* @param {Object} options
* @return {string} str
*/
_toHTML(options) {
var args = this.args.map(function (arg) {
return arg.toHTML(options);
});
// format the arguments like "add(2, 4.2)"
return '<span class="math-function">' + escape(this.fn) + '</span><span class="math-paranthesis math-round-parenthesis">(</span>' + args.join('<span class="math-separator">,</span>') + '<span class="math-paranthesis math-round-parenthesis">)</span>';
}
/**
* Get LaTeX representation. (wrapper function)
* This overrides parts of Node's toTex function.
* If callback is an object containing callbacks, it
* calls the correct callback for the current node,
* otherwise it falls back to calling Node's toTex
* function.
*
* @param {Object} options
* @return {string}
*/
toTex(options) {
var customTex;
if (options && typeof options.handler === 'object' && hasOwnProperty(options.handler, this.name)) {
// callback is a map of callback functions
customTex = options.handler[this.name](this, options);
}
if (typeof customTex !== 'undefined') {
return customTex;
}
// fall back to Node's toTex
return super.toTex(options);
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string} str
*/
_toTex(options) {
var args = this.args.map(function (arg) {
// get LaTeX of the arguments
return arg.toTex(options);
});
var latexConverter;
if (latexFunctions[this.name]) {
latexConverter = latexFunctions[this.name];
}
// toTex property on the function itself
if (math[this.name] && (typeof math[this.name].toTex === 'function' || typeof math[this.name].toTex === 'object' || typeof math[this.name].toTex === 'string')) {
// .toTex is a callback function
latexConverter = math[this.name].toTex;
}
var customToTex;
switch (typeof latexConverter) {
case 'function':
// a callback function
customToTex = latexConverter(this, options);
break;
case 'string':
// a template string
customToTex = expandTemplate(latexConverter, this, options);
break;
case 'object':
// an object with different "converters" for different
// numbers of arguments
switch (typeof latexConverter[args.length]) {
case 'function':
customToTex = latexConverter[args.length](this, options);
break;
case 'string':
customToTex = expandTemplate(latexConverter[args.length], this, options);
break;
}
}
if (typeof customToTex !== 'undefined') {
return customToTex;
}
return expandTemplate(defaultTemplate, this, options);
}
/**
* Get identifier.
* @return {string}
*/
getIdentifier() {
return this.type + ':' + this.name;
}
}
_FunctionNode = FunctionNode;
_defineProperty(FunctionNode, "name", name);
_defineProperty(FunctionNode, "onUndefinedFunction", function (name) {
throw new Error('Undefined function ' + name);
});
_defineProperty(FunctionNode, "fromJSON", function (json) {
return new _FunctionNode(json.fn, json.args);
});
return FunctionNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,219 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { map } from '../../utils/array.js';
import { getSafeProperty } from '../../utils/customs.js';
import { factory } from '../../utils/factory.js';
import { isArray, isConstantNode, isMatrix, isNode, isString, typeOf } from '../../utils/is.js';
import { escape } from '../../utils/string.js';
var name = 'IndexNode';
var dependencies = ['Node', 'size'];
export var createIndexNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
Node,
size
} = _ref;
class IndexNode extends Node {
/**
* @constructor IndexNode
* @extends Node
*
* Describes a subset of a matrix or an object property.
* Cannot be used on its own, needs to be used within an AccessorNode or
* AssignmentNode.
*
* @param {Node[]} dimensions
* @param {boolean} [dotNotation=false]
* Optional property describing whether this index was written using dot
* notation like `a.b`, or using bracket notation like `a["b"]`
* (which is the default). This property is used for string conversion.
*/
constructor(dimensions, dotNotation) {
super();
this.dimensions = dimensions;
this.dotNotation = dotNotation || false;
// validate input
if (!Array.isArray(dimensions) || !dimensions.every(isNode)) {
throw new TypeError('Array containing Nodes expected for parameter "dimensions"');
}
if (this.dotNotation && !this.isObjectProperty()) {
throw new Error('dotNotation only applicable for object properties');
}
}
get type() {
return name;
}
get isIndexNode() {
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) {
// TODO: implement support for bignumber (currently bignumbers are silently
// reduced to numbers when changing the value to zero-based)
// TODO: Optimization: when the range values are ConstantNodes,
// we can beforehand resolve the zero-based value
// optimization for a simple object property
var evalDimensions = map(this.dimensions, function (dimension, i) {
var needsEnd = dimension.filter(node => node.isSymbolNode && node.name === 'end').length > 0;
if (needsEnd) {
// SymbolNode 'end' is used inside the index,
// like in `A[end]` or `A[end - 2]`
var childArgNames = Object.create(argNames);
childArgNames.end = true;
var _evalDimension = dimension._compile(math, childArgNames);
return function evalDimension(scope, args, context) {
if (!isMatrix(context) && !isArray(context) && !isString(context)) {
throw new TypeError('Cannot resolve "end": ' + 'context must be a Matrix, Array, or string but is ' + typeOf(context));
}
var s = size(context).valueOf();
var childArgs = Object.create(args);
childArgs.end = s[i];
return _evalDimension(scope, childArgs, context);
};
} else {
// SymbolNode `end` not used
return dimension._compile(math, argNames);
}
});
var index = getSafeProperty(math, 'index');
return function evalIndexNode(scope, args, context) {
var dimensions = map(evalDimensions, function (evalDimension) {
return evalDimension(scope, args, context);
});
return index(...dimensions);
};
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
for (var i = 0; i < this.dimensions.length; i++) {
callback(this.dimensions[i], 'dimensions[' + i + ']', this);
}
}
/**
* Create a new IndexNode 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 {IndexNode} Returns a transformed copy of the node
*/
map(callback) {
var dimensions = [];
for (var i = 0; i < this.dimensions.length; i++) {
dimensions[i] = this._ifNode(callback(this.dimensions[i], 'dimensions[' + i + ']', this));
}
return new IndexNode(dimensions, this.dotNotation);
}
/**
* Create a clone of this node, a shallow copy
* @return {IndexNode}
*/
clone() {
return new IndexNode(this.dimensions.slice(0), this.dotNotation);
}
/**
* Test whether this IndexNode contains a single property name
* @return {boolean}
*/
isObjectProperty() {
return this.dimensions.length === 1 && isConstantNode(this.dimensions[0]) && typeof this.dimensions[0].value === 'string';
}
/**
* Returns the property name if IndexNode contains a property.
* If not, returns null.
* @return {string | null}
*/
getObjectProperty() {
return this.isObjectProperty() ? this.dimensions[0].value : null;
}
/**
* Get string representation
* @param {Object} options
* @return {string} str
*/
_toString(options) {
// format the parameters like "[1, 0:5]"
return this.dotNotation ? '.' + this.getObjectProperty() : '[' + this.dimensions.join(', ') + ']';
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
dimensions: this.dimensions,
dotNotation: this.dotNotation
};
}
/**
* Instantiate an IndexNode from its JSON representation
* @param {Object} json
* An object structured like
* `{"mathjs": "IndexNode", dimensions: [...], dotNotation: false}`,
* where mathjs is optional
* @returns {IndexNode}
*/
static fromJSON(json) {
return new IndexNode(json.dimensions, json.dotNotation);
}
/**
* Get HTML representation
* @param {Object} options
* @return {string} str
*/
_toHTML(options) {
// format the parameters like "[1, 0:5]"
var dimensions = [];
for (var i = 0; i < this.dimensions.length; i++) {
dimensions[i] = this.dimensions[i].toHTML();
}
if (this.dotNotation) {
return '<span class="math-operator math-accessor-operator">.</span>' + '<span class="math-symbol math-property">' + escape(this.getObjectProperty()) + '</span>';
} else {
return '<span class="math-parenthesis math-square-parenthesis">[</span>' + dimensions.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-square-parenthesis">]</span>';
}
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string} str
*/
_toTex(options) {
var dimensions = this.dimensions.map(function (range) {
return range.toTex(options);
});
return this.dotNotation ? '.' + this.getObjectProperty() + '' : '_{' + dimensions.join(',') + '}';
}
}
_defineProperty(IndexNode, "name", name);
return IndexNode;
}, {
isClass: true,
isNode: true
});

372
node_modules/mathjs/lib/esm/expression/node/Node.js generated vendored Normal file
View File

@@ -0,0 +1,372 @@
import { isNode } from '../../utils/is.js';
import { keywords } from '../keywords.js';
import { deepStrictEqual } from '../../utils/object.js';
import { factory } from '../../utils/factory.js';
import { createMap } from '../../utils/map.js';
var name = 'Node';
var dependencies = ['mathWithTransform'];
export var createNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
mathWithTransform
} = _ref;
/**
* Validate the symbol names of a scope.
* Throws an error when the scope contains an illegal symbol.
* @param {Object} scope
*/
function _validateScope(scope) {
for (var symbol of [...keywords]) {
if (scope.has(symbol)) {
throw new Error('Scope contains an illegal symbol, "' + symbol + '" is a reserved keyword');
}
}
}
class Node {
get type() {
return 'Node';
}
get isNode() {
return true;
}
/**
* Evaluate the node
* @param {Object} [scope] Scope to read/write variables
* @return {*} Returns the result
*/
evaluate(scope) {
return this.compile().evaluate(scope);
}
/**
* Compile the node into an optimized, evauatable 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.
*/
compile() {
var expr = this._compile(mathWithTransform, {});
var args = {};
var context = null;
function evaluate(scope) {
var s = createMap(scope);
_validateScope(s);
return expr(s, args, context);
}
return {
evaluate
};
}
/**
* 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) {
throw new Error('Method _compile must be implemented by type ' + this.type);
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
// must be implemented by each of the Node implementations
throw new Error('Cannot run forEach on a Node interface');
}
/**
* Create a new Node 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 {OperatorNode} Returns a transformed copy of the node
*/
map(callback) {
// must be implemented by each of the Node implementations
throw new Error('Cannot run map on a Node interface');
}
/**
* Validate whether an object is a Node, for use with map
* @param {Node} node
* @returns {Node} Returns the input if it's a node, else throws an Error
* @protected
*/
_ifNode(node) {
if (!isNode(node)) {
throw new TypeError('Callback function must return a Node');
}
return node;
}
/**
* Recursively traverse all nodes in a node tree. Executes given callback for
* this node and each of its child nodes.
* @param {function(node: Node, path: string, parent: Node)} callback
* A callback called for every node in the node tree.
*/
traverse(callback) {
// execute callback for itself
// eslint-disable-next-line
callback(this, null, null);
// recursively traverse over all children of a node
function _traverse(node, callback) {
node.forEach(function (child, path, parent) {
callback(child, path, parent);
_traverse(child, callback);
});
}
_traverse(this, callback);
}
/**
* Recursively transform a node tree via a transform function.
*
* For example, to replace all nodes of type SymbolNode having name 'x' with
* a ConstantNode with value 2:
*
* const res = Node.transform(function (node, path, parent) {
* if (node && node.isSymbolNode) && (node.name === 'x')) {
* return new ConstantNode(2)
* }
* else {
* return node
* }
* })
*
* @param {function(node: Node, path: string, parent: Node) : Node} callback
* A mapping function accepting a node, and returning
* a replacement for the node or the original node. The "signature"
* of the callback must be:
* callback(node: Node, index: string, parent: Node) : Node
* @return {Node} Returns the original node or its replacement
*/
transform(callback) {
function _transform(child, path, parent) {
var replacement = callback(child, path, parent);
if (replacement !== child) {
// stop iterating when the node is replaced
return replacement;
}
return child.map(_transform);
}
return _transform(this, null, null);
}
/**
* Find any node in the node tree matching given filter function. For
* example, to find all nodes of type SymbolNode having name 'x':
*
* const results = Node.filter(function (node) {
* return (node && node.isSymbolNode) && (node.name === 'x')
* })
*
* @param {function(node: Node, path: string, parent: Node) : Node} callback
* A test function returning true when a node matches, and false
* otherwise. Function signature:
* callback(node: Node, index: string, parent: Node) : boolean
* @return {Node[]} nodes
* An array with nodes matching given filter criteria
*/
filter(callback) {
var nodes = [];
this.traverse(function (node, path, parent) {
if (callback(node, path, parent)) {
nodes.push(node);
}
});
return nodes;
}
/**
* Create a shallow clone of this node
* @return {Node}
*/
clone() {
// must be implemented by each of the Node implementations
throw new Error('Cannot clone a Node interface');
}
/**
* Create a deep clone of this node
* @return {Node}
*/
cloneDeep() {
return this.map(function (node) {
return node.cloneDeep();
});
}
/**
* Deep compare this node with another node.
* @param {Node} other
* @return {boolean} Returns true when both nodes are of the same type and
* contain the same values (as do their childs)
*/
equals(other) {
return other ? this.type === other.type && deepStrictEqual(this, other) : false;
}
/**
* Get string representation. (wrapper function)
*
* This function can get an object of the following form:
* {
* handler: //This can be a callback function of the form
* // "function callback(node, options)"or
* // a map that maps function names (used in FunctionNodes)
* // to callbacks
* parenthesis: "keep" //the parenthesis option (This is optional)
* }
*
* @param {Object} [options]
* @return {string}
*/
toString(options) {
var customString = this._getCustomString(options);
if (typeof customString !== 'undefined') {
return customString;
}
return this._toString(options);
}
/**
* Internal function to generate the string output.
* This has to be implemented by every Node
*
* @throws {Error}
*/
_toString() {
// must be implemented by each of the Node implementations
throw new Error('_toString not implemented for ' + this.type);
}
/**
* Get a JSON representation of the node
* Both .toJSON() and the static .fromJSON(json) should be implemented by all
* implementations of Node
* @returns {Object}
*/
toJSON() {
throw new Error('Cannot serialize object: toJSON not implemented by ' + this.type);
}
/**
* Get HTML representation. (wrapper function)
*
* This function can get an object of the following form:
* {
* handler: //This can be a callback function of the form
* // "function callback(node, options)" or
* // a map that maps function names (used in FunctionNodes)
* // to callbacks
* parenthesis: "keep" //the parenthesis option (This is optional)
* }
*
* @param {Object} [options]
* @return {string}
*/
toHTML(options) {
var customString = this._getCustomString(options);
if (typeof customString !== 'undefined') {
return customString;
}
return this._toHTML(options);
}
/**
* Internal function to generate the HTML output.
* This has to be implemented by every Node
*
* @throws {Error}
*/
_toHTML() {
// must be implemented by each of the Node implementations
throw new Error('_toHTML not implemented for ' + this.type);
}
/**
* Get LaTeX representation. (wrapper function)
*
* This function can get an object of the following form:
* {
* handler: //This can be a callback function of the form
* // "function callback(node, options)"or
* // a map that maps function names (used in FunctionNodes)
* // to callbacks
* parenthesis: "keep" //the parenthesis option (This is optional)
* }
*
* @param {Object} [options]
* @return {string}
*/
toTex(options) {
var customString = this._getCustomString(options);
if (typeof customString !== 'undefined') {
return customString;
}
return this._toTex(options);
}
/**
* Internal function to generate the LaTeX output.
* This has to be implemented by every Node
*
* @param {Object} [options]
* @throws {Error}
*/
_toTex(options) {
// must be implemented by each of the Node implementations
throw new Error('_toTex not implemented for ' + this.type);
}
/**
* Helper used by `to...` functions.
*/
_getCustomString(options) {
if (options && typeof options === 'object') {
switch (typeof options.handler) {
case 'object':
case 'undefined':
return;
case 'function':
return options.handler(this, options);
default:
throw new TypeError('Object or function expected as callback');
}
}
}
/**
* Get identifier.
* @return {string}
*/
getIdentifier() {
return this.type;
}
/**
* Get the content of the current Node.
* @return {Node} node
**/
getContent() {
return this;
}
}
return Node;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,193 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { getSafeProperty } from '../../utils/customs.js';
import { factory } from '../../utils/factory.js';
import { isNode } from '../../utils/is.js';
import { hasOwnProperty } from '../../utils/object.js';
import { escape, stringify } from '../../utils/string.js';
var name = 'ObjectNode';
var dependencies = ['Node'];
export var createObjectNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
Node
} = _ref;
class ObjectNode extends Node {
/**
* @constructor ObjectNode
* @extends {Node}
* Holds an object with keys/values
* @param {Object.<string, Node>} [properties] object with key/value pairs
*/
constructor(properties) {
super();
this.properties = properties || {};
// validate input
if (properties) {
if (!(typeof properties === 'object') || !Object.keys(properties).every(function (key) {
return isNode(properties[key]);
})) {
throw new TypeError('Object containing Nodes expected');
}
}
}
get type() {
return name;
}
get isObjectNode() {
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) {
var evalEntries = {};
for (var key in this.properties) {
if (hasOwnProperty(this.properties, key)) {
// we stringify/parse the key here to resolve unicode characters,
// so you cannot create a key like {"co\\u006Estructor": null}
var stringifiedKey = stringify(key);
var parsedKey = JSON.parse(stringifiedKey);
var prop = getSafeProperty(this.properties, key);
evalEntries[parsedKey] = prop._compile(math, argNames);
}
}
return function evalObjectNode(scope, args, context) {
var obj = {};
for (var _key in evalEntries) {
if (hasOwnProperty(evalEntries, _key)) {
obj[_key] = evalEntries[_key](scope, args, context);
}
}
return obj;
};
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
for (var key in this.properties) {
if (hasOwnProperty(this.properties, key)) {
callback(this.properties[key], 'properties[' + stringify(key) + ']', this);
}
}
}
/**
* Create a new ObjectNode 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 {ObjectNode} Returns a transformed copy of the node
*/
map(callback) {
var properties = {};
for (var key in this.properties) {
if (hasOwnProperty(this.properties, key)) {
properties[key] = this._ifNode(callback(this.properties[key], 'properties[' + stringify(key) + ']', this));
}
}
return new ObjectNode(properties);
}
/**
* Create a clone of this node, a shallow copy
* @return {ObjectNode}
*/
clone() {
var properties = {};
for (var key in this.properties) {
if (hasOwnProperty(this.properties, key)) {
properties[key] = this.properties[key];
}
}
return new ObjectNode(properties);
}
/**
* Get string representation
* @param {Object} options
* @return {string} str
* @override
*/
_toString(options) {
var entries = [];
for (var key in this.properties) {
if (hasOwnProperty(this.properties, key)) {
entries.push(stringify(key) + ': ' + this.properties[key].toString(options));
}
}
return '{' + entries.join(', ') + '}';
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
properties: this.properties
};
}
/**
* Instantiate an OperatorNode from its JSON representation
* @param {Object} json An object structured like
* `{"mathjs": "ObjectNode", "properties": {...}}`,
* where mathjs is optional
* @returns {ObjectNode}
*/
static fromJSON(json) {
return new ObjectNode(json.properties);
}
/**
* Get HTML representation
* @param {Object} options
* @return {string} str
* @override
*/
_toHTML(options) {
var entries = [];
for (var key in this.properties) {
if (hasOwnProperty(this.properties, key)) {
entries.push('<span class="math-symbol math-property">' + escape(key) + '</span>' + '<span class="math-operator math-assignment-operator ' + 'math-property-assignment-operator math-binary-operator">' + ':</span>' + this.properties[key].toHTML(options));
}
}
return '<span class="math-parenthesis math-curly-parenthesis">{</span>' + entries.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-curly-parenthesis">}</span>';
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string} str
*/
_toTex(options) {
var entries = [];
for (var key in this.properties) {
if (hasOwnProperty(this.properties, key)) {
entries.push('\\mathbf{' + key + ':} & ' + this.properties[key].toTex(options) + '\\\\');
}
}
var tex = '\\left\\{\\begin{array}{ll}' + entries.join('\n') + '\\end{array}\\right\\}';
return tex;
}
}
_defineProperty(ObjectNode, "name", name);
return ObjectNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,629 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { isNode, isConstantNode, isOperatorNode, isParenthesisNode } from '../../utils/is.js';
import { map } from '../../utils/array.js';
import { createSubScope } from '../../utils/scope.js';
import { escape } from '../../utils/string.js';
import { getSafeProperty, isSafeMethod } from '../../utils/customs.js';
import { getAssociativity, getPrecedence, isAssociativeWith, properties } from '../operators.js';
import { latexOperators } from '../../utils/latex.js';
import { factory } from '../../utils/factory.js';
var name = 'OperatorNode';
var dependencies = ['Node'];
export var createOperatorNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
Node
} = _ref;
/**
* Returns true if the expression starts with a constant, under
* the current parenthesization:
* @param {Node} expression
* @param {string} parenthesis
* @return {boolean}
*/
function startsWithConstant(expr, parenthesis) {
var curNode = expr;
if (parenthesis === 'auto') {
while (isParenthesisNode(curNode)) curNode = curNode.content;
}
if (isConstantNode(curNode)) return true;
if (isOperatorNode(curNode)) {
return startsWithConstant(curNode.args[0], parenthesis);
}
return false;
}
/**
* Calculate which parentheses are necessary. Gets an OperatorNode
* (which is the root of the tree) and an Array of Nodes
* (this.args) and returns an array where 'true' means that an argument
* has to be enclosed in parentheses whereas 'false' means the opposite.
*
* @param {OperatorNode} root
* @param {string} parenthesis
* @param {Node[]} args
* @param {boolean} latex
* @return {boolean[]}
* @private
*/
function calculateNecessaryParentheses(root, parenthesis, implicit, args, latex) {
// precedence of the root OperatorNode
var precedence = getPrecedence(root, parenthesis, implicit);
var associativity = getAssociativity(root, parenthesis);
if (parenthesis === 'all' || args.length > 2 && root.getIdentifier() !== 'OperatorNode:add' && root.getIdentifier() !== 'OperatorNode:multiply') {
return args.map(function (arg) {
switch (arg.getContent().type) {
// Nodes that don't need extra parentheses
case 'ArrayNode':
case 'ConstantNode':
case 'SymbolNode':
case 'ParenthesisNode':
return false;
default:
return true;
}
});
}
var result;
switch (args.length) {
case 0:
result = [];
break;
case 1:
// unary operators
{
// precedence of the operand
var operandPrecedence = getPrecedence(args[0], parenthesis, implicit, root);
// handle special cases for LaTeX, where some of the parentheses aren't needed
if (latex && operandPrecedence !== null) {
var operandIdentifier;
var rootIdentifier;
if (parenthesis === 'keep') {
operandIdentifier = args[0].getIdentifier();
rootIdentifier = root.getIdentifier();
} else {
// Ignore Parenthesis Nodes when not in 'keep' mode
operandIdentifier = args[0].getContent().getIdentifier();
rootIdentifier = root.getContent().getIdentifier();
}
if (properties[precedence][rootIdentifier].latexLeftParens === false) {
result = [false];
break;
}
if (properties[operandPrecedence][operandIdentifier].latexParens === false) {
result = [false];
break;
}
}
if (operandPrecedence === null) {
// if the operand has no defined precedence, no parens are needed
result = [false];
break;
}
if (operandPrecedence <= precedence) {
// if the operands precedence is lower, parens are needed
result = [true];
break;
}
// otherwise, no parens needed
result = [false];
}
break;
case 2:
// binary operators
{
var lhsParens; // left hand side needs parenthesis?
// precedence of the left hand side
var lhsPrecedence = getPrecedence(args[0], parenthesis, implicit, root);
// is the root node associative with the left hand side
var assocWithLhs = isAssociativeWith(root, args[0], parenthesis);
if (lhsPrecedence === null) {
// if the left hand side has no defined precedence, no parens are needed
// FunctionNode for example
lhsParens = false;
} else if (lhsPrecedence === precedence && associativity === 'right' && !assocWithLhs) {
// In case of equal precedence, if the root node is left associative
// parens are **never** necessary for the left hand side.
// If it is right associative however, parens are necessary
// if the root node isn't associative with the left hand side
lhsParens = true;
} else if (lhsPrecedence < precedence) {
lhsParens = true;
} else {
lhsParens = false;
}
var rhsParens; // right hand side needs parenthesis?
// precedence of the right hand side
var rhsPrecedence = getPrecedence(args[1], parenthesis, implicit, root);
// is the root node associative with the right hand side?
var assocWithRhs = isAssociativeWith(root, args[1], parenthesis);
if (rhsPrecedence === null) {
// if the right hand side has no defined precedence, no parens are needed
// FunctionNode for example
rhsParens = false;
} else if (rhsPrecedence === precedence && associativity === 'left' && !assocWithRhs) {
// In case of equal precedence, if the root node is right associative
// parens are **never** necessary for the right hand side.
// If it is left associative however, parens are necessary
// if the root node isn't associative with the right hand side
rhsParens = true;
} else if (rhsPrecedence < precedence) {
rhsParens = true;
} else {
rhsParens = false;
}
// handle special cases for LaTeX, where some of the parentheses aren't needed
if (latex) {
var _rootIdentifier;
var lhsIdentifier;
var rhsIdentifier;
if (parenthesis === 'keep') {
_rootIdentifier = root.getIdentifier();
lhsIdentifier = root.args[0].getIdentifier();
rhsIdentifier = root.args[1].getIdentifier();
} else {
// Ignore ParenthesisNodes when not in 'keep' mode
_rootIdentifier = root.getContent().getIdentifier();
lhsIdentifier = root.args[0].getContent().getIdentifier();
rhsIdentifier = root.args[1].getContent().getIdentifier();
}
if (lhsPrecedence !== null) {
if (properties[precedence][_rootIdentifier].latexLeftParens === false) {
lhsParens = false;
}
if (properties[lhsPrecedence][lhsIdentifier].latexParens === false) {
lhsParens = false;
}
}
if (rhsPrecedence !== null) {
if (properties[precedence][_rootIdentifier].latexRightParens === false) {
rhsParens = false;
}
if (properties[rhsPrecedence][rhsIdentifier].latexParens === false) {
rhsParens = false;
}
}
}
result = [lhsParens, rhsParens];
}
break;
default:
if (root.getIdentifier() === 'OperatorNode:add' || root.getIdentifier() === 'OperatorNode:multiply') {
result = args.map(function (arg) {
var argPrecedence = getPrecedence(arg, parenthesis, implicit, root);
var assocWithArg = isAssociativeWith(root, arg, parenthesis);
var argAssociativity = getAssociativity(arg, parenthesis);
if (argPrecedence === null) {
// if the argument has no defined precedence, no parens are needed
return false;
} else if (precedence === argPrecedence && associativity === argAssociativity && !assocWithArg) {
return true;
} else if (argPrecedence < precedence) {
return true;
}
return false;
});
}
break;
}
// Handles an edge case of parentheses with implicit multiplication
// of ConstantNode.
// In that case, parenthesize ConstantNodes that follow an unparenthesized
// expression, even though they normally wouldn't be printed.
if (args.length >= 2 && root.getIdentifier() === 'OperatorNode:multiply' && root.implicit && parenthesis !== 'all' && implicit === 'hide') {
for (var i = 1; i < result.length; ++i) {
if (startsWithConstant(args[i], parenthesis) && !result[i - 1] && (parenthesis !== 'keep' || !isParenthesisNode(args[i - 1]))) {
result[i] = true;
}
}
}
return result;
}
class OperatorNode extends Node {
/**
* @constructor OperatorNode
* @extends {Node}
* An operator with two arguments, like 2+3
*
* @param {string} op Operator name, for example '+'
* @param {string} fn Function name, for example 'add'
* @param {Node[]} args Operator arguments
* @param {boolean} [implicit] Is this an implicit multiplication?
* @param {boolean} [isPercentage] Is this an percentage Operation?
*/
constructor(op, fn, args, implicit, isPercentage) {
super();
// validate input
if (typeof op !== 'string') {
throw new TypeError('string expected for parameter "op"');
}
if (typeof fn !== 'string') {
throw new TypeError('string expected for parameter "fn"');
}
if (!Array.isArray(args) || !args.every(isNode)) {
throw new TypeError('Array containing Nodes expected for parameter "args"');
}
this.implicit = implicit === true;
this.isPercentage = isPercentage === true;
this.op = op;
this.fn = fn;
this.args = args || [];
}
get type() {
return name;
}
get isOperatorNode() {
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) {
// validate fn
if (typeof this.fn !== 'string' || !isSafeMethod(math, this.fn)) {
if (!math[this.fn]) {
throw new Error('Function ' + this.fn + ' missing in provided namespace "math"');
} else {
throw new Error('No access to function "' + this.fn + '"');
}
}
var fn = getSafeProperty(math, this.fn);
var evalArgs = map(this.args, function (arg) {
return arg._compile(math, argNames);
});
if (typeof fn === 'function' && fn.rawArgs === true) {
// pass unevaluated parameters (nodes) to the function
// "raw" evaluation
var rawArgs = this.args;
return function evalOperatorNode(scope, args, context) {
return fn(rawArgs, math, createSubScope(scope, args));
};
} else if (evalArgs.length === 1) {
var evalArg0 = evalArgs[0];
return function evalOperatorNode(scope, args, context) {
return fn(evalArg0(scope, args, context));
};
} else if (evalArgs.length === 2) {
var _evalArg = evalArgs[0];
var evalArg1 = evalArgs[1];
return function evalOperatorNode(scope, args, context) {
return fn(_evalArg(scope, args, context), evalArg1(scope, args, context));
};
} else {
return function evalOperatorNode(scope, args, context) {
return fn.apply(null, map(evalArgs, function (evalArg) {
return evalArg(scope, args, context);
}));
};
}
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
for (var i = 0; i < this.args.length; i++) {
callback(this.args[i], 'args[' + i + ']', this);
}
}
/**
* Create a new OperatorNode 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 {OperatorNode} Returns a transformed copy of the node
*/
map(callback) {
var args = [];
for (var i = 0; i < this.args.length; i++) {
args[i] = this._ifNode(callback(this.args[i], 'args[' + i + ']', this));
}
return new OperatorNode(this.op, this.fn, args, this.implicit, this.isPercentage);
}
/**
* Create a clone of this node, a shallow copy
* @return {OperatorNode}
*/
clone() {
return new OperatorNode(this.op, this.fn, this.args.slice(0), this.implicit, this.isPercentage);
}
/**
* Check whether this is an unary OperatorNode:
* has exactly one argument, like `-a`.
* @return {boolean}
* Returns true when an unary operator node, false otherwise.
*/
isUnary() {
return this.args.length === 1;
}
/**
* Check whether this is a binary OperatorNode:
* has exactly two arguments, like `a + b`.
* @return {boolean}
* Returns true when a binary operator node, false otherwise.
*/
isBinary() {
return this.args.length === 2;
}
/**
* Get string representation.
* @param {Object} options
* @return {string} str
*/
_toString(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var implicit = options && options.implicit ? options.implicit : 'hide';
var args = this.args;
var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, false);
if (args.length === 1) {
// unary operators
var assoc = getAssociativity(this, parenthesis);
var operand = args[0].toString(options);
if (parens[0]) {
operand = '(' + operand + ')';
}
// for example for "not", we want a space between operand and argument
var opIsNamed = /[a-zA-Z]+/.test(this.op);
if (assoc === 'right') {
// prefix operator
return this.op + (opIsNamed ? ' ' : '') + operand;
} else if (assoc === 'left') {
// postfix
return operand + (opIsNamed ? ' ' : '') + this.op;
}
// fall back to postfix
return operand + this.op;
} else if (args.length === 2) {
var lhs = args[0].toString(options); // left hand side
var rhs = args[1].toString(options); // right hand side
if (parens[0]) {
// left hand side in parenthesis?
lhs = '(' + lhs + ')';
}
if (parens[1]) {
// right hand side in parenthesis?
rhs = '(' + rhs + ')';
}
if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
return lhs + ' ' + rhs;
}
return lhs + ' ' + this.op + ' ' + rhs;
} else if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) {
var stringifiedArgs = args.map(function (arg, index) {
arg = arg.toString(options);
if (parens[index]) {
// put in parenthesis?
arg = '(' + arg + ')';
}
return arg;
});
if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
return stringifiedArgs.join(' ');
}
return stringifiedArgs.join(' ' + this.op + ' ');
} else {
// fallback to formatting as a function call
return this.fn + '(' + this.args.join(', ') + ')';
}
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
op: this.op,
fn: this.fn,
args: this.args,
implicit: this.implicit,
isPercentage: this.isPercentage
};
}
/**
* Instantiate an OperatorNode from its JSON representation
* @param {Object} json
* An object structured like
* ```
* {"mathjs": "OperatorNode",
* "op": "+", "fn": "add", "args": [...],
* "implicit": false,
* "isPercentage":false}
* ```
* where mathjs is optional
* @returns {OperatorNode}
*/
static fromJSON(json) {
return new OperatorNode(json.op, json.fn, json.args, json.implicit, json.isPercentage);
}
/**
* Get HTML representation.
* @param {Object} options
* @return {string} str
*/
_toHTML(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var implicit = options && options.implicit ? options.implicit : 'hide';
var args = this.args;
var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, false);
if (args.length === 1) {
// unary operators
var assoc = getAssociativity(this, parenthesis);
var operand = args[0].toHTML(options);
if (parens[0]) {
operand = '<span class="math-parenthesis math-round-parenthesis">(</span>' + operand + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
if (assoc === 'right') {
// prefix operator
return '<span class="math-operator math-unary-operator ' + 'math-lefthand-unary-operator">' + escape(this.op) + '</span>' + operand;
} else {
// postfix when assoc === 'left' or undefined
return operand + '<span class="math-operator math-unary-operator ' + 'math-righthand-unary-operator">' + escape(this.op) + '</span>';
}
} else if (args.length === 2) {
// binary operatoes
var lhs = args[0].toHTML(options); // left hand side
var rhs = args[1].toHTML(options); // right hand side
if (parens[0]) {
// left hand side in parenthesis?
lhs = '<span class="math-parenthesis math-round-parenthesis">(</span>' + lhs + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
if (parens[1]) {
// right hand side in parenthesis?
rhs = '<span class="math-parenthesis math-round-parenthesis">(</span>' + rhs + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
return lhs + '<span class="math-operator math-binary-operator ' + 'math-implicit-binary-operator"></span>' + rhs;
}
return lhs + '<span class="math-operator math-binary-operator ' + 'math-explicit-binary-operator">' + escape(this.op) + '</span>' + rhs;
} else {
var stringifiedArgs = args.map(function (arg, index) {
arg = arg.toHTML(options);
if (parens[index]) {
// put in parenthesis?
arg = '<span class="math-parenthesis math-round-parenthesis">(</span>' + arg + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
return arg;
});
if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) {
if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
return stringifiedArgs.join('<span class="math-operator math-binary-operator ' + 'math-implicit-binary-operator"></span>');
}
return stringifiedArgs.join('<span class="math-operator math-binary-operator ' + 'math-explicit-binary-operator">' + escape(this.op) + '</span>');
} else {
// fallback to formatting as a function call
return '<span class="math-function">' + escape(this.fn) + '</span><span class="math-paranthesis math-round-parenthesis">' + '(</span>' + stringifiedArgs.join('<span class="math-separator">,</span>') + '<span class="math-paranthesis math-round-parenthesis">)</span>';
}
}
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string} str
*/
_toTex(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var implicit = options && options.implicit ? options.implicit : 'hide';
var args = this.args;
var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, true);
var op = latexOperators[this.fn];
op = typeof op === 'undefined' ? this.op : op; // fall back to using this.op
if (args.length === 1) {
// unary operators
var assoc = getAssociativity(this, parenthesis);
var operand = args[0].toTex(options);
if (parens[0]) {
operand = "\\left(".concat(operand, "\\right)");
}
if (assoc === 'right') {
// prefix operator
return op + operand;
} else if (assoc === 'left') {
// postfix operator
return operand + op;
}
// fall back to postfix
return operand + op;
} else if (args.length === 2) {
// binary operators
var lhs = args[0]; // left hand side
var lhsTex = lhs.toTex(options);
if (parens[0]) {
lhsTex = "\\left(".concat(lhsTex, "\\right)");
}
var rhs = args[1]; // right hand side
var rhsTex = rhs.toTex(options);
if (parens[1]) {
rhsTex = "\\left(".concat(rhsTex, "\\right)");
}
// handle some exceptions (due to the way LaTeX works)
var lhsIdentifier;
if (parenthesis === 'keep') {
lhsIdentifier = lhs.getIdentifier();
} else {
// Ignore ParenthesisNodes if in 'keep' mode
lhsIdentifier = lhs.getContent().getIdentifier();
}
switch (this.getIdentifier()) {
case 'OperatorNode:divide':
// op contains '\\frac' at this point
return op + '{' + lhsTex + '}' + '{' + rhsTex + '}';
case 'OperatorNode:pow':
lhsTex = '{' + lhsTex + '}';
rhsTex = '{' + rhsTex + '}';
switch (lhsIdentifier) {
case 'ConditionalNode': //
case 'OperatorNode:divide':
lhsTex = "\\left(".concat(lhsTex, "\\right)");
}
break;
case 'OperatorNode:multiply':
if (this.implicit && implicit === 'hide') {
return lhsTex + '~' + rhsTex;
}
}
return lhsTex + op + rhsTex;
} else if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) {
var texifiedArgs = args.map(function (arg, index) {
arg = arg.toTex(options);
if (parens[index]) {
arg = "\\left(".concat(arg, "\\right)");
}
return arg;
});
if (this.getIdentifier() === 'OperatorNode:multiply' && this.implicit && implicit === 'hide') {
return texifiedArgs.join('~');
}
return texifiedArgs.join(op);
} else {
// fall back to formatting as a function call
// as this is a fallback, it doesn't use
// fancy function names
return '\\mathrm{' + this.fn + '}\\left(' + args.map(function (arg) {
return arg.toTex(options);
}).join(',') + '\\right)';
}
}
/**
* Get identifier.
* @return {string}
*/
getIdentifier() {
return this.type + ':' + this.fn;
}
}
_defineProperty(OperatorNode, "name", name);
return OperatorNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,152 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { isNode } from '../../utils/is.js';
import { factory } from '../../utils/factory.js';
var name = 'ParenthesisNode';
var dependencies = ['Node'];
export var createParenthesisNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
Node
} = _ref;
class ParenthesisNode extends Node {
/**
* @constructor ParenthesisNode
* @extends {Node}
* A parenthesis node describes manual parenthesis from the user input
* @param {Node} content
* @extends {Node}
*/
constructor(content) {
super();
// validate input
if (!isNode(content)) {
throw new TypeError('Node expected for parameter "content"');
}
this.content = content;
}
get type() {
return name;
}
get isParenthesisNode() {
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) {
return this.content._compile(math, argNames);
}
/**
* Get the content of the current Node.
* @return {Node} content
* @override
**/
getContent() {
return this.content.getContent();
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
callback(this.content, 'content', this);
}
/**
* Create a new ParenthesisNode whose child is the result of calling
* the provided callback function on the child of this node.
* @param {function(child: Node, path: string, parent: Node) : Node} callback
* @returns {ParenthesisNode} Returns a clone of the node
*/
map(callback) {
var content = callback(this.content, 'content', this);
return new ParenthesisNode(content);
}
/**
* Create a clone of this node, a shallow copy
* @return {ParenthesisNode}
*/
clone() {
return new ParenthesisNode(this.content);
}
/**
* Get string representation
* @param {Object} options
* @return {string} str
* @override
*/
_toString(options) {
if (!options || options && !options.parenthesis || options && options.parenthesis === 'keep') {
return '(' + this.content.toString(options) + ')';
}
return this.content.toString(options);
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
content: this.content
};
}
/**
* Instantiate an ParenthesisNode from its JSON representation
* @param {Object} json An object structured like
* `{"mathjs": "ParenthesisNode", "content": ...}`,
* where mathjs is optional
* @returns {ParenthesisNode}
*/
static fromJSON(json) {
return new ParenthesisNode(json.content);
}
/**
* Get HTML representation
* @param {Object} options
* @return {string} str
* @override
*/
_toHTML(options) {
if (!options || options && !options.parenthesis || options && options.parenthesis === 'keep') {
return '<span class="math-parenthesis math-round-parenthesis">(</span>' + this.content.toHTML(options) + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
return this.content.toHTML(options);
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string} str
* @override
*/
_toTex(options) {
if (!options || options && !options.parenthesis || options && options.parenthesis === 'keep') {
return "\\left(".concat(this.content.toTex(options), "\\right)");
}
return this.content.toTex(options);
}
}
_defineProperty(ParenthesisNode, "name", name);
return ParenthesisNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,250 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { isNode, isSymbolNode } from '../../utils/is.js';
import { factory } from '../../utils/factory.js';
import { getPrecedence } from '../operators.js';
var name = 'RangeNode';
var dependencies = ['Node'];
export var createRangeNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
Node
} = _ref;
/**
* Calculate the necessary parentheses
* @param {Node} node
* @param {string} parenthesis
* @param {string} implicit
* @return {Object} parentheses
* @private
*/
function calculateNecessaryParentheses(node, parenthesis, implicit) {
var precedence = getPrecedence(node, parenthesis, implicit);
var parens = {};
var startPrecedence = getPrecedence(node.start, parenthesis, implicit);
parens.start = startPrecedence !== null && startPrecedence <= precedence || parenthesis === 'all';
if (node.step) {
var stepPrecedence = getPrecedence(node.step, parenthesis, implicit);
parens.step = stepPrecedence !== null && stepPrecedence <= precedence || parenthesis === 'all';
}
var endPrecedence = getPrecedence(node.end, parenthesis, implicit);
parens.end = endPrecedence !== null && endPrecedence <= precedence || parenthesis === 'all';
return parens;
}
class RangeNode extends Node {
/**
* @constructor RangeNode
* @extends {Node}
* create a range
* @param {Node} start included lower-bound
* @param {Node} end included upper-bound
* @param {Node} [step] optional step
*/
constructor(start, end, step) {
super();
// validate inputs
if (!isNode(start)) throw new TypeError('Node expected');
if (!isNode(end)) throw new TypeError('Node expected');
if (step && !isNode(step)) throw new TypeError('Node expected');
if (arguments.length > 3) throw new Error('Too many arguments');
this.start = start; // included lower-bound
this.end = end; // included upper-bound
this.step = step || null; // optional step
}
get type() {
return name;
}
get isRangeNode() {
return true;
}
/**
* Check whether the RangeNode needs the `end` symbol to be defined.
* This end is the size of the Matrix in current dimension.
* @return {boolean}
*/
needsEnd() {
// find all `end` symbols in this RangeNode
var endSymbols = this.filter(function (node) {
return isSymbolNode(node) && node.name === 'end';
});
return endSymbols.length > 0;
}
/**
* 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) {
var range = math.range;
var evalStart = this.start._compile(math, argNames);
var evalEnd = this.end._compile(math, argNames);
if (this.step) {
var evalStep = this.step._compile(math, argNames);
return function evalRangeNode(scope, args, context) {
return range(evalStart(scope, args, context), evalEnd(scope, args, context), evalStep(scope, args, context));
};
} else {
return function evalRangeNode(scope, args, context) {
return range(evalStart(scope, args, context), evalEnd(scope, args, context));
};
}
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
callback(this.start, 'start', this);
callback(this.end, 'end', this);
if (this.step) {
callback(this.step, 'step', this);
}
}
/**
* Create a new RangeNode 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 {RangeNode} Returns a transformed copy of the node
*/
map(callback) {
return new RangeNode(this._ifNode(callback(this.start, 'start', this)), this._ifNode(callback(this.end, 'end', this)), this.step && this._ifNode(callback(this.step, 'step', this)));
}
/**
* Create a clone of this node, a shallow copy
* @return {RangeNode}
*/
clone() {
return new RangeNode(this.start, this.end, this.step && this.step);
}
/**
* Get string representation
* @param {Object} options
* @return {string} str
*/
_toString(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var parens = calculateNecessaryParentheses(this, parenthesis, options && options.implicit);
// format string as start:step:stop
var str;
var start = this.start.toString(options);
if (parens.start) {
start = '(' + start + ')';
}
str = start;
if (this.step) {
var step = this.step.toString(options);
if (parens.step) {
step = '(' + step + ')';
}
str += ':' + step;
}
var end = this.end.toString(options);
if (parens.end) {
end = '(' + end + ')';
}
str += ':' + end;
return str;
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
start: this.start,
end: this.end,
step: this.step
};
}
/**
* Instantiate an RangeNode from its JSON representation
* @param {Object} json
* An object structured like
* `{"mathjs": "RangeNode", "start": ..., "end": ..., "step": ...}`,
* where mathjs is optional
* @returns {RangeNode}
*/
static fromJSON(json) {
return new RangeNode(json.start, json.end, json.step);
}
/**
* Get HTML representation
* @param {Object} options
* @return {string} str
*/
_toHTML(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var parens = calculateNecessaryParentheses(this, parenthesis, options && options.implicit);
// format string as start:step:stop
var str;
var start = this.start.toHTML(options);
if (parens.start) {
start = '<span class="math-parenthesis math-round-parenthesis">(</span>' + start + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
str = start;
if (this.step) {
var step = this.step.toHTML(options);
if (parens.step) {
step = '<span class="math-parenthesis math-round-parenthesis">(</span>' + step + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
str += '<span class="math-operator math-range-operator">:</span>' + step;
}
var end = this.end.toHTML(options);
if (parens.end) {
end = '<span class="math-parenthesis math-round-parenthesis">(</span>' + end + '<span class="math-parenthesis math-round-parenthesis">)</span>';
}
str += '<span class="math-operator math-range-operator">:</span>' + end;
return str;
}
/**
* Get LaTeX representation
* @params {Object} options
* @return {string} str
*/
_toTex(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var parens = calculateNecessaryParentheses(this, parenthesis, options && options.implicit);
var str = this.start.toTex(options);
if (parens.start) {
str = "\\left(".concat(str, "\\right)");
}
if (this.step) {
var step = this.step.toTex(options);
if (parens.step) {
step = "\\left(".concat(step, "\\right)");
}
str += ':' + step;
}
var end = this.end.toTex(options);
if (parens.end) {
end = "\\left(".concat(end, "\\right)");
}
str += ':' + end;
return str;
}
}
_defineProperty(RangeNode, "name", name);
return RangeNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,198 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { getPrecedence } from '../operators.js';
import { escape } from '../../utils/string.js';
import { getSafeProperty } from '../../utils/customs.js';
import { latexOperators } from '../../utils/latex.js';
import { factory } from '../../utils/factory.js';
var name = 'RelationalNode';
var dependencies = ['Node'];
export var createRelationalNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
Node
} = _ref;
var operatorMap = {
equal: '==',
unequal: '!=',
smaller: '<',
larger: '>',
smallerEq: '<=',
largerEq: '>='
};
class RelationalNode extends Node {
/**
* A node representing a chained conditional expression, such as 'x > y > z'
*
* @param {String[]} conditionals
* An array of conditional operators used to compare the parameters
* @param {Node[]} params
* The parameters that will be compared
*
* @constructor RelationalNode
* @extends {Node}
*/
constructor(conditionals, params) {
super();
if (!Array.isArray(conditionals)) {
throw new TypeError('Parameter conditionals must be an array');
}
if (!Array.isArray(params)) {
throw new TypeError('Parameter params must be an array');
}
if (conditionals.length !== params.length - 1) {
throw new TypeError('Parameter params must contain exactly one more element ' + 'than parameter conditionals');
}
this.conditionals = conditionals;
this.params = params;
}
get type() {
return name;
}
get isRelationalNode() {
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) {
var self = this;
var compiled = this.params.map(p => p._compile(math, argNames));
return function evalRelationalNode(scope, args, context) {
var evalLhs;
var evalRhs = compiled[0](scope, args, context);
for (var i = 0; i < self.conditionals.length; i++) {
evalLhs = evalRhs;
evalRhs = compiled[i + 1](scope, args, context);
var condFn = getSafeProperty(math, self.conditionals[i]);
if (!condFn(evalLhs, evalRhs)) {
return false;
}
}
return true;
};
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
this.params.forEach((n, i) => callback(n, 'params[' + i + ']', this), this);
}
/**
* Create a new RelationalNode 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 {RelationalNode} Returns a transformed copy of the node
*/
map(callback) {
return new RelationalNode(this.conditionals.slice(), this.params.map((n, i) => this._ifNode(callback(n, 'params[' + i + ']', this)), this));
}
/**
* Create a clone of this node, a shallow copy
* @return {RelationalNode}
*/
clone() {
return new RelationalNode(this.conditionals, this.params);
}
/**
* Get string representation.
* @param {Object} options
* @return {string} str
*/
_toString(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var precedence = getPrecedence(this, parenthesis, options && options.implicit);
var paramStrings = this.params.map(function (p, index) {
var paramPrecedence = getPrecedence(p, parenthesis, options && options.implicit);
return parenthesis === 'all' || paramPrecedence !== null && paramPrecedence <= precedence ? '(' + p.toString(options) + ')' : p.toString(options);
});
var ret = paramStrings[0];
for (var i = 0; i < this.conditionals.length; i++) {
ret += ' ' + operatorMap[this.conditionals[i]];
ret += ' ' + paramStrings[i + 1];
}
return ret;
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: name,
conditionals: this.conditionals,
params: this.params
};
}
/**
* Instantiate a RelationalNode from its JSON representation
* @param {Object} json
* An object structured like
* `{"mathjs": "RelationalNode", "conditionals": ..., "params": ...}`,
* where mathjs is optional
* @returns {RelationalNode}
*/
static fromJSON(json) {
return new RelationalNode(json.conditionals, json.params);
}
/**
* Get HTML representation
* @param {Object} options
* @return {string} str
*/
_toHTML(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var precedence = getPrecedence(this, parenthesis, options && options.implicit);
var paramStrings = this.params.map(function (p, index) {
var paramPrecedence = getPrecedence(p, parenthesis, options && options.implicit);
return parenthesis === 'all' || paramPrecedence !== null && paramPrecedence <= precedence ? '<span class="math-parenthesis math-round-parenthesis">(</span>' + p.toHTML(options) + '<span class="math-parenthesis math-round-parenthesis">)</span>' : p.toHTML(options);
});
var ret = paramStrings[0];
for (var i = 0; i < this.conditionals.length; i++) {
ret += '<span class="math-operator math-binary-operator ' + 'math-explicit-binary-operator">' + escape(operatorMap[this.conditionals[i]]) + '</span>' + paramStrings[i + 1];
}
return ret;
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string} str
*/
_toTex(options) {
var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
var precedence = getPrecedence(this, parenthesis, options && options.implicit);
var paramStrings = this.params.map(function (p, index) {
var paramPrecedence = getPrecedence(p, parenthesis, options && options.implicit);
return parenthesis === 'all' || paramPrecedence !== null && paramPrecedence <= precedence ? '\\left(' + p.toTex(options) + '\right)' : p.toTex(options);
});
var ret = paramStrings[0];
for (var i = 0; i < this.conditionals.length; i++) {
ret += latexOperators[this.conditionals[i]] + paramStrings[i + 1];
}
return ret;
}
}
_defineProperty(RelationalNode, "name", name);
return RelationalNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,193 @@
import { escape } from '../../utils/string.js';
import { getSafeProperty } from '../../utils/customs.js';
import { factory } from '../../utils/factory.js';
import { toSymbol } from '../../utils/latex.js';
var name = 'SymbolNode';
var dependencies = ['math', '?Unit', 'Node'];
export var createSymbolNode = /* #__PURE__ */factory(name, dependencies, _ref => {
var {
math,
Unit,
Node
} = _ref;
/**
* Check whether some name is a valueless unit like "inch".
* @param {string} name
* @return {boolean}
*/
function isValuelessUnit(name) {
return Unit ? Unit.isValuelessUnit(name) : false;
}
class SymbolNode extends Node {
/**
* @constructor SymbolNode
* @extends {Node}
* A symbol node can hold and resolve a symbol
* @param {string} name
* @extends {Node}
*/
constructor(name) {
super();
// validate input
if (typeof name !== 'string') {
throw new TypeError('String expected for parameter "name"');
}
this.name = name;
}
get type() {
return 'SymbolNode';
}
get isSymbolNode() {
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) {
var name = this.name;
if (argNames[name] === true) {
// this is a FunctionAssignment argument
// (like an x when inside the expression of a function
// assignment `f(x) = ...`)
return function (scope, args, context) {
return getSafeProperty(args, name);
};
} else if (name in math) {
return function (scope, args, context) {
return scope.has(name) ? scope.get(name) : getSafeProperty(math, name);
};
} else {
var isUnit = isValuelessUnit(name);
return function (scope, args, context) {
return scope.has(name) ? scope.get(name) : isUnit ? new Unit(null, name) : SymbolNode.onUndefinedSymbol(name);
};
}
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
forEach(callback) {
// nothing to do, we don't have any children
}
/**
* Create a new SymbolNode with children produced by the given callback.
* Trivial since a SymbolNode has no children
* @param {function(child: Node, path: string, parent: Node) : Node} callback
* @returns {SymbolNode} Returns a clone of the node
*/
map(callback) {
return this.clone();
}
/**
* Throws an error 'Undefined symbol {name}'
* @param {string} name
*/
static onUndefinedSymbol(name) {
throw new Error('Undefined symbol ' + name);
}
/**
* Create a clone of this node, a shallow copy
* @return {SymbolNode}
*/
clone() {
return new SymbolNode(this.name);
}
/**
* Get string representation
* @param {Object} options
* @return {string} str
* @override
*/
_toString(options) {
return this.name;
}
/**
* Get HTML representation
* @param {Object} options
* @return {string} str
* @override
*/
_toHTML(options) {
var name = escape(this.name);
if (name === 'true' || name === 'false') {
return '<span class="math-symbol math-boolean">' + name + '</span>';
} else if (name === 'i') {
return '<span class="math-symbol math-imaginary-symbol">' + name + '</span>';
} else if (name === 'Infinity') {
return '<span class="math-symbol math-infinity-symbol">' + name + '</span>';
} else if (name === 'NaN') {
return '<span class="math-symbol math-nan-symbol">' + name + '</span>';
} else if (name === 'null') {
return '<span class="math-symbol math-null-symbol">' + name + '</span>';
} else if (name === 'undefined') {
return '<span class="math-symbol math-undefined-symbol">' + name + '</span>';
}
return '<span class="math-symbol">' + name + '</span>';
}
/**
* Get a JSON representation of the node
* @returns {Object}
*/
toJSON() {
return {
mathjs: 'SymbolNode',
name: this.name
};
}
/**
* Instantiate a SymbolNode from its JSON representation
* @param {Object} json An object structured like
* `{"mathjs": "SymbolNode", name: "x"}`,
* where mathjs is optional
* @returns {SymbolNode}
*/
static fromJSON(json) {
return new SymbolNode(json.name);
}
/**
* Get LaTeX representation
* @param {Object} options
* @return {string} str
* @override
*/
_toTex(options) {
var isUnit = false;
if (typeof math[this.name] === 'undefined' && isValuelessUnit(this.name)) {
isUnit = true;
}
var symbol = toSymbol(this.name, isUnit);
if (symbol[0] === '\\') {
// no space needed if the symbol starts with '\'
return symbol;
}
// the space prevents symbols from breaking stuff like '\cdot'
// if it's written right before the symbol
return ' ' + symbol;
}
}
return SymbolNode;
}, {
isClass: true,
isNode: true
});

View File

@@ -0,0 +1,40 @@
import { errorTransform } from '../../transform/utils/errorTransform.js';
import { getSafeProperty } from '../../../utils/customs.js';
export function accessFactory(_ref) {
var {
subset
} = _ref;
/**
* Retrieve part of an object:
*
* - Retrieve a property from an object
* - Retrieve a part of a string
* - Retrieve a matrix subset
*
* @param {Object | Array | Matrix | string} object
* @param {Index} index
* @return {Object | Array | Matrix | string} Returns the subset
*/
return function access(object, index) {
try {
if (Array.isArray(object)) {
return subset(object, index);
} else if (object && typeof object.subset === 'function') {
// Matrix
return object.subset(index);
} else if (typeof object === 'string') {
// TODO: move getStringSubset into a separate util file, use that
return subset(object, index);
} else if (typeof object === 'object') {
if (!index.isObjectProperty()) {
throw new TypeError('Cannot apply a numeric index as object property');
}
return getSafeProperty(object, index.getObjectProperty());
} else {
throw new TypeError('Cannot apply index: unsupported type of object');
}
} catch (err) {
throw errorTransform(err);
}
};
}

View File

@@ -0,0 +1,51 @@
import { errorTransform } from '../../transform/utils/errorTransform.js';
import { setSafeProperty } from '../../../utils/customs.js';
export function assignFactory(_ref) {
var {
subset,
matrix
} = _ref;
/**
* Replace part of an object:
*
* - Assign a property to an object
* - Replace a part of a string
* - Replace a matrix subset
*
* @param {Object | Array | Matrix | string} object
* @param {Index} index
* @param {*} value
* @return {Object | Array | Matrix | string} Returns the original object
* except in case of a string
*/
// TODO: change assign to return the value instead of the object
return function assign(object, index, value) {
try {
if (Array.isArray(object)) {
var result = matrix(object).subset(index, value).valueOf();
// shallow copy all (updated) items into the original array
result.forEach((item, index) => {
object[index] = item;
});
return object;
} else if (object && typeof object.subset === 'function') {
// Matrix
return object.subset(index, value);
} else if (typeof object === 'string') {
// TODO: move setStringSubset into a separate util file, use that
return subset(object, index, value);
} else if (typeof object === 'object') {
if (!index.isObjectProperty()) {
throw TypeError('Cannot apply a numeric index as object property');
}
setSafeProperty(object, index.getObjectProperty(), value);
return object;
} else {
throw new TypeError('Cannot apply index: unsupported type of object');
}
} catch (err) {
throw errorTransform(err);
}
};
}