OperatorNode.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.createOperatorNode = void 0;
  7. var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
  8. var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
  9. var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
  10. var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
  11. var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
  12. var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
  13. var _is = require("../../utils/is.js");
  14. var _array = require("../../utils/array.js");
  15. var _string = require("../../utils/string.js");
  16. var _customs = require("../../utils/customs.js");
  17. var _operators = require("../operators.js");
  18. var _latex = require("../../utils/latex.js");
  19. var _factory = require("../../utils/factory.js");
  20. function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; }
  21. function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
  22. var name = 'OperatorNode';
  23. var dependencies = ['Node'];
  24. var createOperatorNode = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) {
  25. var Node = _ref.Node;
  26. /**
  27. * Returns true if the expression starts with a constant, under
  28. * the current parenthesization:
  29. * @param {Node} expression
  30. * @param {string} parenthesis
  31. * @return {boolean}
  32. */
  33. function startsWithConstant(expr, parenthesis) {
  34. var curNode = expr;
  35. if (parenthesis === 'auto') {
  36. while ((0, _is.isParenthesisNode)(curNode)) curNode = curNode.content;
  37. }
  38. if ((0, _is.isConstantNode)(curNode)) return true;
  39. if ((0, _is.isOperatorNode)(curNode)) {
  40. return startsWithConstant(curNode.args[0], parenthesis);
  41. }
  42. return false;
  43. }
  44. /**
  45. * Calculate which parentheses are necessary. Gets an OperatorNode
  46. * (which is the root of the tree) and an Array of Nodes
  47. * (this.args) and returns an array where 'true' means that an argument
  48. * has to be enclosed in parentheses whereas 'false' means the opposite.
  49. *
  50. * @param {OperatorNode} root
  51. * @param {string} parenthesis
  52. * @param {Node[]} args
  53. * @param {boolean} latex
  54. * @return {boolean[]}
  55. * @private
  56. */
  57. function calculateNecessaryParentheses(root, parenthesis, implicit, args, latex) {
  58. // precedence of the root OperatorNode
  59. var precedence = (0, _operators.getPrecedence)(root, parenthesis, implicit);
  60. var associativity = (0, _operators.getAssociativity)(root, parenthesis);
  61. if (parenthesis === 'all' || args.length > 2 && root.getIdentifier() !== 'OperatorNode:add' && root.getIdentifier() !== 'OperatorNode:multiply') {
  62. return args.map(function (arg) {
  63. switch (arg.getContent().type) {
  64. // Nodes that don't need extra parentheses
  65. case 'ArrayNode':
  66. case 'ConstantNode':
  67. case 'SymbolNode':
  68. case 'ParenthesisNode':
  69. return false;
  70. default:
  71. return true;
  72. }
  73. });
  74. }
  75. var result;
  76. switch (args.length) {
  77. case 0:
  78. result = [];
  79. break;
  80. case 1:
  81. // unary operators
  82. {
  83. // precedence of the operand
  84. var operandPrecedence = (0, _operators.getPrecedence)(args[0], parenthesis, implicit, root);
  85. // handle special cases for LaTeX, where some of the parentheses aren't needed
  86. if (latex && operandPrecedence !== null) {
  87. var operandIdentifier;
  88. var rootIdentifier;
  89. if (parenthesis === 'keep') {
  90. operandIdentifier = args[0].getIdentifier();
  91. rootIdentifier = root.getIdentifier();
  92. } else {
  93. // Ignore Parenthesis Nodes when not in 'keep' mode
  94. operandIdentifier = args[0].getContent().getIdentifier();
  95. rootIdentifier = root.getContent().getIdentifier();
  96. }
  97. if (_operators.properties[precedence][rootIdentifier].latexLeftParens === false) {
  98. result = [false];
  99. break;
  100. }
  101. if (_operators.properties[operandPrecedence][operandIdentifier].latexParens === false) {
  102. result = [false];
  103. break;
  104. }
  105. }
  106. if (operandPrecedence === null) {
  107. // if the operand has no defined precedence, no parens are needed
  108. result = [false];
  109. break;
  110. }
  111. if (operandPrecedence <= precedence) {
  112. // if the operands precedence is lower, parens are needed
  113. result = [true];
  114. break;
  115. }
  116. // otherwise, no parens needed
  117. result = [false];
  118. }
  119. break;
  120. case 2:
  121. // binary operators
  122. {
  123. var lhsParens; // left hand side needs parenthesis?
  124. // precedence of the left hand side
  125. var lhsPrecedence = (0, _operators.getPrecedence)(args[0], parenthesis, implicit, root);
  126. // is the root node associative with the left hand side
  127. var assocWithLhs = (0, _operators.isAssociativeWith)(root, args[0], parenthesis);
  128. if (lhsPrecedence === null) {
  129. // if the left hand side has no defined precedence, no parens are needed
  130. // FunctionNode for example
  131. lhsParens = false;
  132. } else if (lhsPrecedence === precedence && associativity === 'right' && !assocWithLhs) {
  133. // In case of equal precedence, if the root node is left associative
  134. // parens are **never** necessary for the left hand side.
  135. // If it is right associative however, parens are necessary
  136. // if the root node isn't associative with the left hand side
  137. lhsParens = true;
  138. } else if (lhsPrecedence < precedence) {
  139. lhsParens = true;
  140. } else {
  141. lhsParens = false;
  142. }
  143. var rhsParens; // right hand side needs parenthesis?
  144. // precedence of the right hand side
  145. var rhsPrecedence = (0, _operators.getPrecedence)(args[1], parenthesis, implicit, root);
  146. // is the root node associative with the right hand side?
  147. var assocWithRhs = (0, _operators.isAssociativeWith)(root, args[1], parenthesis);
  148. if (rhsPrecedence === null) {
  149. // if the right hand side has no defined precedence, no parens are needed
  150. // FunctionNode for example
  151. rhsParens = false;
  152. } else if (rhsPrecedence === precedence && associativity === 'left' && !assocWithRhs) {
  153. // In case of equal precedence, if the root node is right associative
  154. // parens are **never** necessary for the right hand side.
  155. // If it is left associative however, parens are necessary
  156. // if the root node isn't associative with the right hand side
  157. rhsParens = true;
  158. } else if (rhsPrecedence < precedence) {
  159. rhsParens = true;
  160. } else {
  161. rhsParens = false;
  162. }
  163. // handle special cases for LaTeX, where some of the parentheses aren't needed
  164. if (latex) {
  165. var _rootIdentifier;
  166. var lhsIdentifier;
  167. var rhsIdentifier;
  168. if (parenthesis === 'keep') {
  169. _rootIdentifier = root.getIdentifier();
  170. lhsIdentifier = root.args[0].getIdentifier();
  171. rhsIdentifier = root.args[1].getIdentifier();
  172. } else {
  173. // Ignore ParenthesisNodes when not in 'keep' mode
  174. _rootIdentifier = root.getContent().getIdentifier();
  175. lhsIdentifier = root.args[0].getContent().getIdentifier();
  176. rhsIdentifier = root.args[1].getContent().getIdentifier();
  177. }
  178. if (lhsPrecedence !== null) {
  179. if (_operators.properties[precedence][_rootIdentifier].latexLeftParens === false) {
  180. lhsParens = false;
  181. }
  182. if (_operators.properties[lhsPrecedence][lhsIdentifier].latexParens === false) {
  183. lhsParens = false;
  184. }
  185. }
  186. if (rhsPrecedence !== null) {
  187. if (_operators.properties[precedence][_rootIdentifier].latexRightParens === false) {
  188. rhsParens = false;
  189. }
  190. if (_operators.properties[rhsPrecedence][rhsIdentifier].latexParens === false) {
  191. rhsParens = false;
  192. }
  193. }
  194. }
  195. result = [lhsParens, rhsParens];
  196. }
  197. break;
  198. default:
  199. if (root.getIdentifier() === 'OperatorNode:add' || root.getIdentifier() === 'OperatorNode:multiply') {
  200. result = args.map(function (arg) {
  201. var argPrecedence = (0, _operators.getPrecedence)(arg, parenthesis, implicit, root);
  202. var assocWithArg = (0, _operators.isAssociativeWith)(root, arg, parenthesis);
  203. var argAssociativity = (0, _operators.getAssociativity)(arg, parenthesis);
  204. if (argPrecedence === null) {
  205. // if the argument has no defined precedence, no parens are needed
  206. return false;
  207. } else if (precedence === argPrecedence && associativity === argAssociativity && !assocWithArg) {
  208. return true;
  209. } else if (argPrecedence < precedence) {
  210. return true;
  211. }
  212. return false;
  213. });
  214. }
  215. break;
  216. }
  217. // Handles an edge case of parentheses with implicit multiplication
  218. // of ConstantNode.
  219. // In that case, parenthesize ConstantNodes that follow an unparenthesized
  220. // expression, even though they normally wouldn't be printed.
  221. if (args.length >= 2 && root.getIdentifier() === 'OperatorNode:multiply' && root.implicit && parenthesis !== 'all' && implicit === 'hide') {
  222. for (var i = 1; i < result.length; ++i) {
  223. if (startsWithConstant(args[i], parenthesis) && !result[i - 1] && (parenthesis !== 'keep' || !(0, _is.isParenthesisNode)(args[i - 1]))) {
  224. result[i] = true;
  225. }
  226. }
  227. }
  228. return result;
  229. }
  230. var OperatorNode = /*#__PURE__*/function (_Node) {
  231. (0, _inherits2["default"])(OperatorNode, _Node);
  232. var _super = _createSuper(OperatorNode);
  233. /**
  234. * @constructor OperatorNode
  235. * @extends {Node}
  236. * An operator with two arguments, like 2+3
  237. *
  238. * @param {string} op Operator name, for example '+'
  239. * @param {string} fn Function name, for example 'add'
  240. * @param {Node[]} args Operator arguments
  241. * @param {boolean} [implicit] Is this an implicit multiplication?
  242. * @param {boolean} [isPercentage] Is this an percentage Operation?
  243. */
  244. function OperatorNode(op, fn, args, implicit, isPercentage) {
  245. var _this;
  246. (0, _classCallCheck2["default"])(this, OperatorNode);
  247. _this = _super.call(this);
  248. // validate input
  249. if (typeof op !== 'string') {
  250. throw new TypeError('string expected for parameter "op"');
  251. }
  252. if (typeof fn !== 'string') {
  253. throw new TypeError('string expected for parameter "fn"');
  254. }
  255. if (!Array.isArray(args) || !args.every(_is.isNode)) {
  256. throw new TypeError('Array containing Nodes expected for parameter "args"');
  257. }
  258. _this.implicit = implicit === true;
  259. _this.isPercentage = isPercentage === true;
  260. _this.op = op;
  261. _this.fn = fn;
  262. _this.args = args || [];
  263. return _this;
  264. }
  265. (0, _createClass2["default"])(OperatorNode, [{
  266. key: "type",
  267. get: function get() {
  268. return name;
  269. }
  270. }, {
  271. key: "isOperatorNode",
  272. get: function get() {
  273. return true;
  274. }
  275. /**
  276. * Compile a node into a JavaScript function.
  277. * This basically pre-calculates as much as possible and only leaves open
  278. * calculations which depend on a dynamic scope with variables.
  279. * @param {Object} math Math.js namespace with functions and constants.
  280. * @param {Object} argNames An object with argument names as key and `true`
  281. * as value. Used in the SymbolNode to optimize
  282. * for arguments from user assigned functions
  283. * (see FunctionAssignmentNode) or special symbols
  284. * like `end` (see IndexNode).
  285. * @return {function} Returns a function which can be called like:
  286. * evalNode(scope: Object, args: Object, context: *)
  287. */
  288. }, {
  289. key: "_compile",
  290. value: function _compile(math, argNames) {
  291. // validate fn
  292. if (typeof this.fn !== 'string' || !(0, _customs.isSafeMethod)(math, this.fn)) {
  293. if (!math[this.fn]) {
  294. throw new Error('Function ' + this.fn + ' missing in provided namespace "math"');
  295. } else {
  296. throw new Error('No access to function "' + this.fn + '"');
  297. }
  298. }
  299. var fn = (0, _customs.getSafeProperty)(math, this.fn);
  300. var evalArgs = (0, _array.map)(this.args, function (arg) {
  301. return arg._compile(math, argNames);
  302. });
  303. if (evalArgs.length === 1) {
  304. var evalArg0 = evalArgs[0];
  305. return function evalOperatorNode(scope, args, context) {
  306. return fn(evalArg0(scope, args, context));
  307. };
  308. } else if (evalArgs.length === 2) {
  309. var _evalArg = evalArgs[0];
  310. var evalArg1 = evalArgs[1];
  311. return function evalOperatorNode(scope, args, context) {
  312. return fn(_evalArg(scope, args, context), evalArg1(scope, args, context));
  313. };
  314. } else {
  315. return function evalOperatorNode(scope, args, context) {
  316. return fn.apply(null, (0, _array.map)(evalArgs, function (evalArg) {
  317. return evalArg(scope, args, context);
  318. }));
  319. };
  320. }
  321. }
  322. /**
  323. * Execute a callback for each of the child nodes of this node
  324. * @param {function(child: Node, path: string, parent: Node)} callback
  325. */
  326. }, {
  327. key: "forEach",
  328. value: function forEach(callback) {
  329. for (var i = 0; i < this.args.length; i++) {
  330. callback(this.args[i], 'args[' + i + ']', this);
  331. }
  332. }
  333. /**
  334. * Create a new OperatorNode whose children are the results of calling
  335. * the provided callback function for each child of the original node.
  336. * @param {function(child: Node, path: string, parent: Node): Node} callback
  337. * @returns {OperatorNode} Returns a transformed copy of the node
  338. */
  339. }, {
  340. key: "map",
  341. value: function map(callback) {
  342. var args = [];
  343. for (var i = 0; i < this.args.length; i++) {
  344. args[i] = this._ifNode(callback(this.args[i], 'args[' + i + ']', this));
  345. }
  346. return new OperatorNode(this.op, this.fn, args, this.implicit, this.isPercentage);
  347. }
  348. /**
  349. * Create a clone of this node, a shallow copy
  350. * @return {OperatorNode}
  351. */
  352. }, {
  353. key: "clone",
  354. value: function clone() {
  355. return new OperatorNode(this.op, this.fn, this.args.slice(0), this.implicit, this.isPercentage);
  356. }
  357. /**
  358. * Check whether this is an unary OperatorNode:
  359. * has exactly one argument, like `-a`.
  360. * @return {boolean}
  361. * Returns true when an unary operator node, false otherwise.
  362. */
  363. }, {
  364. key: "isUnary",
  365. value: function isUnary() {
  366. return this.args.length === 1;
  367. }
  368. /**
  369. * Check whether this is a binary OperatorNode:
  370. * has exactly two arguments, like `a + b`.
  371. * @return {boolean}
  372. * Returns true when a binary operator node, false otherwise.
  373. */
  374. }, {
  375. key: "isBinary",
  376. value: function isBinary() {
  377. return this.args.length === 2;
  378. }
  379. /**
  380. * Get string representation.
  381. * @param {Object} options
  382. * @return {string} str
  383. */
  384. }, {
  385. key: "_toString",
  386. value: function _toString(options) {
  387. var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
  388. var implicit = options && options.implicit ? options.implicit : 'hide';
  389. var args = this.args;
  390. var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, false);
  391. if (args.length === 1) {
  392. // unary operators
  393. var assoc = (0, _operators.getAssociativity)(this, parenthesis);
  394. var operand = args[0].toString(options);
  395. if (parens[0]) {
  396. operand = '(' + operand + ')';
  397. }
  398. // for example for "not", we want a space between operand and argument
  399. var opIsNamed = /[a-zA-Z]+/.test(this.op);
  400. if (assoc === 'right') {
  401. // prefix operator
  402. return this.op + (opIsNamed ? ' ' : '') + operand;
  403. } else if (assoc === 'left') {
  404. // postfix
  405. return operand + (opIsNamed ? ' ' : '') + this.op;
  406. }
  407. // fall back to postfix
  408. return operand + this.op;
  409. } else if (args.length === 2) {
  410. var lhs = args[0].toString(options); // left hand side
  411. var rhs = args[1].toString(options); // right hand side
  412. if (parens[0]) {
  413. // left hand side in parenthesis?
  414. lhs = '(' + lhs + ')';
  415. }
  416. if (parens[1]) {
  417. // right hand side in parenthesis?
  418. rhs = '(' + rhs + ')';
  419. }
  420. if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
  421. return lhs + ' ' + rhs;
  422. }
  423. return lhs + ' ' + this.op + ' ' + rhs;
  424. } else if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) {
  425. var stringifiedArgs = args.map(function (arg, index) {
  426. arg = arg.toString(options);
  427. if (parens[index]) {
  428. // put in parenthesis?
  429. arg = '(' + arg + ')';
  430. }
  431. return arg;
  432. });
  433. if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
  434. return stringifiedArgs.join(' ');
  435. }
  436. return stringifiedArgs.join(' ' + this.op + ' ');
  437. } else {
  438. // fallback to formatting as a function call
  439. return this.fn + '(' + this.args.join(', ') + ')';
  440. }
  441. }
  442. /**
  443. * Get a JSON representation of the node
  444. * @returns {Object}
  445. */
  446. }, {
  447. key: "toJSON",
  448. value: function toJSON() {
  449. return {
  450. mathjs: name,
  451. op: this.op,
  452. fn: this.fn,
  453. args: this.args,
  454. implicit: this.implicit,
  455. isPercentage: this.isPercentage
  456. };
  457. }
  458. /**
  459. * Instantiate an OperatorNode from its JSON representation
  460. * @param {Object} json
  461. * An object structured like
  462. * ```
  463. * {"mathjs": "OperatorNode",
  464. * "op": "+", "fn": "add", "args": [...],
  465. * "implicit": false,
  466. * "isPercentage":false}
  467. * ```
  468. * where mathjs is optional
  469. * @returns {OperatorNode}
  470. */
  471. }, {
  472. key: "toHTML",
  473. value:
  474. /**
  475. * Get HTML representation.
  476. * @param {Object} options
  477. * @return {string} str
  478. */
  479. function toHTML(options) {
  480. var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
  481. var implicit = options && options.implicit ? options.implicit : 'hide';
  482. var args = this.args;
  483. var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, false);
  484. if (args.length === 1) {
  485. // unary operators
  486. var assoc = (0, _operators.getAssociativity)(this, parenthesis);
  487. var operand = args[0].toHTML(options);
  488. if (parens[0]) {
  489. operand = '<span class="math-parenthesis math-round-parenthesis">(</span>' + operand + '<span class="math-parenthesis math-round-parenthesis">)</span>';
  490. }
  491. if (assoc === 'right') {
  492. // prefix operator
  493. return '<span class="math-operator math-unary-operator ' + 'math-lefthand-unary-operator">' + (0, _string.escape)(this.op) + '</span>' + operand;
  494. } else {
  495. // postfix when assoc === 'left' or undefined
  496. return operand + '<span class="math-operator math-unary-operator ' + 'math-righthand-unary-operator">' + (0, _string.escape)(this.op) + '</span>';
  497. }
  498. } else if (args.length === 2) {
  499. // binary operatoes
  500. var lhs = args[0].toHTML(options); // left hand side
  501. var rhs = args[1].toHTML(options); // right hand side
  502. if (parens[0]) {
  503. // left hand side in parenthesis?
  504. lhs = '<span class="math-parenthesis math-round-parenthesis">(</span>' + lhs + '<span class="math-parenthesis math-round-parenthesis">)</span>';
  505. }
  506. if (parens[1]) {
  507. // right hand side in parenthesis?
  508. rhs = '<span class="math-parenthesis math-round-parenthesis">(</span>' + rhs + '<span class="math-parenthesis math-round-parenthesis">)</span>';
  509. }
  510. if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
  511. return lhs + '<span class="math-operator math-binary-operator ' + 'math-implicit-binary-operator"></span>' + rhs;
  512. }
  513. return lhs + '<span class="math-operator math-binary-operator ' + 'math-explicit-binary-operator">' + (0, _string.escape)(this.op) + '</span>' + rhs;
  514. } else {
  515. var stringifiedArgs = args.map(function (arg, index) {
  516. arg = arg.toHTML(options);
  517. if (parens[index]) {
  518. // put in parenthesis?
  519. arg = '<span class="math-parenthesis math-round-parenthesis">(</span>' + arg + '<span class="math-parenthesis math-round-parenthesis">)</span>';
  520. }
  521. return arg;
  522. });
  523. if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) {
  524. if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
  525. return stringifiedArgs.join('<span class="math-operator math-binary-operator ' + 'math-implicit-binary-operator"></span>');
  526. }
  527. return stringifiedArgs.join('<span class="math-operator math-binary-operator ' + 'math-explicit-binary-operator">' + (0, _string.escape)(this.op) + '</span>');
  528. } else {
  529. // fallback to formatting as a function call
  530. return '<span class="math-function">' + (0, _string.escape)(this.fn) + '</span><span class="math-paranthesis math-round-parenthesis">' + '(</span>' + stringifiedArgs.join('<span class="math-separator">,</span>') + '<span class="math-paranthesis math-round-parenthesis">)</span>';
  531. }
  532. }
  533. }
  534. /**
  535. * Get LaTeX representation
  536. * @param {Object} options
  537. * @return {string} str
  538. */
  539. }, {
  540. key: "_toTex",
  541. value: function _toTex(options) {
  542. var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
  543. var implicit = options && options.implicit ? options.implicit : 'hide';
  544. var args = this.args;
  545. var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, true);
  546. var op = _latex.latexOperators[this.fn];
  547. op = typeof op === 'undefined' ? this.op : op; // fall back to using this.op
  548. if (args.length === 1) {
  549. // unary operators
  550. var assoc = (0, _operators.getAssociativity)(this, parenthesis);
  551. var operand = args[0].toTex(options);
  552. if (parens[0]) {
  553. operand = "\\left(".concat(operand, "\\right)");
  554. }
  555. if (assoc === 'right') {
  556. // prefix operator
  557. return op + operand;
  558. } else if (assoc === 'left') {
  559. // postfix operator
  560. return operand + op;
  561. }
  562. // fall back to postfix
  563. return operand + op;
  564. } else if (args.length === 2) {
  565. // binary operators
  566. var lhs = args[0]; // left hand side
  567. var lhsTex = lhs.toTex(options);
  568. if (parens[0]) {
  569. lhsTex = "\\left(".concat(lhsTex, "\\right)");
  570. }
  571. var rhs = args[1]; // right hand side
  572. var rhsTex = rhs.toTex(options);
  573. if (parens[1]) {
  574. rhsTex = "\\left(".concat(rhsTex, "\\right)");
  575. }
  576. // handle some exceptions (due to the way LaTeX works)
  577. var lhsIdentifier;
  578. if (parenthesis === 'keep') {
  579. lhsIdentifier = lhs.getIdentifier();
  580. } else {
  581. // Ignore ParenthesisNodes if in 'keep' mode
  582. lhsIdentifier = lhs.getContent().getIdentifier();
  583. }
  584. switch (this.getIdentifier()) {
  585. case 'OperatorNode:divide':
  586. // op contains '\\frac' at this point
  587. return op + '{' + lhsTex + '}' + '{' + rhsTex + '}';
  588. case 'OperatorNode:pow':
  589. lhsTex = '{' + lhsTex + '}';
  590. rhsTex = '{' + rhsTex + '}';
  591. switch (lhsIdentifier) {
  592. case 'ConditionalNode': //
  593. case 'OperatorNode:divide':
  594. lhsTex = "\\left(".concat(lhsTex, "\\right)");
  595. }
  596. break;
  597. case 'OperatorNode:multiply':
  598. if (this.implicit && implicit === 'hide') {
  599. return lhsTex + '~' + rhsTex;
  600. }
  601. }
  602. return lhsTex + op + rhsTex;
  603. } else if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) {
  604. var texifiedArgs = args.map(function (arg, index) {
  605. arg = arg.toTex(options);
  606. if (parens[index]) {
  607. arg = "\\left(".concat(arg, "\\right)");
  608. }
  609. return arg;
  610. });
  611. if (this.getIdentifier() === 'OperatorNode:multiply' && this.implicit && implicit === 'hide') {
  612. return texifiedArgs.join('~');
  613. }
  614. return texifiedArgs.join(op);
  615. } else {
  616. // fall back to formatting as a function call
  617. // as this is a fallback, it doesn't use
  618. // fancy function names
  619. return '\\mathrm{' + this.fn + '}\\left(' + args.map(function (arg) {
  620. return arg.toTex(options);
  621. }).join(',') + '\\right)';
  622. }
  623. }
  624. /**
  625. * Get identifier.
  626. * @return {string}
  627. */
  628. }, {
  629. key: "getIdentifier",
  630. value: function getIdentifier() {
  631. return this.type + ':' + this.fn;
  632. }
  633. }], [{
  634. key: "fromJSON",
  635. value: function fromJSON(json) {
  636. return new OperatorNode(json.op, json.fn, json.args, json.implicit, json.isPercentage);
  637. }
  638. }]);
  639. return OperatorNode;
  640. }(Node);
  641. (0, _defineProperty2["default"])(OperatorNode, "name", name);
  642. return OperatorNode;
  643. }, {
  644. isClass: true,
  645. isNode: true
  646. });
  647. exports.createOperatorNode = createOperatorNode;