【问题标题】:How to visualize an Abstract Syntax Tree graphically?如何以图形方式可视化抽象语法树?
【发布时间】:2019-05-03 20:33:04
【问题描述】:

我用 C++ 编写了一个简单的编译器,并希望可视化它生成的抽象语法树。目前,我将 AST 转储为类似于以下内容的超长字符串:

Program(decls=[ConstDecl(type=BasicTypeKind::Int, value=Num(n=1, loc=Location(1, 21)), name=positive, loc=Location(1, 10)), ConstDecl(type=BasicTypeKind::Int, value=Num(n=-1, loc=Location(2, 21)), name=negative, loc=Location(2, 10)), ConstDecl(type=BasicTypeKind::Int, value=Num(n=100, loc=Location(3, 26)), name=max_heap_size, loc=Location(3, 10)), ConstDecl(type=BasicTypeKind::Character, value=Char(c=99, loc=Location(4, 23)), name=...

如您所见,这个转储在可视化方面不是很人性化。人们无法自然地将 tree 的概念与这么长的字符串联系起来。我尝试了漂亮打印 AST 的方法,并找到了用于 Python 的astpretty。它主要用于调试,但如果我想要 AST 的插图怎么办?图形格式肯定更合适。

实际上,我有关于我期待的输出的图片。 Graphviz 在这个领域做得很好,C++ 文档工具 Doxygen 生成的各种图表在概念上非常接近我的目的。

将这些放在一起,我想要一种方法将内存中的 AST 作为 C++ 对象转换为体面的图形输出(静态的也可以)。有什么好的起点吗?

编辑: 正如 cmets 所说,以 Graphviz 识别的格式转储我的 AST 是一个很好的起点。我会尝试这样做,直到出现新的更具体的问题。谢谢大家。

【问题讨论】:

  • 为什么不在你的输出中的数组元素等之间添加换行符,加上一些缩进呢?之后应该看起来好多了。
  • 使用点格式以便graphviz能够正确绘制
  • 在我看来你已经有了一个合适的起点。将您的 AST 表示转换为 graphviz 格式并从那里渲染。当然,除非这个问题还有更多内容,否则我建议您编辑您的帖子。
  • @bipll 我已经有一个漂亮的打印机,虽然是在 Python 中。 png 会比纯文本更好,因为我持有它。
  • 这个 SO 答案显示了如果您 a)将 AST 打印为带有缩进的嵌套树,并且 b)如果您走 AST 并为节点吐出 GraphVis(“点”)命令,则可以产生什么和弧线。 stackoverflow.com/a/17393852/120163

标签: c++ abstract-syntax-tree graph-visualization


【解决方案1】:

我找到了一个既快速又可以接受的解决方案。 LLVM-8 包含一个选项,通过从中生成点格式文件来可视化控制流图 (CFG)。基本上,这适用于更一般的图形结构,只要您专门化 llvm::GraphTraitsllvm::DOTGraphTraits 模板(稍作解决)并且 llvm::WriteGraph() 将适合您。这是没有太多微调的结果:

鉴于类似 C 的 sn-p:

const int IntConstant = 1;
int Array[2];

void main() {
  Printf("hello");
}

这种语言是 C 语言的简化版,不区分大小写。 Printf()Write 语句表示为图像中的节点。 ConstDecl 表示常量声明VarDecl 表示变量声明FuncDef 表示函数定义。其他事情都非常简单。

在解释魔法之前,我想向您指出这些对如何使用所涉及的 llvm APT 有帮助的文档。

  • GraphTraits 知道您的代码应该具有的签名。
  • WriteGraph 了解 WriteGraph 对您的代码有什么要求。
  • CFGPrinter 知道如何专业化你的GraphTraits
  • DOTGraphTraits 了解如何通过为节点提供标签和描述来使您的插图有意义。
  • iterator_facade_base 知道如何轻松编写工作迭代器。

现在有了这些知识库,您只需将它们放在一起。事实上,请按照以下步骤操作:

  1. 考虑如何遍历 AST 中的每个节点并专门化 nodes_iterator 实现。
  2. 考虑如何遍历给定节点的所有子节点并专门化 ChildIteratorType 实现。
  3. 想想如何从你的节点中提取有用的信息,并专门化你的DOTGraphTraitsgetNodeLabel()getNodeDescription()
  4. 注意我的:GraphTraitsNodeRef 类型必须是一个指针!事先知道这一点可以为您节省一天的时间。这适用于最新版本。也许将来他们会取消限制。

我当前的实现只包括一个节点的类名作为它的标签和一个不言自明的字符串作为它的描述,没有任何微调。如果您想要更多效果,即颜色、形状等,可以在您的DOTGraphTraits 中进行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-18
    • 2013-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多