simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

resolve.js (3052B)


      1 import { createMap, isMap } from '../../utils/map.js';
      2 import { isFunctionNode, isNode, isOperatorNode, isParenthesisNode, isSymbolNode } from '../../utils/is.js';
      3 import { factory } from '../../utils/factory.js';
      4 var name = 'resolve';
      5 var dependencies = ['parse', 'ConstantNode', 'FunctionNode', 'OperatorNode', 'ParenthesisNode'];
      6 export var createResolve = /* #__PURE__ */factory(name, dependencies, _ref => {
      7   var {
      8     parse,
      9     ConstantNode,
     10     FunctionNode,
     11     OperatorNode,
     12     ParenthesisNode
     13   } = _ref;
     14 
     15   /**
     16    * resolve(expr, scope) replaces variable nodes with their scoped values
     17    *
     18    * Syntax:
     19    *
     20    *     resolve(expr, scope)
     21    *
     22    * Examples:
     23    *
     24    *     math.resolve('x + y', {x:1, y:2})           // Node {1 + 2}
     25    *     math.resolve(math.parse('x+y'), {x:1, y:2}) // Node {1 + 2}
     26    *     math.simplify('x+y', {x:2, y:'x+x'}).toString()      // "6"
     27    *
     28    * See also:
     29    *
     30    *     simplify, evaluate
     31    *
     32    * @param {Node} node
     33    *     The expression tree to be simplified
     34    * @param {Object} scope
     35    *     Scope specifying variables to be resolved
     36    * @return {Node} Returns `node` with variables recursively substituted.
     37    * @throws {ReferenceError}
     38    *     If there is a cyclic dependency among the variables in `scope`,
     39    *     resolution is impossible and a ReferenceError is thrown.
     40    */
     41   function resolve(node, scope) {
     42     var within = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Set();
     43 
     44     // note `within`:
     45     // `within` is not documented, since it is for internal cycle
     46     // detection only
     47     if (!scope) {
     48       return node;
     49     }
     50 
     51     if (!isMap(scope)) {
     52       scope = createMap(scope);
     53     }
     54 
     55     if (isSymbolNode(node)) {
     56       if (within.has(node.name)) {
     57         var variables = Array.from(within).join(', ');
     58         throw new ReferenceError("recursive loop of variable definitions among {".concat(variables, "}"));
     59       }
     60 
     61       var value = scope.get(node.name);
     62 
     63       if (isNode(value)) {
     64         var nextWithin = new Set(within);
     65         nextWithin.add(node.name);
     66         return resolve(value, scope, nextWithin);
     67       } else if (typeof value === 'number') {
     68         return parse(String(value));
     69       } else if (value !== undefined) {
     70         return new ConstantNode(value);
     71       } else {
     72         return node;
     73       }
     74     } else if (isOperatorNode(node)) {
     75       var args = node.args.map(function (arg) {
     76         return resolve(arg, scope, within);
     77       });
     78       return new OperatorNode(node.op, node.fn, args, node.implicit);
     79     } else if (isParenthesisNode(node)) {
     80       return new ParenthesisNode(resolve(node.content, scope, within));
     81     } else if (isFunctionNode(node)) {
     82       var _args = node.args.map(function (arg) {
     83         return resolve(arg, scope, within);
     84       });
     85 
     86       return new FunctionNode(node.name, _args);
     87     } // Otherwise just recursively resolve any children (might also work
     88     // for some of the above special cases)
     89 
     90 
     91     return node.map(child => resolve(child, scope, within));
     92   }
     93 
     94   return resolve;
     95 });