Node.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.createNode = void 0;
  7. var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
  8. var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
  9. var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
  10. var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
  11. var _is = require("../../utils/is.js");
  12. var _keywords = require("../keywords.js");
  13. var _object = require("../../utils/object.js");
  14. var _factory = require("../../utils/factory.js");
  15. var _map = require("../../utils/map.js");
  16. var name = 'Node';
  17. var dependencies = ['mathWithTransform'];
  18. var createNode = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) {
  19. var mathWithTransform = _ref.mathWithTransform;
  20. /**
  21. * Validate the symbol names of a scope.
  22. * Throws an error when the scope contains an illegal symbol.
  23. * @param {Object} scope
  24. */
  25. function _validateScope(scope) {
  26. for (var _i = 0, _arr = (0, _toConsumableArray2["default"])(_keywords.keywords); _i < _arr.length; _i++) {
  27. var symbol = _arr[_i];
  28. if (scope.has(symbol)) {
  29. throw new Error('Scope contains an illegal symbol, "' + symbol + '" is a reserved keyword');
  30. }
  31. }
  32. }
  33. var Node = /*#__PURE__*/function () {
  34. function Node() {
  35. (0, _classCallCheck2["default"])(this, Node);
  36. }
  37. (0, _createClass2["default"])(Node, [{
  38. key: "type",
  39. get: function get() {
  40. return 'Node';
  41. }
  42. }, {
  43. key: "isNode",
  44. get: function get() {
  45. return true;
  46. }
  47. /**
  48. * Evaluate the node
  49. * @param {Object} [scope] Scope to read/write variables
  50. * @return {*} Returns the result
  51. */
  52. }, {
  53. key: "evaluate",
  54. value: function evaluate(scope) {
  55. return this.compile().evaluate(scope);
  56. }
  57. /**
  58. * Compile the node into an optimized, evauatable JavaScript function
  59. * @return {{evaluate: function([Object])}} object
  60. * Returns an object with a function 'evaluate',
  61. * which can be invoked as expr.evaluate([scope: Object]),
  62. * where scope is an optional object with
  63. * variables.
  64. */
  65. }, {
  66. key: "compile",
  67. value: function compile() {
  68. var expr = this._compile(mathWithTransform, {});
  69. var args = {};
  70. var context = null;
  71. function evaluate(scope) {
  72. var s = (0, _map.createMap)(scope);
  73. _validateScope(s);
  74. return expr(s, args, context);
  75. }
  76. return {
  77. evaluate: evaluate
  78. };
  79. }
  80. /**
  81. * Compile a node into a JavaScript function.
  82. * This basically pre-calculates as much as possible and only leaves open
  83. * calculations which depend on a dynamic scope with variables.
  84. * @param {Object} math Math.js namespace with functions and constants.
  85. * @param {Object} argNames An object with argument names as key and `true`
  86. * as value. Used in the SymbolNode to optimize
  87. * for arguments from user assigned functions
  88. * (see FunctionAssignmentNode) or special symbols
  89. * like `end` (see IndexNode).
  90. * @return {function} Returns a function which can be called like:
  91. * evalNode(scope: Object, args: Object, context: *)
  92. */
  93. }, {
  94. key: "_compile",
  95. value: function _compile(math, argNames) {
  96. throw new Error('Method _compile must be implemented by type ' + this.type);
  97. }
  98. /**
  99. * Execute a callback for each of the child nodes of this node
  100. * @param {function(child: Node, path: string, parent: Node)} callback
  101. */
  102. }, {
  103. key: "forEach",
  104. value: function forEach(callback) {
  105. // must be implemented by each of the Node implementations
  106. throw new Error('Cannot run forEach on a Node interface');
  107. }
  108. /**
  109. * Create a new Node whose children are the results of calling the
  110. * provided callback function for each child of the original node.
  111. * @param {function(child: Node, path: string, parent: Node): Node} callback
  112. * @returns {OperatorNode} Returns a transformed copy of the node
  113. */
  114. }, {
  115. key: "map",
  116. value: function map(callback) {
  117. // must be implemented by each of the Node implementations
  118. throw new Error('Cannot run map on a Node interface');
  119. }
  120. /**
  121. * Validate whether an object is a Node, for use with map
  122. * @param {Node} node
  123. * @returns {Node} Returns the input if it's a node, else throws an Error
  124. * @protected
  125. */
  126. }, {
  127. key: "_ifNode",
  128. value: function _ifNode(node) {
  129. if (!(0, _is.isNode)(node)) {
  130. throw new TypeError('Callback function must return a Node');
  131. }
  132. return node;
  133. }
  134. /**
  135. * Recursively traverse all nodes in a node tree. Executes given callback for
  136. * this node and each of its child nodes.
  137. * @param {function(node: Node, path: string, parent: Node)} callback
  138. * A callback called for every node in the node tree.
  139. */
  140. }, {
  141. key: "traverse",
  142. value: function traverse(callback) {
  143. // execute callback for itself
  144. // eslint-disable-next-line
  145. callback(this, null, null);
  146. // recursively traverse over all children of a node
  147. function _traverse(node, callback) {
  148. node.forEach(function (child, path, parent) {
  149. callback(child, path, parent);
  150. _traverse(child, callback);
  151. });
  152. }
  153. _traverse(this, callback);
  154. }
  155. /**
  156. * Recursively transform a node tree via a transform function.
  157. *
  158. * For example, to replace all nodes of type SymbolNode having name 'x' with
  159. * a ConstantNode with value 2:
  160. *
  161. * const res = Node.transform(function (node, path, parent) {
  162. * if (node && node.isSymbolNode) && (node.name === 'x')) {
  163. * return new ConstantNode(2)
  164. * }
  165. * else {
  166. * return node
  167. * }
  168. * })
  169. *
  170. * @param {function(node: Node, path: string, parent: Node) : Node} callback
  171. * A mapping function accepting a node, and returning
  172. * a replacement for the node or the original node. The "signature"
  173. * of the callback must be:
  174. * callback(node: Node, index: string, parent: Node) : Node
  175. * @return {Node} Returns the original node or its replacement
  176. */
  177. }, {
  178. key: "transform",
  179. value: function transform(callback) {
  180. function _transform(child, path, parent) {
  181. var replacement = callback(child, path, parent);
  182. if (replacement !== child) {
  183. // stop iterating when the node is replaced
  184. return replacement;
  185. }
  186. return child.map(_transform);
  187. }
  188. return _transform(this, null, null);
  189. }
  190. /**
  191. * Find any node in the node tree matching given filter function. For
  192. * example, to find all nodes of type SymbolNode having name 'x':
  193. *
  194. * const results = Node.filter(function (node) {
  195. * return (node && node.isSymbolNode) && (node.name === 'x')
  196. * })
  197. *
  198. * @param {function(node: Node, path: string, parent: Node) : Node} callback
  199. * A test function returning true when a node matches, and false
  200. * otherwise. Function signature:
  201. * callback(node: Node, index: string, parent: Node) : boolean
  202. * @return {Node[]} nodes
  203. * An array with nodes matching given filter criteria
  204. */
  205. }, {
  206. key: "filter",
  207. value: function filter(callback) {
  208. var nodes = [];
  209. this.traverse(function (node, path, parent) {
  210. if (callback(node, path, parent)) {
  211. nodes.push(node);
  212. }
  213. });
  214. return nodes;
  215. }
  216. /**
  217. * Create a shallow clone of this node
  218. * @return {Node}
  219. */
  220. }, {
  221. key: "clone",
  222. value: function clone() {
  223. // must be implemented by each of the Node implementations
  224. throw new Error('Cannot clone a Node interface');
  225. }
  226. /**
  227. * Create a deep clone of this node
  228. * @return {Node}
  229. */
  230. }, {
  231. key: "cloneDeep",
  232. value: function cloneDeep() {
  233. return this.map(function (node) {
  234. return node.cloneDeep();
  235. });
  236. }
  237. /**
  238. * Deep compare this node with another node.
  239. * @param {Node} other
  240. * @return {boolean} Returns true when both nodes are of the same type and
  241. * contain the same values (as do their childs)
  242. */
  243. }, {
  244. key: "equals",
  245. value: function equals(other) {
  246. return other ? this.type === other.type && (0, _object.deepStrictEqual)(this, other) : false;
  247. }
  248. /**
  249. * Get string representation. (wrapper function)
  250. *
  251. * This function can get an object of the following form:
  252. * {
  253. * handler: //This can be a callback function of the form
  254. * // "function callback(node, options)"or
  255. * // a map that maps function names (used in FunctionNodes)
  256. * // to callbacks
  257. * parenthesis: "keep" //the parenthesis option (This is optional)
  258. * }
  259. *
  260. * @param {Object} [options]
  261. * @return {string}
  262. */
  263. }, {
  264. key: "toString",
  265. value: function toString(options) {
  266. var customString = this._getCustomString(options);
  267. if (typeof customString !== 'undefined') {
  268. return customString;
  269. }
  270. return this._toString(options);
  271. }
  272. /**
  273. * Get a JSON representation of the node
  274. * Both .toJSON() and the static .fromJSON(json) should be implemented by all
  275. * implementations of Node
  276. * @returns {Object}
  277. */
  278. }, {
  279. key: "toJSON",
  280. value: function toJSON() {
  281. throw new Error('Cannot serialize object: toJSON not implemented by ' + this.type);
  282. }
  283. /**
  284. * Get HTML representation. (wrapper function)
  285. *
  286. * This function can get an object of the following form:
  287. * {
  288. * handler: //This can be a callback function of the form
  289. * // "function callback(node, options)" or
  290. * // a map that maps function names (used in FunctionNodes)
  291. * // to callbacks
  292. * parenthesis: "keep" //the parenthesis option (This is optional)
  293. * }
  294. *
  295. * @param {Object} [options]
  296. * @return {string}
  297. */
  298. }, {
  299. key: "toHTML",
  300. value: function toHTML(options) {
  301. var customString = this._getCustomString(options);
  302. if (typeof customString !== 'undefined') {
  303. return customString;
  304. }
  305. return this.toHTML(options);
  306. }
  307. /**
  308. * Internal function to generate the string output.
  309. * This has to be implemented by every Node
  310. *
  311. * @throws {Error}
  312. */
  313. }, {
  314. key: "_toString",
  315. value: function _toString() {
  316. // must be implemented by each of the Node implementations
  317. throw new Error('_toString not implemented for ' + this.type);
  318. }
  319. /**
  320. * Get LaTeX representation. (wrapper function)
  321. *
  322. * This function can get an object of the following form:
  323. * {
  324. * handler: //This can be a callback function of the form
  325. * // "function callback(node, options)"or
  326. * // a map that maps function names (used in FunctionNodes)
  327. * // to callbacks
  328. * parenthesis: "keep" //the parenthesis option (This is optional)
  329. * }
  330. *
  331. * @param {Object} [options]
  332. * @return {string}
  333. */
  334. }, {
  335. key: "toTex",
  336. value: function toTex(options) {
  337. var customString = this._getCustomString(options);
  338. if (typeof customString !== 'undefined') {
  339. return customString;
  340. }
  341. return this._toTex(options);
  342. }
  343. /**
  344. * Internal function to generate the LaTeX output.
  345. * This has to be implemented by every Node
  346. *
  347. * @param {Object} [options]
  348. * @throws {Error}
  349. */
  350. }, {
  351. key: "_toTex",
  352. value: function _toTex(options) {
  353. // must be implemented by each of the Node implementations
  354. throw new Error('_toTex not implemented for ' + this.type);
  355. }
  356. /**
  357. * Helper used by `to...` functions.
  358. */
  359. }, {
  360. key: "_getCustomString",
  361. value: function _getCustomString(options) {
  362. if (options && (0, _typeof2["default"])(options) === 'object') {
  363. switch ((0, _typeof2["default"])(options.handler)) {
  364. case 'object':
  365. case 'undefined':
  366. return;
  367. case 'function':
  368. return options.handler(this, options);
  369. default:
  370. throw new TypeError('Object or function expected as callback');
  371. }
  372. }
  373. }
  374. /**
  375. * Get identifier.
  376. * @return {string}
  377. */
  378. }, {
  379. key: "getIdentifier",
  380. value: function getIdentifier() {
  381. return this.type;
  382. }
  383. /**
  384. * Get the content of the current Node.
  385. * @return {Node} node
  386. **/
  387. }, {
  388. key: "getContent",
  389. value: function getContent() {
  390. return this;
  391. }
  392. }]);
  393. return Node;
  394. }();
  395. return Node;
  396. }, {
  397. isClass: true,
  398. isNode: true
  399. });
  400. exports.createNode = createNode;