编译器构造:显式解析树

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译器构造:显式解析树相关的知识,希望对你有一定的参考价值。

编译器如何在不构造显式解析树的情况下完成?显式解析树构造的好处和缺点是什么?

我知道编译器可以在没有显式解析树的情况下通过使用SDT并在解析期间运行与之关联的语义来构建。但我想知道显式解析树结构的好处和缺点。

答案

我有点像菜鸟所以请耐心等待...... thx ...

但是要回答你的问题,一个递归正常的编译(没有解析树)只能在最简单的情况下完成,其中没有前向引用和符号仅从其声明的点而不是它的整个范围有效。

显然它不会使用像java这样的语言。如果是前向引用,那么至少需要两次传递,如果在java之上重载函数,那么将需要三次传递(或者如果你知道如何在不到三个时间内执行它那么请指教我们) 。为此,我们构建了一个解析树。

最简单的解析树节点可能看起来像这样(免责声明:这不是真正的代码)。

package compiler;
import java.util.ArrayList;
import scanner.Token;
import scanner.TokenSet;

class Production
{
   Token leading;      // the first token in the production
   int productionID;   // a unique integer that identifies the production
   ArrayList<Production> childNodes;   // duh
   Production mother;  // mother node (may be null)

   public Production (Token leading, int productionID)
   {
      this.leading      = leading;
      this.productionID = productionID;
      childNodes        = new ArrayList<Production>();
   }

   public void append (Production child)   // add a new child node
   {
      child nodes.add(child);
      child.mother = this;
   }

   public abstract void build1 (TokenSet follow, TokenSet anchor);   // implements pass 1
   public abstract void build2 ....

}

但更强大的方法是在每个生成中派生一个新的子类,并将子节点声明为字段变量。然后我们可以消除productionID并使用instanceof检查。您甚至可以在定义符号的给定子类上实现符号接口,并将该节点直接插入到符号表中;定义嵌套作用域的作品也可以有自己的符号表(我不会在这里做)。关键是这样,句法和语义分析都可以集成到解析树结构中,甚至也可以集成到最终翻译中。唯一的缺点是那些可怕的java接口:lol:

例如,我们可以将Modula-2头文件声明为:

// i wont bother with imports since this isnt real code

class DefinitionModule extends Production
{
   Identifier name;

   ArrayList<ImportClause> importClauses;
   ArrayList<ExportClause> exportClauses;
   ArrayList<Production>   itemList;   // CONST-,TYPE-, & VAR- declarators & function headers

   public DefinitionModule()   // no more productionID
   {
      super(lastTokenRead());   // always sits on DEFINITION
      importClauses = new ArrayList<ImportClause>;

   }


   // build()
   //
   // DefinitionModule ::= DEFINITION MODULE Identifier ";" {ImportClause}{ExportClause}{HeaderItem} END Identifier
   //
   // where HeaderItem ::= ConstDeclarator | TypeDeclarator | VarDeclator | ProcedureHeader.
   // Identifier, ImportClause, & ExportClause below are all derived from
   // Production, above



   public void build (TokenSet follow, TokenSet anchor)
   {
      Scanner.getToken();   // skip the DEFINITION
      Scanner.expectToken(Token.ID_MODULE);    // make sure MODULE is there & then skip it
      name = name.build(new TokenSet(Token.ID_SEMICOLON));
      expectToken(Token.ID_SEMICOLON);
      while (lastTokenRead()==Token.ID_IMPORT || lastTokenRead()==Token.ID_FROM)
      {
         ImportClause IC = new ImportClause(lastTokenRead());
         importClauses.add(IC.build(new TokenSet(Token.ID_SEMICOLON));
         Scanner.expectToken(Token.ID_SEMICOLON);
      }

      while (lastTokenRead()==Token.ID_EXPORT)
      {
         ExportClause XC = new ExportClause(lastTokenRead());
         exportClauses.add(XC.build(new TokenSet(Token.ID_SEMICOLON));
         Scanner.expectToken(Token.ID_SEMICOLON);
      }

      // etc, etc, etc
   }
}

如果你这样做,编译器将围绕语言的功能而不是编译器的传统通道构建自己。

祝好运...

以上是关于编译器构造:显式解析树的主要内容,如果未能解决你的问题,请参考以下文章

自己动手写编译器:手动构造语法树实现中间代码生成

自己动手写编译器:通过语法编译构建语法树并实现中间代码生成

无法解析片段中的 ViewModelProvider 构造?

C++显式构造函数 [翻译]

为啥为非泛型方法或构造函数提供显式类型参数会编译?

c++类的构造函数不显式声明会自动生成吗