operators.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.getAssociativity = getAssociativity;
  6. exports.getOperator = getOperator;
  7. exports.getPrecedence = getPrecedence;
  8. exports.isAssociativeWith = isAssociativeWith;
  9. exports.properties = void 0;
  10. var _object = require("../utils/object.js");
  11. var _is = require("../utils/is.js");
  12. function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
  13. function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
  14. function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
  15. var properties = [{
  16. // assignment
  17. AssignmentNode: {},
  18. FunctionAssignmentNode: {}
  19. }, {
  20. // conditional expression
  21. ConditionalNode: {
  22. latexLeftParens: false,
  23. latexRightParens: false,
  24. latexParens: false
  25. // conditionals don't need parentheses in LaTeX because
  26. // they are 2 dimensional
  27. }
  28. }, {
  29. // logical or
  30. 'OperatorNode:or': {
  31. op: 'or',
  32. associativity: 'left',
  33. associativeWith: []
  34. }
  35. }, {
  36. // logical xor
  37. 'OperatorNode:xor': {
  38. op: 'xor',
  39. associativity: 'left',
  40. associativeWith: []
  41. }
  42. }, {
  43. // logical and
  44. 'OperatorNode:and': {
  45. op: 'and',
  46. associativity: 'left',
  47. associativeWith: []
  48. }
  49. }, {
  50. // bitwise or
  51. 'OperatorNode:bitOr': {
  52. op: '|',
  53. associativity: 'left',
  54. associativeWith: []
  55. }
  56. }, {
  57. // bitwise xor
  58. 'OperatorNode:bitXor': {
  59. op: '^|',
  60. associativity: 'left',
  61. associativeWith: []
  62. }
  63. }, {
  64. // bitwise and
  65. 'OperatorNode:bitAnd': {
  66. op: '&',
  67. associativity: 'left',
  68. associativeWith: []
  69. }
  70. }, {
  71. // relational operators
  72. 'OperatorNode:equal': {
  73. op: '==',
  74. associativity: 'left',
  75. associativeWith: []
  76. },
  77. 'OperatorNode:unequal': {
  78. op: '!=',
  79. associativity: 'left',
  80. associativeWith: []
  81. },
  82. 'OperatorNode:smaller': {
  83. op: '<',
  84. associativity: 'left',
  85. associativeWith: []
  86. },
  87. 'OperatorNode:larger': {
  88. op: '>',
  89. associativity: 'left',
  90. associativeWith: []
  91. },
  92. 'OperatorNode:smallerEq': {
  93. op: '<=',
  94. associativity: 'left',
  95. associativeWith: []
  96. },
  97. 'OperatorNode:largerEq': {
  98. op: '>=',
  99. associativity: 'left',
  100. associativeWith: []
  101. },
  102. RelationalNode: {
  103. associativity: 'left',
  104. associativeWith: []
  105. }
  106. }, {
  107. // bitshift operators
  108. 'OperatorNode:leftShift': {
  109. op: '<<',
  110. associativity: 'left',
  111. associativeWith: []
  112. },
  113. 'OperatorNode:rightArithShift': {
  114. op: '>>',
  115. associativity: 'left',
  116. associativeWith: []
  117. },
  118. 'OperatorNode:rightLogShift': {
  119. op: '>>>',
  120. associativity: 'left',
  121. associativeWith: []
  122. }
  123. }, {
  124. // unit conversion
  125. 'OperatorNode:to': {
  126. op: 'to',
  127. associativity: 'left',
  128. associativeWith: []
  129. }
  130. }, {
  131. // range
  132. RangeNode: {}
  133. }, {
  134. // addition, subtraction
  135. 'OperatorNode:add': {
  136. op: '+',
  137. associativity: 'left',
  138. associativeWith: ['OperatorNode:add', 'OperatorNode:subtract']
  139. },
  140. 'OperatorNode:subtract': {
  141. op: '-',
  142. associativity: 'left',
  143. associativeWith: []
  144. }
  145. }, {
  146. // multiply, divide, modulus
  147. 'OperatorNode:multiply': {
  148. op: '*',
  149. associativity: 'left',
  150. associativeWith: ['OperatorNode:multiply', 'OperatorNode:divide', 'Operator:dotMultiply', 'Operator:dotDivide']
  151. },
  152. 'OperatorNode:divide': {
  153. op: '/',
  154. associativity: 'left',
  155. associativeWith: [],
  156. latexLeftParens: false,
  157. latexRightParens: false,
  158. latexParens: false
  159. // fractions don't require parentheses because
  160. // they're 2 dimensional, so parens aren't needed
  161. // in LaTeX
  162. },
  163. 'OperatorNode:dotMultiply': {
  164. op: '.*',
  165. associativity: 'left',
  166. associativeWith: ['OperatorNode:multiply', 'OperatorNode:divide', 'OperatorNode:dotMultiply', 'OperatorNode:doDivide']
  167. },
  168. 'OperatorNode:dotDivide': {
  169. op: './',
  170. associativity: 'left',
  171. associativeWith: []
  172. },
  173. 'OperatorNode:mod': {
  174. op: 'mod',
  175. associativity: 'left',
  176. associativeWith: []
  177. }
  178. }, {
  179. // Repeat multiplication for implicit multiplication
  180. 'OperatorNode:multiply': {
  181. associativity: 'left',
  182. associativeWith: ['OperatorNode:multiply', 'OperatorNode:divide', 'Operator:dotMultiply', 'Operator:dotDivide']
  183. }
  184. }, {
  185. // unary prefix operators
  186. 'OperatorNode:unaryPlus': {
  187. op: '+',
  188. associativity: 'right'
  189. },
  190. 'OperatorNode:unaryMinus': {
  191. op: '-',
  192. associativity: 'right'
  193. },
  194. 'OperatorNode:bitNot': {
  195. op: '~',
  196. associativity: 'right'
  197. },
  198. 'OperatorNode:not': {
  199. op: 'not',
  200. associativity: 'right'
  201. }
  202. }, {
  203. // exponentiation
  204. 'OperatorNode:pow': {
  205. op: '^',
  206. associativity: 'right',
  207. associativeWith: [],
  208. latexRightParens: false
  209. // the exponent doesn't need parentheses in
  210. // LaTeX because it's 2 dimensional
  211. // (it's on top)
  212. },
  213. 'OperatorNode:dotPow': {
  214. op: '.^',
  215. associativity: 'right',
  216. associativeWith: []
  217. }
  218. }, {
  219. // factorial
  220. 'OperatorNode:factorial': {
  221. op: '!',
  222. associativity: 'left'
  223. }
  224. }, {
  225. // matrix transpose
  226. 'OperatorNode:ctranspose': {
  227. op: "'",
  228. associativity: 'left'
  229. }
  230. }];
  231. /**
  232. * Returns the first non-parenthesis internal node, but only
  233. * when the 'parenthesis' option is unset or auto.
  234. * @param {Node} _node
  235. * @param {string} parenthesis
  236. * @return {Node}
  237. */
  238. exports.properties = properties;
  239. function unwrapParen(_node, parenthesis) {
  240. if (!parenthesis || parenthesis !== 'auto') return _node;
  241. var node = _node;
  242. while ((0, _is.isParenthesisNode)(node)) {
  243. node = node.content;
  244. }
  245. return node;
  246. }
  247. /**
  248. * Get the precedence of a Node.
  249. * Higher number for higher precedence, starting with 0.
  250. * Returns null if the precedence is undefined.
  251. *
  252. * @param {Node} _node
  253. * @param {string} parenthesis
  254. * @param {string} implicit
  255. * @param {Node} parent (for determining context for implicit multiplication)
  256. * @return {number | null}
  257. */
  258. function getPrecedence(_node, parenthesis, implicit, parent) {
  259. var node = _node;
  260. if (parenthesis !== 'keep') {
  261. // ParenthesisNodes are only ignored when not in 'keep' mode
  262. node = _node.getContent();
  263. }
  264. var identifier = node.getIdentifier();
  265. var precedence = null;
  266. for (var i = 0; i < properties.length; i++) {
  267. if (identifier in properties[i]) {
  268. precedence = i;
  269. break;
  270. }
  271. }
  272. // Bump up precedence of implicit multiplication, except when preceded
  273. // by a "Rule 2" fraction ( [unaryOp]constant / constant )
  274. if (identifier === 'OperatorNode:multiply' && node.implicit && implicit !== 'show') {
  275. var leftArg = unwrapParen(node.args[0], parenthesis);
  276. if (!((0, _is.isConstantNode)(leftArg) && parent && parent.getIdentifier() === 'OperatorNode:divide' && (0, _is.rule2Node)(unwrapParen(parent.args[0], parenthesis))) && !(leftArg.getIdentifier() === 'OperatorNode:divide' && (0, _is.rule2Node)(unwrapParen(leftArg.args[0], parenthesis)) && (0, _is.isConstantNode)(unwrapParen(leftArg.args[1])))) {
  277. precedence += 1;
  278. }
  279. }
  280. return precedence;
  281. }
  282. /**
  283. * Get the associativity of an operator (left or right).
  284. * Returns a string containing 'left' or 'right' or null if
  285. * the associativity is not defined.
  286. *
  287. * @param {Node} _node
  288. * @param {string} parenthesis
  289. * @return {string|null}
  290. * @throws {Error}
  291. */
  292. function getAssociativity(_node, parenthesis) {
  293. var node = _node;
  294. if (parenthesis !== 'keep') {
  295. // ParenthesisNodes are only ignored when not in 'keep' mode
  296. node = _node.getContent();
  297. }
  298. var identifier = node.getIdentifier();
  299. var index = getPrecedence(node, parenthesis);
  300. if (index === null) {
  301. // node isn't in the list
  302. return null;
  303. }
  304. var property = properties[index][identifier];
  305. if ((0, _object.hasOwnProperty)(property, 'associativity')) {
  306. if (property.associativity === 'left') {
  307. return 'left';
  308. }
  309. if (property.associativity === 'right') {
  310. return 'right';
  311. }
  312. // associativity is invalid
  313. throw Error('\'' + identifier + '\' has the invalid associativity \'' + property.associativity + '\'.');
  314. }
  315. // associativity is undefined
  316. return null;
  317. }
  318. /**
  319. * Check if an operator is associative with another operator.
  320. * Returns either true or false or null if not defined.
  321. *
  322. * @param {Node} nodeA
  323. * @param {Node} nodeB
  324. * @param {string} parenthesis
  325. * @return {boolean | null}
  326. */
  327. function isAssociativeWith(nodeA, nodeB, parenthesis) {
  328. // ParenthesisNodes are only ignored when not in 'keep' mode
  329. var a = parenthesis !== 'keep' ? nodeA.getContent() : nodeA;
  330. var b = parenthesis !== 'keep' ? nodeA.getContent() : nodeB;
  331. var identifierA = a.getIdentifier();
  332. var identifierB = b.getIdentifier();
  333. var index = getPrecedence(a, parenthesis);
  334. if (index === null) {
  335. // node isn't in the list
  336. return null;
  337. }
  338. var property = properties[index][identifierA];
  339. if ((0, _object.hasOwnProperty)(property, 'associativeWith') && property.associativeWith instanceof Array) {
  340. for (var i = 0; i < property.associativeWith.length; i++) {
  341. if (property.associativeWith[i] === identifierB) {
  342. return true;
  343. }
  344. }
  345. return false;
  346. }
  347. // associativeWith is not defined
  348. return null;
  349. }
  350. /**
  351. * Get the operator associated with a function name.
  352. * Returns a string with the operator symbol, or null if the
  353. * input is not the name of a function associated with an
  354. * operator.
  355. *
  356. * @param {string} Function name
  357. * @return {string | null} Associated operator symbol, if any
  358. */
  359. function getOperator(fn) {
  360. var identifier = 'OperatorNode:' + fn;
  361. var _iterator = _createForOfIteratorHelper(properties),
  362. _step;
  363. try {
  364. for (_iterator.s(); !(_step = _iterator.n()).done;) {
  365. var group = _step.value;
  366. if (identifier in group) {
  367. return group[identifier].op;
  368. }
  369. }
  370. } catch (err) {
  371. _iterator.e(err);
  372. } finally {
  373. _iterator.f();
  374. }
  375. return null;
  376. }