FunctionAssignmentNode.js (8620B)
1 "use strict"; 2 3 Object.defineProperty(exports, "__esModule", { 4 value: true 5 }); 6 exports.createFunctionAssignmentNode = void 0; 7 8 var _is = require("../../utils/is.js"); 9 10 var _keywords = require("../keywords.js"); 11 12 var _string = require("../../utils/string.js"); 13 14 var _array = require("../../utils/array.js"); 15 16 var _latex = require("../../utils/latex.js"); 17 18 var _operators = require("../operators.js"); 19 20 var _factory = require("../../utils/factory.js"); 21 22 var name = 'FunctionAssignmentNode'; 23 var dependencies = ['typed', 'Node']; 24 var createFunctionAssignmentNode = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) { 25 var typed = _ref.typed, 26 Node = _ref.Node; 27 28 /** 29 * @constructor FunctionAssignmentNode 30 * @extends {Node} 31 * Function assignment 32 * 33 * @param {string} name Function name 34 * @param {string[] | Array.<{name: string, type: string}>} params 35 * Array with function parameter names, or an 36 * array with objects containing the name 37 * and type of the parameter 38 * @param {Node} expr The function expression 39 */ 40 function FunctionAssignmentNode(name, params, expr) { 41 if (!(this instanceof FunctionAssignmentNode)) { 42 throw new SyntaxError('Constructor must be called with the new operator'); 43 } // validate input 44 45 46 if (typeof name !== 'string') throw new TypeError('String expected for parameter "name"'); 47 if (!Array.isArray(params)) throw new TypeError('Array containing strings or objects expected for parameter "params"'); 48 if (!(0, _is.isNode)(expr)) throw new TypeError('Node expected for parameter "expr"'); 49 if (_keywords.keywords.has(name)) throw new Error('Illegal function name, "' + name + '" is a reserved keyword'); 50 this.name = name; 51 this.params = params.map(function (param) { 52 return param && param.name || param; 53 }); 54 this.types = params.map(function (param) { 55 return param && param.type || 'any'; 56 }); 57 this.expr = expr; 58 } 59 60 FunctionAssignmentNode.prototype = new Node(); 61 FunctionAssignmentNode.prototype.type = 'FunctionAssignmentNode'; 62 FunctionAssignmentNode.prototype.isFunctionAssignmentNode = true; 63 /** 64 * Compile a node into a JavaScript function. 65 * This basically pre-calculates as much as possible and only leaves open 66 * calculations which depend on a dynamic scope with variables. 67 * @param {Object} math Math.js namespace with functions and constants. 68 * @param {Object} argNames An object with argument names as key and `true` 69 * as value. Used in the SymbolNode to optimize 70 * for arguments from user assigned functions 71 * (see FunctionAssignmentNode) or special symbols 72 * like `end` (see IndexNode). 73 * @return {function} Returns a function which can be called like: 74 * evalNode(scope: Object, args: Object, context: *) 75 */ 76 77 FunctionAssignmentNode.prototype._compile = function (math, argNames) { 78 var childArgNames = Object.create(argNames); 79 (0, _array.forEach)(this.params, function (param) { 80 childArgNames[param] = true; 81 }); // compile the function expression with the child args 82 83 var evalExpr = this.expr._compile(math, childArgNames); 84 85 var name = this.name; 86 var params = this.params; 87 var signature = (0, _array.join)(this.types, ','); 88 var syntax = name + '(' + (0, _array.join)(this.params, ', ') + ')'; 89 return function evalFunctionAssignmentNode(scope, args, context) { 90 var signatures = {}; 91 92 signatures[signature] = function () { 93 var childArgs = Object.create(args); 94 95 for (var i = 0; i < params.length; i++) { 96 childArgs[params[i]] = arguments[i]; 97 } 98 99 return evalExpr(scope, childArgs, context); 100 }; 101 102 var fn = typed(name, signatures); 103 fn.syntax = syntax; 104 scope.set(name, fn); 105 return fn; 106 }; 107 }; 108 /** 109 * Execute a callback for each of the child nodes of this node 110 * @param {function(child: Node, path: string, parent: Node)} callback 111 */ 112 113 114 FunctionAssignmentNode.prototype.forEach = function (callback) { 115 callback(this.expr, 'expr', this); 116 }; 117 /** 118 * Create a new FunctionAssignmentNode having it's childs be the results of calling 119 * the provided callback function for each of the childs of the original node. 120 * @param {function(child: Node, path: string, parent: Node): Node} callback 121 * @returns {FunctionAssignmentNode} Returns a transformed copy of the node 122 */ 123 124 125 FunctionAssignmentNode.prototype.map = function (callback) { 126 var expr = this._ifNode(callback(this.expr, 'expr', this)); 127 128 return new FunctionAssignmentNode(this.name, this.params.slice(0), expr); 129 }; 130 /** 131 * Create a clone of this node, a shallow copy 132 * @return {FunctionAssignmentNode} 133 */ 134 135 136 FunctionAssignmentNode.prototype.clone = function () { 137 return new FunctionAssignmentNode(this.name, this.params.slice(0), this.expr); 138 }; 139 /** 140 * Is parenthesis needed? 141 * @param {Node} node 142 * @param {Object} parenthesis 143 * @private 144 */ 145 146 147 function needParenthesis(node, parenthesis) { 148 var precedence = (0, _operators.getPrecedence)(node, parenthesis); 149 var exprPrecedence = (0, _operators.getPrecedence)(node.expr, parenthesis); 150 return parenthesis === 'all' || exprPrecedence !== null && exprPrecedence <= precedence; 151 } 152 /** 153 * get string representation 154 * @param {Object} options 155 * @return {string} str 156 */ 157 158 159 FunctionAssignmentNode.prototype._toString = function (options) { 160 var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; 161 var expr = this.expr.toString(options); 162 163 if (needParenthesis(this, parenthesis)) { 164 expr = '(' + expr + ')'; 165 } 166 167 return this.name + '(' + this.params.join(', ') + ') = ' + expr; 168 }; 169 /** 170 * Get a JSON representation of the node 171 * @returns {Object} 172 */ 173 174 175 FunctionAssignmentNode.prototype.toJSON = function () { 176 var types = this.types; 177 return { 178 mathjs: 'FunctionAssignmentNode', 179 name: this.name, 180 params: this.params.map(function (param, index) { 181 return { 182 name: param, 183 type: types[index] 184 }; 185 }), 186 expr: this.expr 187 }; 188 }; 189 /** 190 * Instantiate an FunctionAssignmentNode from its JSON representation 191 * @param {Object} json An object structured like 192 * `{"mathjs": "FunctionAssignmentNode", name: ..., params: ..., expr: ...}`, 193 * where mathjs is optional 194 * @returns {FunctionAssignmentNode} 195 */ 196 197 198 FunctionAssignmentNode.fromJSON = function (json) { 199 return new FunctionAssignmentNode(json.name, json.params, json.expr); 200 }; 201 /** 202 * get HTML representation 203 * @param {Object} options 204 * @return {string} str 205 */ 206 207 208 FunctionAssignmentNode.prototype.toHTML = function (options) { 209 var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; 210 var params = []; 211 212 for (var i = 0; i < this.params.length; i++) { 213 params.push('<span class="math-symbol math-parameter">' + (0, _string.escape)(this.params[i]) + '</span>'); 214 } 215 216 var expr = this.expr.toHTML(options); 217 218 if (needParenthesis(this, parenthesis)) { 219 expr = '<span class="math-parenthesis math-round-parenthesis">(</span>' + expr + '<span class="math-parenthesis math-round-parenthesis">)</span>'; 220 } 221 222 return '<span class="math-function">' + (0, _string.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; 223 }; 224 /** 225 * get LaTeX representation 226 * @param {Object} options 227 * @return {string} str 228 */ 229 230 231 FunctionAssignmentNode.prototype._toTex = function (options) { 232 var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; 233 var expr = this.expr.toTex(options); 234 235 if (needParenthesis(this, parenthesis)) { 236 expr = "\\left(".concat(expr, "\\right)"); 237 } 238 239 return '\\mathrm{' + this.name + '}\\left(' + this.params.map(_latex.toSymbol).join(',') + '\\right):=' + expr; 240 }; 241 242 return FunctionAssignmentNode; 243 }, { 244 isClass: true, 245 isNode: true 246 }); 247 exports.createFunctionAssignmentNode = createFunctionAssignmentNode;