simple-squiggle

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

RelationalNode.js (7553B)


      1 import { getPrecedence } from '../operators.js';
      2 import { escape } from '../../utils/string.js';
      3 import { getSafeProperty } from '../../utils/customs.js';
      4 import { latexOperators } from '../../utils/latex.js';
      5 import { factory } from '../../utils/factory.js';
      6 var name = 'RelationalNode';
      7 var dependencies = ['Node'];
      8 export var createRelationalNode = /* #__PURE__ */factory(name, dependencies, _ref => {
      9   var {
     10     Node
     11   } = _ref;
     12 
     13   /**
     14    * A node representing a chained conditional expression, such as 'x > y > z'
     15    *
     16    * @param {String[]} conditionals   An array of conditional operators used to compare the parameters
     17    * @param {Node[]} params   The parameters that will be compared
     18    *
     19    * @constructor RelationalNode
     20    * @extends {Node}
     21    */
     22   function RelationalNode(conditionals, params) {
     23     if (!(this instanceof RelationalNode)) {
     24       throw new SyntaxError('Constructor must be called with the new operator');
     25     }
     26 
     27     if (!Array.isArray(conditionals)) throw new TypeError('Parameter conditionals must be an array');
     28     if (!Array.isArray(params)) throw new TypeError('Parameter params must be an array');
     29     if (conditionals.length !== params.length - 1) throw new TypeError('Parameter params must contain exactly one more element than parameter conditionals');
     30     this.conditionals = conditionals;
     31     this.params = params;
     32   }
     33 
     34   RelationalNode.prototype = new Node();
     35   RelationalNode.prototype.type = 'RelationalNode';
     36   RelationalNode.prototype.isRelationalNode = true;
     37   /**
     38    * Compile a node into a JavaScript function.
     39    * This basically pre-calculates as much as possible and only leaves open
     40    * calculations which depend on a dynamic scope with variables.
     41    * @param {Object} math     Math.js namespace with functions and constants.
     42    * @param {Object} argNames An object with argument names as key and `true`
     43    *                          as value. Used in the SymbolNode to optimize
     44    *                          for arguments from user assigned functions
     45    *                          (see FunctionAssignmentNode) or special symbols
     46    *                          like `end` (see IndexNode).
     47    * @return {function} Returns a function which can be called like:
     48    *                        evalNode(scope: Object, args: Object, context: *)
     49    */
     50 
     51   RelationalNode.prototype._compile = function (math, argNames) {
     52     var self = this;
     53     var compiled = this.params.map(p => p._compile(math, argNames));
     54     return function evalRelationalNode(scope, args, context) {
     55       var evalLhs;
     56       var evalRhs = compiled[0](scope, args, context);
     57 
     58       for (var i = 0; i < self.conditionals.length; i++) {
     59         evalLhs = evalRhs;
     60         evalRhs = compiled[i + 1](scope, args, context);
     61         var condFn = getSafeProperty(math, self.conditionals[i]);
     62 
     63         if (!condFn(evalLhs, evalRhs)) {
     64           return false;
     65         }
     66       }
     67 
     68       return true;
     69     };
     70   };
     71   /**
     72    * Execute a callback for each of the child nodes of this node
     73    * @param {function(child: Node, path: string, parent: Node)} callback
     74    */
     75 
     76 
     77   RelationalNode.prototype.forEach = function (callback) {
     78     this.params.forEach((n, i) => callback(n, 'params[' + i + ']', this), this);
     79   };
     80   /**
     81    * Create a new RelationalNode having its childs be the results of calling
     82    * the provided callback function for each of the childs of the original node.
     83    * @param {function(child: Node, path: string, parent: Node): Node} callback
     84    * @returns {RelationalNode} Returns a transformed copy of the node
     85    */
     86 
     87 
     88   RelationalNode.prototype.map = function (callback) {
     89     return new RelationalNode(this.conditionals.slice(), this.params.map((n, i) => this._ifNode(callback(n, 'params[' + i + ']', this)), this));
     90   };
     91   /**
     92    * Create a clone of this node, a shallow copy
     93    * @return {RelationalNode}
     94    */
     95 
     96 
     97   RelationalNode.prototype.clone = function () {
     98     return new RelationalNode(this.conditionals, this.params);
     99   };
    100   /**
    101    * Get string representation.
    102    * @param {Object} options
    103    * @return {string} str
    104    */
    105 
    106 
    107   RelationalNode.prototype._toString = function (options) {
    108     var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
    109     var precedence = getPrecedence(this, parenthesis);
    110     var paramStrings = this.params.map(function (p, index) {
    111       var paramPrecedence = getPrecedence(p, parenthesis);
    112       return parenthesis === 'all' || paramPrecedence !== null && paramPrecedence <= precedence ? '(' + p.toString(options) + ')' : p.toString(options);
    113     });
    114     var operatorMap = {
    115       equal: '==',
    116       unequal: '!=',
    117       smaller: '<',
    118       larger: '>',
    119       smallerEq: '<=',
    120       largerEq: '>='
    121     };
    122     var ret = paramStrings[0];
    123 
    124     for (var i = 0; i < this.conditionals.length; i++) {
    125       ret += ' ' + operatorMap[this.conditionals[i]] + ' ' + paramStrings[i + 1];
    126     }
    127 
    128     return ret;
    129   };
    130   /**
    131    * Get a JSON representation of the node
    132    * @returns {Object}
    133    */
    134 
    135 
    136   RelationalNode.prototype.toJSON = function () {
    137     return {
    138       mathjs: 'RelationalNode',
    139       conditionals: this.conditionals,
    140       params: this.params
    141     };
    142   };
    143   /**
    144    * Instantiate a RelationalNode from its JSON representation
    145    * @param {Object} json  An object structured like
    146    *                       `{"mathjs": "RelationalNode", "condition": ..., "trueExpr": ..., "falseExpr": ...}`,
    147    *                       where mathjs is optional
    148    * @returns {RelationalNode}
    149    */
    150 
    151 
    152   RelationalNode.fromJSON = function (json) {
    153     return new RelationalNode(json.conditionals, json.params);
    154   };
    155   /**
    156    * Get HTML representation
    157    * @param {Object} options
    158    * @return {string} str
    159    */
    160 
    161 
    162   RelationalNode.prototype.toHTML = function (options) {
    163     var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
    164     var precedence = getPrecedence(this, parenthesis);
    165     var paramStrings = this.params.map(function (p, index) {
    166       var paramPrecedence = getPrecedence(p, parenthesis);
    167       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);
    168     });
    169     var operatorMap = {
    170       equal: '==',
    171       unequal: '!=',
    172       smaller: '<',
    173       larger: '>',
    174       smallerEq: '<=',
    175       largerEq: '>='
    176     };
    177     var ret = paramStrings[0];
    178 
    179     for (var i = 0; i < this.conditionals.length; i++) {
    180       ret += '<span class="math-operator math-binary-operator math-explicit-binary-operator">' + escape(operatorMap[this.conditionals[i]]) + '</span>' + paramStrings[i + 1];
    181     }
    182 
    183     return ret;
    184   };
    185   /**
    186    * Get LaTeX representation
    187    * @param {Object} options
    188    * @return {string} str
    189    */
    190 
    191 
    192   RelationalNode.prototype._toTex = function (options) {
    193     var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
    194     var precedence = getPrecedence(this, parenthesis);
    195     var paramStrings = this.params.map(function (p, index) {
    196       var paramPrecedence = getPrecedence(p, parenthesis);
    197       return parenthesis === 'all' || paramPrecedence !== null && paramPrecedence <= precedence ? '\\left(' + p.toTex(options) + '\right)' : p.toTex(options);
    198     });
    199     var ret = paramStrings[0];
    200 
    201     for (var i = 0; i < this.conditionals.length; i++) {
    202       ret += latexOperators[this.conditionals[i]] + paramStrings[i + 1];
    203     }
    204 
    205     return ret;
    206   };
    207 
    208   return RelationalNode;
    209 }, {
    210   isClass: true,
    211   isNode: true
    212 });