simple-squiggle

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

SymbolNode.js (6141B)


      1 import { escape } from '../../utils/string.js';
      2 import { getSafeProperty } from '../../utils/customs.js';
      3 import { factory } from '../../utils/factory.js';
      4 import { toSymbol } from '../../utils/latex.js';
      5 var name = 'SymbolNode';
      6 var dependencies = ['math', '?Unit', 'Node'];
      7 export var createSymbolNode = /* #__PURE__ */factory(name, dependencies, _ref => {
      8   var {
      9     math,
     10     Unit,
     11     Node
     12   } = _ref;
     13 
     14   /**
     15    * Check whether some name is a valueless unit like "inch".
     16    * @param {string} name
     17    * @return {boolean}
     18    */
     19   function isValuelessUnit(name) {
     20     return Unit ? Unit.isValuelessUnit(name) : false;
     21   }
     22   /**
     23    * @constructor SymbolNode
     24    * @extends {Node}
     25    * A symbol node can hold and resolve a symbol
     26    * @param {string} name
     27    * @extends {Node}
     28    */
     29 
     30 
     31   function SymbolNode(name) {
     32     if (!(this instanceof SymbolNode)) {
     33       throw new SyntaxError('Constructor must be called with the new operator');
     34     } // validate input
     35 
     36 
     37     if (typeof name !== 'string') throw new TypeError('String expected for parameter "name"');
     38     this.name = name;
     39   }
     40 
     41   SymbolNode.prototype = new Node();
     42   SymbolNode.prototype.type = 'SymbolNode';
     43   SymbolNode.prototype.isSymbolNode = true;
     44   /**
     45    * Compile a node into a JavaScript function.
     46    * This basically pre-calculates as much as possible and only leaves open
     47    * calculations which depend on a dynamic scope with variables.
     48    * @param {Object} math     Math.js namespace with functions and constants.
     49    * @param {Object} argNames An object with argument names as key and `true`
     50    *                          as value. Used in the SymbolNode to optimize
     51    *                          for arguments from user assigned functions
     52    *                          (see FunctionAssignmentNode) or special symbols
     53    *                          like `end` (see IndexNode).
     54    * @return {function} Returns a function which can be called like:
     55    *                        evalNode(scope: Object, args: Object, context: *)
     56    */
     57 
     58   SymbolNode.prototype._compile = function (math, argNames) {
     59     var name = this.name;
     60 
     61     if (argNames[name] === true) {
     62       // this is a FunctionAssignment argument
     63       // (like an x when inside the expression of a function assignment `f(x) = ...`)
     64       return function (scope, args, context) {
     65         return args[name];
     66       };
     67     } else if (name in math) {
     68       return function (scope, args, context) {
     69         return scope.has(name) ? scope.get(name) : getSafeProperty(math, name);
     70       };
     71     } else {
     72       var isUnit = isValuelessUnit(name);
     73       return function (scope, args, context) {
     74         return scope.has(name) ? scope.get(name) : isUnit ? new Unit(null, name) : SymbolNode.onUndefinedSymbol(name);
     75       };
     76     }
     77   };
     78   /**
     79    * Execute a callback for each of the child nodes of this node
     80    * @param {function(child: Node, path: string, parent: Node)} callback
     81    */
     82 
     83 
     84   SymbolNode.prototype.forEach = function (callback) {// nothing to do, we don't have childs
     85   };
     86   /**
     87    * Create a new SymbolNode having it's childs be the results of calling
     88    * the provided callback function for each of the childs of the original node.
     89    * @param {function(child: Node, path: string, parent: Node) : Node} callback
     90    * @returns {SymbolNode} Returns a clone of the node
     91    */
     92 
     93 
     94   SymbolNode.prototype.map = function (callback) {
     95     return this.clone();
     96   };
     97   /**
     98    * Throws an error 'Undefined symbol {name}'
     99    * @param {string} name
    100    */
    101 
    102 
    103   SymbolNode.onUndefinedSymbol = function (name) {
    104     throw new Error('Undefined symbol ' + name);
    105   };
    106   /**
    107    * Create a clone of this node, a shallow copy
    108    * @return {SymbolNode}
    109    */
    110 
    111 
    112   SymbolNode.prototype.clone = function () {
    113     return new SymbolNode(this.name);
    114   };
    115   /**
    116    * Get string representation
    117    * @param {Object} options
    118    * @return {string} str
    119    * @override
    120    */
    121 
    122 
    123   SymbolNode.prototype._toString = function (options) {
    124     return this.name;
    125   };
    126   /**
    127    * Get HTML representation
    128    * @param {Object} options
    129    * @return {string} str
    130    * @override
    131    */
    132 
    133 
    134   SymbolNode.prototype.toHTML = function (options) {
    135     var name = escape(this.name);
    136 
    137     if (name === 'true' || name === 'false') {
    138       return '<span class="math-symbol math-boolean">' + name + '</span>';
    139     } else if (name === 'i') {
    140       return '<span class="math-symbol math-imaginary-symbol">' + name + '</span>';
    141     } else if (name === 'Infinity') {
    142       return '<span class="math-symbol math-infinity-symbol">' + name + '</span>';
    143     } else if (name === 'NaN') {
    144       return '<span class="math-symbol math-nan-symbol">' + name + '</span>';
    145     } else if (name === 'null') {
    146       return '<span class="math-symbol math-null-symbol">' + name + '</span>';
    147     } else if (name === 'undefined') {
    148       return '<span class="math-symbol math-undefined-symbol">' + name + '</span>';
    149     }
    150 
    151     return '<span class="math-symbol">' + name + '</span>';
    152   };
    153   /**
    154    * Get a JSON representation of the node
    155    * @returns {Object}
    156    */
    157 
    158 
    159   SymbolNode.prototype.toJSON = function () {
    160     return {
    161       mathjs: 'SymbolNode',
    162       name: this.name
    163     };
    164   };
    165   /**
    166    * Instantiate a SymbolNode from its JSON representation
    167    * @param {Object} json  An object structured like
    168    *                       `{"mathjs": "SymbolNode", name: "x"}`,
    169    *                       where mathjs is optional
    170    * @returns {SymbolNode}
    171    */
    172 
    173 
    174   SymbolNode.fromJSON = function (json) {
    175     return new SymbolNode(json.name);
    176   };
    177   /**
    178    * Get LaTeX representation
    179    * @param {Object} options
    180    * @return {string} str
    181    * @override
    182    */
    183 
    184 
    185   SymbolNode.prototype._toTex = function (options) {
    186     var isUnit = false;
    187 
    188     if (typeof math[this.name] === 'undefined' && isValuelessUnit(this.name)) {
    189       isUnit = true;
    190     }
    191 
    192     var symbol = toSymbol(this.name, isUnit);
    193 
    194     if (symbol[0] === '\\') {
    195       // no space needed if the symbol starts with '\'
    196       return symbol;
    197     } // the space prevents symbols from breaking stuff like '\cdot' if it's written right before the symbol
    198 
    199 
    200     return ' ' + symbol;
    201   };
    202 
    203   return SymbolNode;
    204 }, {
    205   isClass: true,
    206   isNode: true
    207 });