【问题标题】:Is there any way to traverse the Ast in preorder, postorder or Inorder in Dart有什么方法可以在 Dart 中按 preorder、postorder 或 Inorder 遍历 Ast
【发布时间】:2018-07-25 12:42:23
【问题描述】:

有没有办法在 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);
}

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

【问题讨论】:

  • 你不应该用node.childEntities.forEach((n) => print(n)); return super.visitNode(node);自己控制吗?
  • 不,从上到下递归遍历代码访问父节点,然后是其子节点,然后是下一个父节点,我想先遍历所有父节点,如果满足所需的节点,然后是它的子节点。

标签: dart dart-analyzer


【解决方案1】:

GeneralizingAstVisitor 所做的模式是预购的。

按顺序遍历在 AST 的上下文中没有意义。顺序遍历是左、根、右。但是一个 AST 分支可能有 1 到无穷多个子节点。所以你能做的最好的事情就是定义一些有序(n)遍历,你访问第一个孩子,第二个孩子,......第n个孩子,根,第n个+1个孩子,第n个+2个孩子......我不'看不出这样做的目的。

对于后订购来说,它有点微妙。如果您只想打印节点及其子实体,那么您的解决方案很简单。您只需要在打印节点之前调用 super

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;
  }
}

但是,如果您想要为一堆节点类型自定义逻辑,则必须在每个访问处理程序中遵循该模式:

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
}

在这种情况下,我会编写访问者以使这更容易:

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
}

在这种情况下,PostOrderVisitor 处理后排序,Visitor4 根据该顺序处理各个节点,但本身不应进行任何递归。

对于大多数用例来说,这些应该可以帮助您,尽管如果不知道您要做什么就很难确定。

【讨论】:

  • 是的,这个作品,你能解释一下node.accept(postOrderedVisitor)在代码中的作用吗
  • @ShubhamKumar(我建议阅读更多关于访问者模式的信息,有很多很好的资源可以提供帮助!)访问者模式是一种双重调度的方式。它为某些类型的组合调用一些代码。在这种情况下,节点的类型和访问者的类型。所以 x.accept(y) 最终会调用 Y.visitX()。由于 PostOrderVisitor 处理订单,Visitor4.visitX() 处理算法,我们调用“node.accept(postOrderedVisitor)”,以便执行 Visitor4.visitX()。如果“节点”的类型与您无关,您可以编写“postOrderedVisitor.visitNode(node)”。
  • 非常感谢您的时间和帮助。它确实解决了我的一些问题。我刚开始从事 dart-Analysis 工作,如果您在上面分享一些文档或资源,将会非常有帮助
  • 这是我通过谷歌搜索找到的关于访问者模式的最佳指南:manski.net/2013/05/the-visitor-pattern-explained 虽然它是为 C# 编写的,所以如果您不熟悉方法重载,那么您可能需要查看它.否则 C# 和 Dart 之间的差异希望不会大到足以成为问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-17
  • 1970-01-01
  • 1970-01-01
  • 2021-06-17
  • 2021-08-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多