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 });