OperatorNode.js 28 KB

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