SymbolNode.js 5.9 KB

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