text: // Copyright (c) 2014 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  /**
   * @unrestricted
   */
  export class ESTreeWalker {
    /**
     * @param {function(!ESTree.Node):(!Object|undefined)} beforeVisit
     * @param {function(!ESTree.Node)=} afterVisit
     */
    constructor(beforeVisit, afterVisit) {
      this._beforeVisit = beforeVisit;
      this._afterVisit = afterVisit || new Function();
      this._walkNulls = false;
    }
  
    /**
     * @param {boolean} value
     */
    setWalkNulls(value) {
      this._walkNulls = value;
    }
  
    /**
     * @param {!ESTree.Node} ast
     */
    walk(ast) {
      this._innerWalk(ast, null);
    }
  
    /**
     * @param {!ESTree.Node} node
     * @param {?ESTree.Node} parent
     */
    _innerWalk(node, parent) {
      if (!node && parent && this._walkNulls) {
        const result = /** @type {!Object} */ ({raw: 'null', value: null});
        result.type = 'Literal';
        node = /** @type {!ESTree.Node} */ (result);
      }
  
      if (!node) {
        return;
      }
      node.parent = parent;
  
      if (this._beforeVisit.call(null, node) === FormatterWorker.ESTreeWalker.SkipSubtree) {
        this._afterVisit.call(null, node);
        return;
      }
  
      const walkOrder = _walkOrder[node.type];
      if (!walkOrder) {
        console.error('Walk order not defined for ' + node.type);
        return;
      }
  
      if (node.type === 'TemplateLiteral') {
        const templateLiteral = /** @type {!ESTree.TemplateLiteralNode} */ (node);
        const expressionsLength = templateLiteral.expressions.length;
        for (let i = 0; i < expressionsLength; ++i) {
          this._innerWalk(templateLiteral.quasis[i], templateLiteral);
          this._innerWalk(templateLiteral.expressions[i], templateLiteral);
        }
        this._innerWalk(templateLiteral.quasis[expressionsLength], templateLiteral);
      } else {
        for (let i = 0; i < walkOrder.length; ++i) {
          const entity = node[walkOrder[i]];
          if (Array.isArray(entity)) {
            this._walkArray(entity, node);
          } else {
            this._innerWalk(entity, node);
          }
        }
      }
  
      this._afterVisit.call(null, node);
    }
  
    /**
     * @param {!Array.<!ESTree.Node>} nodeArray
     * @param {?ESTree.Node} parentNode
     */
    _walkArray(nodeArray, parentNode) {
      for (let i = 0; i < nodeArray.length; ++i) {
        this._innerWalk(nodeArray[i], parentNode);
      }
    }
  }
  
  /** @enum {!Array.<string>} */
  const _walkOrder = {
    'AwaitExpression': ['arguments'],
    'ArrayExpression': ['elements'],
    'ArrayPattern': ['elements'],
    'ArrowFunctionExpression': ['params', 'body'],
    'AssignmentExpression': ['left', 'right'],
    'AssignmentPattern': ['left', 'right'],
    'BinaryExpression': ['left', 'right'],
    'BlockStatement': ['body'],
    'BreakStatement': ['label'],
    'CallExpression': ['callee', 'arguments'],
    'CatchClause': ['param', 'body'],
    'ClassBody': ['body'],
    'ClassDeclaration': ['id', 'superClass', 'body'],
    'ClassExpression': ['id', 'superClass', 'body'],
    'ConditionalExpression': ['test', 'consequent', 'alternate'],
    'ContinueStatement': ['label'],
    'DebuggerStatement': [],
    'DoWhileStatement': ['body', 'test'],
    'EmptyStatement': [],
    'ExpressionStatement': ['expression'],
    'ForInStatement': ['left', 'right', 'body'],
    'ForOfStatement': ['left', 'right', 'body'],
    'ForStatement': ['init', 'test', 'update', 'body'],
    'FunctionDeclaration': ['id', 'params', 'body'],
    'FunctionExpression': ['id', 'params', 'body'],
    'Identifier': [],
    'ImportDeclaration': ['specifiers', 'source'],
    'ImportExpression': ['source'],
    'ExportAllDeclaration': ['source'],
    'ExportDefaultDeclaration': ['declaration'],
    'ExportNamedDeclaration': ['specifiers', 'source', 'declaration'],
    'IfStatement': ['test', 'consequent', 'alternate'],
    'LabeledStatement': ['label', 'body'],
    'Literal': [],
    'LogicalExpression': ['left', 'right'],
    'MemberExpression': ['object', 'property'],
    'MethodDefinition': ['key', 'value'],
    'NewExpression': ['callee', 'arguments'],
    'ObjectExpression': ['properties'],
    'ObjectPattern': ['properties'],
    'ParenthesizedExpression': ['expression'],
    'Program': ['body'],
    'Property': ['key', 'value'],
    'RestElement': ['argument'],
    'ReturnStatement': ['argument'],
    'SequenceExpression': ['expressions'],
    'SpreadElement': ['argument'],
    'Super': [],
    'SwitchCase': ['test', 'consequent'],
    'SwitchStatement': ['discriminant', 'cases'],
    'TaggedTemplateExpression': ['tag', 'quasi'],
    'TemplateElement': [],
    'TemplateLiteral': ['quasis', 'expressions'],
    'ThisExpression': [],
    'ThrowStatement': ['argument'],
    'TryStatement': ['block', 'handler', 'finalizer'],
    'UnaryExpression': ['argument'],
    'UpdateExpression': ['argument'],
    'VariableDeclaration': ['declarations'],
    'VariableDeclarator': ['id', 'init'],
    'WhileStatement': ['test', 'body'],
    'WithStatement': ['object', 'body'],
    'YieldExpression': ['argument']
  };
  
  /* Legacy exported object */
  self.FormatterWorker = self.FormatterWorker || {};
  
  /* Legacy exported object */
  FormatterWorker = FormatterWorker || {};
  
  /** @constructor */
  FormatterWorker.ESTreeWalker = ESTreeWalker;
  
  /** @typedef {!Object} FormatterWorker.ESTreeWalker.SkipSubtree */
  FormatterWorker.ESTreeWalker.SkipSubtree = {};
  