【问题标题】:What is the difference between an Abstract Syntax Tree and a Concrete Syntax Tree?抽象语法树和具体语法树有什么区别?
【发布时间】:2010-12-25 17:19:23
【问题描述】:

我一直在阅读有关解释器/编译器如何工作的一些内容,而我感到困惑的一个领域是 AST 和 CST 之间的区别。我的理解是解析器生成一个 CST,将其交给语义分析器,语义分析器将其转换为 AST。但是,我的理解是语义分析器只是确保遵循规则。我真的不明白为什么它实际上会进行任何更改以使其抽象而不是具体。

我对语义分析器有什么遗漏吗,还是 AST 和 CST 之间的区别有些人为?

【问题讨论】:

    标签: parsing terminology abstract-syntax-tree semantic-analysis concrete-syntax-tree


    【解决方案1】:

    具体语法树匹配语法规则所说的语法。 抽象语法树的目的是对“语法树”中的基本内容进行“简单”表示。

    AST 恕我直言的真正价值在于它比 CST 小,因此处理时间更短。 (你可能会说,谁在乎呢?但我使用的是我们拥有的工具 数以千万计的节点同时存在!)。

    大多数支持构建语法树的解析器生成器都坚持要求您亲自指定它们的构建方式,前提是您的树节点将比 CST“更简单”(在这一点上,它们通常是正确的,因为程序员很懒)。可以说,这意味着您必须编写更少的树访问函数,这也很有价值,因为它可以最大限度地减少工程能量。当您有 3500 条规则时(例如,对于 COBOL),这很重要。而这种“简单”又导致了“小”的良好特性。

    但是拥有这样的 AST 会产生一个不存在的问题:它与语法不匹配,现在您必须在心理上跟踪它们。当 3500 个规则语法有 1500 个 AST 节点时,这很重要。如果语法不断发展(他们总是这样!),那么现在你有两套巨大的东西要保持同步。

    另一个解决方案是让解析器简单地为您构建 CST 节点并使用它们。这在构建语法时是一个巨大的优势:无需发明 1500 个特殊的 AST 节点来建模 3500 个语法规则。想想这棵树与语法同构。从语法工程师的角度来看,这完全是无脑的,这让他可以专注于正确地编写语法并尽情发挥。可以说您必须编写更多节点访问者规则,但这可以管理。稍后会详细介绍。

    我们对DMS Software Reengineering Toolkit 所做的是根据 (GLR) 解析过程的结果自动构建 CST。然后,出于空间效率的原因,DMS 会自动构造一个“压缩的”CST,方法是消除非携带值的终结符(关键字、标点符号)、语义上无用的一元产生式,并为语法规则对形成可直接索引的列表,如下所示:

        L = e ;
        L = L e ;
        L2 = e2 ;
        L2 = L2  ','  e2 ;
    

    以及此类形式的各种变体。您根据语法规则和虚拟 CST 进行思考;该工具对压缩表示进行操作。让您的大脑轻松,运行时更快/更小。

    值得注意的是,以这种方式构建的压缩 CST 看起来很像您可能手动设计的 AST(参见示例末尾的链接)。特别是,压缩的 CST 不携带任何只是具体语法的节点。 有一些小尴尬:例如,虽然表达式子语法中经典的 '(' 和 ')' 的具体节点不在树中,但“括号节点”确实出现在压缩的 CST 中并且必须处理。真正的 AST 不会有这个。对于不必指定 AST 构造的便利性,这似乎是一个很小的代价。并且树的文档始终可用且正确:语法文档。

    我们如何避免“额外访客”?我们不完全是,但 DMS 提供了一个 AST 库,它遍历 AST 并透明地处理 CST 和 AST 之间的差异。 DMS 还提供了一个“属性语法”评估器 (AGE),它是一种用于在树上和下层传递计算出的值的方法; AGE 处理所有的树表示问题,因此工具工程师只担心直接在语法规则本身上有效地编写计算。最后,DMS 还提供了“表面语法”模式,它允许语法中的代码片段用于查找特定类型的子树,而无需了解所涉及的大多数节点类型。

    其他答案之一是,如果您想构建可以重新生成源代码的工具,您的 AST 必须与 CST 匹配。这不太对,但如果您有 CST 节点,则重新生成源要容易得多。 DMS generates most of the prettyprinter automatically 因为它可以访问两者:-}

    底线:AST 适合小型的,无论是物理的还是概念的。来自 CST 的自动 AST 构造提供了这两者,并让您避免跟踪两个不同集合的问题。

    2015 年 3 月编辑:Link to examples of CST vs. "AST" built this way

    【讨论】:

      【解决方案2】:

      具体的语法树以解析的形式准确地表示源文本。一般来说,它符合定义源语言的上下文无关语法。

      然而,具体的语法和树有很多东西是使源文本明确可解析所必需的,但对实际意义没有贡献。例如,为了实现运算符优先级,您的 CFG 通常具有多个级别的表达式组件(术语、因子等),运算符在不同级别连接它们(您添加术语以获取表达式,术语由可选的多个因子组成, 等等。)。但是,要实际解释或编译该语言,您不需要它;您只需要具有运算符和操作数的表达式节点。抽象语法树是将具体语法树简化为表示程序含义实际需要的东西的结果。这棵树的定义要简单得多,因此在执行的后期更容易处理。

      您通常不需要实际构建具体的语法树。您的 YACC(或 Antlr、或 Menhir 或其他...)语法中的动作例程可以直接构建抽象语法树,因此具体语法树仅作为表示源文本解析结构的概念实体存在。

      【讨论】:

      • 补充:Python解释器先构建一个CST,然后再转换成AST。
      【解决方案3】:

      CST(具体语法树)是语法的树表示(程序应该如何编写的规则)。 根据编译器架构,解析器可以使用它来生成 AST。

      AST(Abstract Syntax Tree) 是 Parsed 源代码的树形表示,由编译器的 Parser 部分生成。它存储有关标记+语法的信息。

      根据编译器的架构,CST 可用于生成 AST。可以公平地说,CST 演变成 AST。或者,AST 是更丰富的 CST。

      更多解释可以在这个链接上找到:http://eli.thegreenplace.net/2009/02/16/abstract-vs-concrete-syntax-trees#id6

      【讨论】:

      • 我认为这些需要澄清,特别是在“简化”方面我倾向于将其视为“复杂”,至少在概念上是相反的,并且仍然没有描述任何有用的内容。
      • 我把我的 -1 改成了 +1。我觉得你的说明已经足够了。
      【解决方案4】:

      这是基于 Terrence Parr 的 Expression Evaluator 语法。

      本例的语法:

      grammar Expr002;
      
      options 
      {
          output=AST;
          ASTLabelType=CommonTree; // type of $stat.tree ref etc...
      }
      
      prog    :   ( stat )+ ;
      
      stat    :   expr NEWLINE        -> expr
              |   ID '=' expr NEWLINE -> ^('=' ID expr)
              |   NEWLINE             ->
              ;
      
      expr    :   multExpr (( '+'^ | '-'^ ) multExpr)*
              ; 
      
      multExpr
              :   atom ('*'^ atom)*
              ; 
      
      atom    :   INT 
              |   ID
              |   '('! expr ')'!
              ;
      
      ID      : ('a'..'z' | 'A'..'Z' )+ ;
      INT     : '0'..'9'+ ;
      NEWLINE : '\r'? '\n' ;
      WS      : ( ' ' | '\t' )+ { skip(); } ;
      

      输入

      x=1
      y=2
      3*(x+y)
      

      解析树

      解析树是输入的具体表示。解析树保留了输入的所有信息。空框代表空白,即行尾。

      AST

      AST 是输入的抽象表示。请注意,AST 中不存在括号,因为关联是从树结构派生的。

      编辑

      更详细的解释见Compilers and Compiler Generators pg。 23

      【讨论】:

        【解决方案5】:

        简单地说,AST 只包含代码的语义,解析树/CST 还包含有关代码编写方式的信息。

        【讨论】:

          【解决方案6】:

          这是一个没有区别的区别。

          AST 通常被解释为一种通过丢弃词汇内容来近似编程语言表达式语义的方法。例如,在上下文无关语法中,您可能会编写以下 EBNF 规则

          term: atom (('*' | '/') term )*
          

          而在 AST 情况下,您只使用 mul_rulediv_rule 来表达正确的算术运算。

          这些规则不能一开始就在语法中引入吗?当然。您可以重写上述紧凑和 抽象 规则,将其分解为用于模仿上述 AST 节点的更具体规则:

          term: mul_rule | div_rule
          mul_rule: atom ('*' term)*
          div_rule: atom ('/' term)*
          

          现在,当您考虑自顶向下解析时,第二个 term 引入了 mul_rulediv_rule 之间的 FIRST/FIRST 冲突LL(1) 解析器无法处理的东西。第一个规则形式是第二个规则形式的左因子版本,它有效地消除了结构。在这里使用 LL(1) 需要付出一些代价。

          因此,AST 是一种临时补充,用于修复语法和解析器的缺陷。 CST -> AST 转换是一种重构举措。当额外的逗号或冒号存储在语法树中时,没有人会担心。相反,一些作者将它们改造成 AST,因为他们喜欢使用 AST 进行重构,而不是同时维护各种树或编写额外的推理引擎。程序员懒惰是有原因的。实际上,它们甚至将通过词法分析收集的行和列信息存储在 AST 中以用于错误报告。确实很抽象。

          【讨论】:

            【解决方案7】:

            具体语法树包含所有信息,如多余的括号、空格和 cmets,抽象语法树从这些信息中抽象出来。

             

            NB:很有趣,当您实现重构引擎时,您的 AST 将再次包含所有具体信息,但您将继续将其称为 AST,因为这已成为该领域的标准术语(所以可以说它早就失去了原来的意义)。

            【讨论】:

            • 嗯,它可能没有所有的具体信息。所需要的只是它能够重新生成该信息。看我的回答。
            • 昨天评论了吗? SO bug 还是有一个我不知道的评论死灵法师徽章? :)(PS:但很高兴收到您的来信,您刚刚偶然发现了您在 DMS 上的 Google 技术演讲……)
            【解决方案8】:

            具体语法树遵循语言的语法规则。在语法中,“表达式列表”通常用两条规则定义

            • 表达式列表可以是:表达式
            • 表达式列表可以是:表达式、表达式列表

            从字面上看,这两条规则为出现在程序中的任何表达式列表提供了一个梳子形状。

            抽象语法树的形式便于进一步操作。它以一种对理解程序含义的人有意义的方式表示事物,而不仅仅是它们的编写方式。上面的表达式列表可能是函数的参数列表,可以方便地表示为表达式的向量,因为静态分析最好让表达式的总数显式可用并且能够通过其访问每个表达式索引。

            【讨论】:

              【解决方案9】:

              This blog post 可能会有所帮助。

              在我看来,AST“丢弃”了许多对语义没有贡献的中间语法/结构信息。例如,您不在乎 3 是一个原子是一个项是一个因子是一个...。您只关心它是 3 当您实现求幂表达式或其他什么时。

              【讨论】:

                猜你喜欢
                • 2011-08-23
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-12-17
                • 1970-01-01
                相关资源
                最近更新 更多