AccessorNode.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import _defineProperty from "@babel/runtime/helpers/defineProperty";
  2. import { isAccessorNode, isArrayNode, isConstantNode, isFunctionNode, isIndexNode, isNode, isObjectNode, isParenthesisNode, isSymbolNode } from '../../utils/is.js';
  3. import { getSafeProperty } from '../../utils/customs.js';
  4. import { factory } from '../../utils/factory.js';
  5. import { accessFactory } from './utils/access.js';
  6. var name = 'AccessorNode';
  7. var dependencies = ['subset', 'Node'];
  8. export var createAccessorNode = /* #__PURE__ */factory(name, dependencies, _ref => {
  9. var {
  10. subset,
  11. Node
  12. } = _ref;
  13. var access = accessFactory({
  14. subset
  15. });
  16. /**
  17. * Are parenthesis needed?
  18. * @private
  19. */
  20. function needParenthesis(node) {
  21. // TODO: maybe make a method on the nodes which tells whether they need parenthesis?
  22. return !(isAccessorNode(node) || isArrayNode(node) || isConstantNode(node) || isFunctionNode(node) || isObjectNode(node) || isParenthesisNode(node) || isSymbolNode(node));
  23. }
  24. class AccessorNode extends Node {
  25. /**
  26. * @constructor AccessorNode
  27. * @extends {Node}
  28. * Access an object property or get a matrix subset
  29. *
  30. * @param {Node} object The object from which to retrieve
  31. * a property or subset.
  32. * @param {IndexNode} index IndexNode containing ranges
  33. */
  34. constructor(object, index) {
  35. super();
  36. if (!isNode(object)) {
  37. throw new TypeError('Node expected for parameter "object"');
  38. }
  39. if (!isIndexNode(index)) {
  40. throw new TypeError('IndexNode expected for parameter "index"');
  41. }
  42. this.object = object;
  43. this.index = index;
  44. }
  45. // readonly property name
  46. get name() {
  47. if (this.index) {
  48. return this.index.isObjectProperty() ? this.index.getObjectProperty() : '';
  49. } else {
  50. return this.object.name || '';
  51. }
  52. }
  53. get type() {
  54. return name;
  55. }
  56. get isAccessorNode() {
  57. return true;
  58. }
  59. /**
  60. * Compile a node into a JavaScript function.
  61. * This basically pre-calculates as much as possible and only leaves open
  62. * calculations which depend on a dynamic scope with variables.
  63. * @param {Object} math Math.js namespace with functions and constants.
  64. * @param {Object} argNames An object with argument names as key and `true`
  65. * as value. Used in the SymbolNode to optimize
  66. * for arguments from user assigned functions
  67. * (see FunctionAssignmentNode) or special symbols
  68. * like `end` (see IndexNode).
  69. * @return {function} Returns a function which can be called like:
  70. * evalNode(scope: Object, args: Object, context: *)
  71. */
  72. _compile(math, argNames) {
  73. var evalObject = this.object._compile(math, argNames);
  74. var evalIndex = this.index._compile(math, argNames);
  75. if (this.index.isObjectProperty()) {
  76. var prop = this.index.getObjectProperty();
  77. return function evalAccessorNode(scope, args, context) {
  78. // get a property from an object evaluated using the scope.
  79. return getSafeProperty(evalObject(scope, args, context), prop);
  80. };
  81. } else {
  82. return function evalAccessorNode(scope, args, context) {
  83. var object = evalObject(scope, args, context);
  84. // we pass just object here instead of context:
  85. var index = evalIndex(scope, args, object);
  86. return access(object, index);
  87. };
  88. }
  89. }
  90. /**
  91. * Execute a callback for each of the child nodes of this node
  92. * @param {function(child: Node, path: string, parent: Node)} callback
  93. */
  94. forEach(callback) {
  95. callback(this.object, 'object', this);
  96. callback(this.index, 'index', this);
  97. }
  98. /**
  99. * Create a new AccessorNode whose children are the results of calling
  100. * the provided callback function for each child of the original node.
  101. * @param {function(child: Node, path: string, parent: Node): Node} callback
  102. * @returns {AccessorNode} Returns a transformed copy of the node
  103. */
  104. map(callback) {
  105. return new AccessorNode(this._ifNode(callback(this.object, 'object', this)), this._ifNode(callback(this.index, 'index', this)));
  106. }
  107. /**
  108. * Create a clone of this node, a shallow copy
  109. * @return {AccessorNode}
  110. */
  111. clone() {
  112. return new AccessorNode(this.object, this.index);
  113. }
  114. /**
  115. * Get string representation
  116. * @param {Object} options
  117. * @return {string}
  118. */
  119. _toString(options) {
  120. var object = this.object.toString(options);
  121. if (needParenthesis(this.object)) {
  122. object = '(' + object + ')';
  123. }
  124. return object + this.index.toString(options);
  125. }
  126. /**
  127. * Get HTML representation
  128. * @param {Object} options
  129. * @return {string}
  130. */
  131. toHTML(options) {
  132. var object = this.object.toHTML(options);
  133. if (needParenthesis(this.object)) {
  134. object = '<span class="math-parenthesis math-round-parenthesis">(</span>' + object + '<span class="math-parenthesis math-round-parenthesis">)</span>';
  135. }
  136. return object + this.index.toHTML(options);
  137. }
  138. /**
  139. * Get LaTeX representation
  140. * @param {Object} options
  141. * @return {string}
  142. */
  143. _toTex(options) {
  144. var object = this.object.toTex(options);
  145. if (needParenthesis(this.object)) {
  146. object = '\\left(\' + object + \'\\right)';
  147. }
  148. return object + this.index.toTex(options);
  149. }
  150. /**
  151. * Get a JSON representation of the node
  152. * @returns {Object}
  153. */
  154. toJSON() {
  155. return {
  156. mathjs: name,
  157. object: this.object,
  158. index: this.index
  159. };
  160. }
  161. /**
  162. * Instantiate an AccessorNode from its JSON representation
  163. * @param {Object} json
  164. * An object structured like
  165. * `{"mathjs": "AccessorNode", object: ..., index: ...}`,
  166. * where mathjs is optional
  167. * @returns {AccessorNode}
  168. */
  169. static fromJSON(json) {
  170. return new AccessorNode(json.object, json.index);
  171. }
  172. }
  173. _defineProperty(AccessorNode, "name", name);
  174. return AccessorNode;
  175. }, {
  176. isClass: true,
  177. isNode: true
  178. });