ConditionalNode.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.createConditionalNode = 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 _factory = require("../../utils/factory.js");
  15. var _operators = require("../operators.js");
  16. 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); }; }
  17. 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; } }
  18. var name = 'ConditionalNode';
  19. var dependencies = ['Node'];
  20. var createConditionalNode = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) {
  21. var Node = _ref.Node;
  22. /**
  23. * Test whether a condition is met
  24. * @param {*} condition
  25. * @returns {boolean} true if condition is true or non-zero, else false
  26. */
  27. function testCondition(condition) {
  28. if (typeof condition === 'number' || typeof condition === 'boolean' || typeof condition === 'string') {
  29. return !!condition;
  30. }
  31. if (condition) {
  32. if ((0, _is.isBigNumber)(condition)) {
  33. return !condition.isZero();
  34. }
  35. if ((0, _is.isComplex)(condition)) {
  36. return !!(condition.re || condition.im);
  37. }
  38. if ((0, _is.isUnit)(condition)) {
  39. return !!condition.value;
  40. }
  41. }
  42. if (condition === null || condition === undefined) {
  43. return false;
  44. }
  45. throw new TypeError('Unsupported type of condition "' + (0, _is.typeOf)(condition) + '"');
  46. }
  47. var ConditionalNode = /*#__PURE__*/function (_Node) {
  48. (0, _inherits2["default"])(ConditionalNode, _Node);
  49. var _super = _createSuper(ConditionalNode);
  50. /**
  51. * A lazy evaluating conditional operator: 'condition ? trueExpr : falseExpr'
  52. *
  53. * @param {Node} condition Condition, must result in a boolean
  54. * @param {Node} trueExpr Expression evaluated when condition is true
  55. * @param {Node} falseExpr Expression evaluated when condition is true
  56. *
  57. * @constructor ConditionalNode
  58. * @extends {Node}
  59. */
  60. function ConditionalNode(condition, trueExpr, falseExpr) {
  61. var _this;
  62. (0, _classCallCheck2["default"])(this, ConditionalNode);
  63. _this = _super.call(this);
  64. if (!(0, _is.isNode)(condition)) {
  65. throw new TypeError('Parameter condition must be a Node');
  66. }
  67. if (!(0, _is.isNode)(trueExpr)) {
  68. throw new TypeError('Parameter trueExpr must be a Node');
  69. }
  70. if (!(0, _is.isNode)(falseExpr)) {
  71. throw new TypeError('Parameter falseExpr must be a Node');
  72. }
  73. _this.condition = condition;
  74. _this.trueExpr = trueExpr;
  75. _this.falseExpr = falseExpr;
  76. return _this;
  77. }
  78. (0, _createClass2["default"])(ConditionalNode, [{
  79. key: "type",
  80. get: function get() {
  81. return name;
  82. }
  83. }, {
  84. key: "isConditionalNode",
  85. get: function get() {
  86. return true;
  87. }
  88. /**
  89. * Compile a node into a JavaScript function.
  90. * This basically pre-calculates as much as possible and only leaves open
  91. * calculations which depend on a dynamic scope with variables.
  92. * @param {Object} math Math.js namespace with functions and constants.
  93. * @param {Object} argNames An object with argument names as key and `true`
  94. * as value. Used in the SymbolNode to optimize
  95. * for arguments from user assigned functions
  96. * (see FunctionAssignmentNode) or special symbols
  97. * like `end` (see IndexNode).
  98. * @return {function} Returns a function which can be called like:
  99. * evalNode(scope: Object, args: Object, context: *)
  100. */
  101. }, {
  102. key: "_compile",
  103. value: function _compile(math, argNames) {
  104. var evalCondition = this.condition._compile(math, argNames);
  105. var evalTrueExpr = this.trueExpr._compile(math, argNames);
  106. var evalFalseExpr = this.falseExpr._compile(math, argNames);
  107. return function evalConditionalNode(scope, args, context) {
  108. return testCondition(evalCondition(scope, args, context)) ? evalTrueExpr(scope, args, context) : evalFalseExpr(scope, args, context);
  109. };
  110. }
  111. /**
  112. * Execute a callback for each of the child nodes of this node
  113. * @param {function(child: Node, path: string, parent: Node)} callback
  114. */
  115. }, {
  116. key: "forEach",
  117. value: function forEach(callback) {
  118. callback(this.condition, 'condition', this);
  119. callback(this.trueExpr, 'trueExpr', this);
  120. callback(this.falseExpr, 'falseExpr', this);
  121. }
  122. /**
  123. * Create a new ConditionalNode whose children are the results of calling
  124. * the provided callback function for each child of the original node.
  125. * @param {function(child: Node, path: string, parent: Node): Node} callback
  126. * @returns {ConditionalNode} Returns a transformed copy of the node
  127. */
  128. }, {
  129. key: "map",
  130. value: function map(callback) {
  131. return new ConditionalNode(this._ifNode(callback(this.condition, 'condition', this)), this._ifNode(callback(this.trueExpr, 'trueExpr', this)), this._ifNode(callback(this.falseExpr, 'falseExpr', this)));
  132. }
  133. /**
  134. * Create a clone of this node, a shallow copy
  135. * @return {ConditionalNode}
  136. */
  137. }, {
  138. key: "clone",
  139. value: function clone() {
  140. return new ConditionalNode(this.condition, this.trueExpr, this.falseExpr);
  141. }
  142. /**
  143. * Get string representation
  144. * @param {Object} options
  145. * @return {string} str
  146. */
  147. }, {
  148. key: "_toString",
  149. value: function _toString(options) {
  150. var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
  151. var precedence = (0, _operators.getPrecedence)(this, parenthesis, options && options.implicit);
  152. // Enclose Arguments in parentheses if they are an OperatorNode
  153. // or have lower or equal precedence
  154. // NOTE: enclosing all OperatorNodes in parentheses is a decision
  155. // purely based on aesthetics and readability
  156. var condition = this.condition.toString(options);
  157. var conditionPrecedence = (0, _operators.getPrecedence)(this.condition, parenthesis, options && options.implicit);
  158. if (parenthesis === 'all' || this.condition.type === 'OperatorNode' || conditionPrecedence !== null && conditionPrecedence <= precedence) {
  159. condition = '(' + condition + ')';
  160. }
  161. var trueExpr = this.trueExpr.toString(options);
  162. var truePrecedence = (0, _operators.getPrecedence)(this.trueExpr, parenthesis, options && options.implicit);
  163. if (parenthesis === 'all' || this.trueExpr.type === 'OperatorNode' || truePrecedence !== null && truePrecedence <= precedence) {
  164. trueExpr = '(' + trueExpr + ')';
  165. }
  166. var falseExpr = this.falseExpr.toString(options);
  167. var falsePrecedence = (0, _operators.getPrecedence)(this.falseExpr, parenthesis, options && options.implicit);
  168. if (parenthesis === 'all' || this.falseExpr.type === 'OperatorNode' || falsePrecedence !== null && falsePrecedence <= precedence) {
  169. falseExpr = '(' + falseExpr + ')';
  170. }
  171. return condition + ' ? ' + trueExpr + ' : ' + falseExpr;
  172. }
  173. /**
  174. * Get a JSON representation of the node
  175. * @returns {Object}
  176. */
  177. }, {
  178. key: "toJSON",
  179. value: function toJSON() {
  180. return {
  181. mathjs: name,
  182. condition: this.condition,
  183. trueExpr: this.trueExpr,
  184. falseExpr: this.falseExpr
  185. };
  186. }
  187. /**
  188. * Instantiate an ConditionalNode from its JSON representation
  189. * @param {Object} json
  190. * An object structured like
  191. * ```
  192. * {"mathjs": "ConditionalNode",
  193. * "condition": ...,
  194. * "trueExpr": ...,
  195. * "falseExpr": ...}
  196. * ```
  197. * where mathjs is optional
  198. * @returns {ConditionalNode}
  199. */
  200. }, {
  201. key: "toHTML",
  202. value:
  203. /**
  204. * Get HTML representation
  205. * @param {Object} options
  206. * @return {string} str
  207. */
  208. function toHTML(options) {
  209. var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
  210. var precedence = (0, _operators.getPrecedence)(this, parenthesis, options && options.implicit);
  211. // Enclose Arguments in parentheses if they are an OperatorNode
  212. // or have lower or equal precedence
  213. // NOTE: enclosing all OperatorNodes in parentheses is a decision
  214. // purely based on aesthetics and readability
  215. var condition = this.condition.toHTML(options);
  216. var conditionPrecedence = (0, _operators.getPrecedence)(this.condition, parenthesis, options && options.implicit);
  217. if (parenthesis === 'all' || this.condition.type === 'OperatorNode' || conditionPrecedence !== null && conditionPrecedence <= precedence) {
  218. condition = '<span class="math-parenthesis math-round-parenthesis">(</span>' + condition + '<span class="math-parenthesis math-round-parenthesis">)</span>';
  219. }
  220. var trueExpr = this.trueExpr.toHTML(options);
  221. var truePrecedence = (0, _operators.getPrecedence)(this.trueExpr, parenthesis, options && options.implicit);
  222. if (parenthesis === 'all' || this.trueExpr.type === 'OperatorNode' || truePrecedence !== null && truePrecedence <= precedence) {
  223. trueExpr = '<span class="math-parenthesis math-round-parenthesis">(</span>' + trueExpr + '<span class="math-parenthesis math-round-parenthesis">)</span>';
  224. }
  225. var falseExpr = this.falseExpr.toHTML(options);
  226. var falsePrecedence = (0, _operators.getPrecedence)(this.falseExpr, parenthesis, options && options.implicit);
  227. if (parenthesis === 'all' || this.falseExpr.type === 'OperatorNode' || falsePrecedence !== null && falsePrecedence <= precedence) {
  228. falseExpr = '<span class="math-parenthesis math-round-parenthesis">(</span>' + falseExpr + '<span class="math-parenthesis math-round-parenthesis">)</span>';
  229. }
  230. return condition + '<span class="math-operator math-conditional-operator">?</span>' + trueExpr + '<span class="math-operator math-conditional-operator">:</span>' + falseExpr;
  231. }
  232. /**
  233. * Get LaTeX representation
  234. * @param {Object} options
  235. * @return {string} str
  236. */
  237. }, {
  238. key: "_toTex",
  239. value: function _toTex(options) {
  240. return '\\begin{cases} {' + this.trueExpr.toTex(options) + '}, &\\quad{\\text{if }\\;' + this.condition.toTex(options) + '}\\\\{' + this.falseExpr.toTex(options) + '}, &\\quad{\\text{otherwise}}\\end{cases}';
  241. }
  242. }], [{
  243. key: "fromJSON",
  244. value: function fromJSON(json) {
  245. return new ConditionalNode(json.condition, json.trueExpr, json.falseExpr);
  246. }
  247. }]);
  248. return ConditionalNode;
  249. }(Node);
  250. (0, _defineProperty2["default"])(ConditionalNode, "name", name);
  251. return ConditionalNode;
  252. }, {
  253. isClass: true,
  254. isNode: true
  255. });
  256. exports.createConditionalNode = createConditionalNode;