simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

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;