有什么办法可以在Dart中以预购,后购或有序方式遍历Ast

时间:2018-07-23 15:44:35

标签: dart dart-analyzer

是否可以通过PreOrder,postOrder或inOrder遍历由dart Analyzer组成的AST。我正在使用访问节点通过GeneralizingAstVisitor遍历AST树,但它只是从代码的顶部到底部递归遍历。

import'package:analyzer/src/generated/testing/element_factory.dart';
import 'package:analyzer/analyzer.dart';
import 'dart:io';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/context/builder.dart';
import 'package:analyzer/src/dart/sdk/sdk.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/source/source_resource.dart';

main() {
  LibraryElement libElement;
  Source source;
  AnalysisContext context;
  var ast = parseCompilationUnit(src,
      parseFunctionBodies: true, suppressErrors: true);
  print(ast.toSource());
  PhysicalResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE;
  DartSdk sdk = new FolderBasedDartSdk(resourceProvider,
      resourceProvider.getFolder("/usr/local/opt/dart/libexec"));

  var resolvers = [
    new DartUriResolver(sdk),
  ];
  context = AnalysisEngine.instance.createAnalysisContext()
    ..sourceFactory = new SourceFactory(resolvers);
  source = new FileSource(resourceProvider.getFile(
      "/Users/shubhamkumar/Sites/projects/flutterX/dart_analyser/demo.dart"));
  ChangeSet changeSet = new ChangeSet()..addedSource(source);
  context.applyChanges(changeSet);

  libElement = context.computeLibraryElement(source);
  callAST(context, source, libElement);
}

class Visitor1 extends GeneralizingAstVisitor {
  @override
  visitNode(AstNode node) {
    print("node $node ${node.runtimeType}   ");
    node.childEntities.forEach((n) => print(n));
    return super.visitNode(node);
  }
}

callAST(context, source, libElement) {
  CompilationUnit resolvedUnit =
      context.resolveCompilationUnit(source, libElement);
  var visitor = new Visitor1();
  resolvedUnit.accept(visitor);
}

如果您有任何解决方案,请提供帮助。

1 个答案:

答案 0 :(得分:2)

The pattern that GeneralizingAstVisitor does is pre-order.

In-order traversal doesn't make sense in the context of an AST. In-order traversal is left, root, right. But an AST branch may have anywhere from 1 to infinity children. So the best you could do is define some in-order(n) traversal, where you visit the first child, second child, ... nth-child, root, nth+1 child, nth+2 child... I don't see a purpose of this.

For post-order its a bit more nuanced. If all you want to do is print the node and its child entities, then your solution is simple. You just have to call super before printing the node:

class Visitor2 extends GeneralizingAstVisitor {
  @override
  visitNode(AstNode node) {
    final val = super.visitNode(node);
    print("node $node ${node.runtimeType}   ");
    node.childEntities.forEach((n) => print(n));
    return val;
  }
}

But if you wanted custom logic for a bunch of node types, you'd have to follow that pattern in each visit handler:

class Visitor3 extends GeneralizingAstVisitor {
  @override
  visitAssignmentExpression(AssignmentExpression node) {
    final val = super.visitNode(node);
    // use assignment expression here
    return val;
  }
  @override
  visitBinaryExpression(BinaryExpression node) {
    final val = super.visitNode(node);
    // use binary expression here
    return val;
  }
  // ... more handlers
}

In this case, I would compose visitors to make this easier:

class PostOrderVisitor extends GeneralizingAstVisitor {
  AstVisitor postOrderedVisitor = new Visitor4();
  @override
  visitNode(AstNode node) {
    final val = super.visitNode(node);
    return node.accept(postOrderedVisitor);
  }
}

class Visitor4 extends AstVisitor {
  @override
  visitAssignmentExpression(AssignmentExpression node) {
    // use assignment expression here
  }
  @override
  visitBinaryExpression(BinaryExpression node) {
    // use binary expression here
  }
  // ... more handlers
}

In this case, PostOrderVisitor handles the post-ordering, and Visitor4 handles the individual nodes according to that order but should not do any recursion itself.

These should get you by for most use cases, though it's hard to be certain without knowing what you're trying to do.