ObjectNode.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import _defineProperty from "@babel/runtime/helpers/defineProperty";
  2. import { isNode } from '../../utils/is.js';
  3. import { escape, stringify } from '../../utils/string.js';
  4. import { isSafeProperty } from '../../utils/customs.js';
  5. import { hasOwnProperty } from '../../utils/object.js';
  6. import { factory } from '../../utils/factory.js';
  7. var name = 'ObjectNode';
  8. var dependencies = ['Node'];
  9. export var createObjectNode = /* #__PURE__ */factory(name, dependencies, _ref => {
  10. var {
  11. Node
  12. } = _ref;
  13. class ObjectNode extends Node {
  14. /**
  15. * @constructor ObjectNode
  16. * @extends {Node}
  17. * Holds an object with keys/values
  18. * @param {Object.<string, Node>} [properties] object with key/value pairs
  19. */
  20. constructor(properties) {
  21. super();
  22. this.properties = properties || {};
  23. // validate input
  24. if (properties) {
  25. if (!(typeof properties === 'object') || !Object.keys(properties).every(function (key) {
  26. return isNode(properties[key]);
  27. })) {
  28. throw new TypeError('Object containing Nodes expected');
  29. }
  30. }
  31. }
  32. get type() {
  33. return name;
  34. }
  35. get isObjectNode() {
  36. return true;
  37. }
  38. /**
  39. * Compile a node into a JavaScript function.
  40. * This basically pre-calculates as much as possible and only leaves open
  41. * calculations which depend on a dynamic scope with variables.
  42. * @param {Object} math Math.js namespace with functions and constants.
  43. * @param {Object} argNames An object with argument names as key and `true`
  44. * as value. Used in the SymbolNode to optimize
  45. * for arguments from user assigned functions
  46. * (see FunctionAssignmentNode) or special symbols
  47. * like `end` (see IndexNode).
  48. * @return {function} Returns a function which can be called like:
  49. * evalNode(scope: Object, args: Object, context: *)
  50. */
  51. _compile(math, argNames) {
  52. var evalEntries = {};
  53. for (var key in this.properties) {
  54. if (hasOwnProperty(this.properties, key)) {
  55. // we stringify/parse the key here to resolve unicode characters,
  56. // so you cannot create a key like {"co\\u006Estructor": null}
  57. var stringifiedKey = stringify(key);
  58. var parsedKey = JSON.parse(stringifiedKey);
  59. if (!isSafeProperty(this.properties, parsedKey)) {
  60. throw new Error('No access to property "' + parsedKey + '"');
  61. }
  62. evalEntries[parsedKey] = this.properties[key]._compile(math, argNames);
  63. }
  64. }
  65. return function evalObjectNode(scope, args, context) {
  66. var obj = {};
  67. for (var _key in evalEntries) {
  68. if (hasOwnProperty(evalEntries, _key)) {
  69. obj[_key] = evalEntries[_key](scope, args, context);
  70. }
  71. }
  72. return obj;
  73. };
  74. }
  75. /**
  76. * Execute a callback for each of the child nodes of this node
  77. * @param {function(child: Node, path: string, parent: Node)} callback
  78. */
  79. forEach(callback) {
  80. for (var key in this.properties) {
  81. if (hasOwnProperty(this.properties, key)) {
  82. callback(this.properties[key], 'properties[' + stringify(key) + ']', this);
  83. }
  84. }
  85. }
  86. /**
  87. * Create a new ObjectNode whose children are the results of calling
  88. * the provided callback function for each child of the original node.
  89. * @param {function(child: Node, path: string, parent: Node): Node} callback
  90. * @returns {ObjectNode} Returns a transformed copy of the node
  91. */
  92. map(callback) {
  93. var properties = {};
  94. for (var key in this.properties) {
  95. if (hasOwnProperty(this.properties, key)) {
  96. properties[key] = this._ifNode(callback(this.properties[key], 'properties[' + stringify(key) + ']', this));
  97. }
  98. }
  99. return new ObjectNode(properties);
  100. }
  101. /**
  102. * Create a clone of this node, a shallow copy
  103. * @return {ObjectNode}
  104. */
  105. clone() {
  106. var properties = {};
  107. for (var key in this.properties) {
  108. if (hasOwnProperty(this.properties, key)) {
  109. properties[key] = this.properties[key];
  110. }
  111. }
  112. return new ObjectNode(properties);
  113. }
  114. /**
  115. * Get string representation
  116. * @param {Object} options
  117. * @return {string} str
  118. * @override
  119. */
  120. _toString(options) {
  121. var entries = [];
  122. for (var key in this.properties) {
  123. if (hasOwnProperty(this.properties, key)) {
  124. entries.push(stringify(key) + ': ' + this.properties[key].toString(options));
  125. }
  126. }
  127. return '{' + entries.join(', ') + '}';
  128. }
  129. /**
  130. * Get a JSON representation of the node
  131. * @returns {Object}
  132. */
  133. toJSON() {
  134. return {
  135. mathjs: name,
  136. properties: this.properties
  137. };
  138. }
  139. /**
  140. * Instantiate an OperatorNode from its JSON representation
  141. * @param {Object} json An object structured like
  142. * `{"mathjs": "ObjectNode", "properties": {...}}`,
  143. * where mathjs is optional
  144. * @returns {ObjectNode}
  145. */
  146. static fromJSON(json) {
  147. return new ObjectNode(json.properties);
  148. }
  149. /**
  150. * Get HTML representation
  151. * @param {Object} options
  152. * @return {string} str
  153. * @override
  154. */
  155. toHTML(options) {
  156. var entries = [];
  157. for (var key in this.properties) {
  158. if (hasOwnProperty(this.properties, key)) {
  159. entries.push('<span class="math-symbol math-property">' + escape(key) + '</span>' + '<span class="math-operator math-assignment-operator ' + 'math-property-assignment-operator math-binary-operator">' + ':</span>' + this.properties[key].toHTML(options));
  160. }
  161. }
  162. return '<span class="math-parenthesis math-curly-parenthesis">{</span>' + entries.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-curly-parenthesis">}</span>';
  163. }
  164. /**
  165. * Get LaTeX representation
  166. * @param {Object} options
  167. * @return {string} str
  168. */
  169. _toTex(options) {
  170. var entries = [];
  171. for (var key in this.properties) {
  172. if (hasOwnProperty(this.properties, key)) {
  173. entries.push('\\mathbf{' + key + ':} & ' + this.properties[key].toTex(options) + '\\\\');
  174. }
  175. }
  176. var tex = '\\left\\{\\begin{array}{ll}' + entries.join('\n') + '\\end{array}\\right\\}';
  177. return tex;
  178. }
  179. }
  180. _defineProperty(ObjectNode, "name", name);
  181. return ObjectNode;
  182. }, {
  183. isClass: true,
  184. isNode: true
  185. });