首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在C#中编写抽象语法树的访问者模式?

如何在C#中编写抽象语法树的访问者模式?
EN

Stack Overflow用户
提问于 2013-04-23 17:23:21
回答 1查看 8.7K关注 0票数 17

我必须编写一个访问者模式来导航AST。有没有人能告诉我更多,我该如何开始写?据我所知,AST中的每个节点都会有As ()方法(?)会以某种方式被调用(从哪里?)。这就是我的理解。为了简化一切,假设我有节点Root、Expression、Number、Op,树看起来像这样:

代码语言:javascript
运行
复制
      Root
        |
       Op(+)
      /   \
     /     \
 Number(5)  \
             Op(*)
             /   \
            /     \
           /       \
       Number(2)   Number(444)
EN

回答 1

Stack Overflow用户

发布于 2013-08-04 03:00:20

模式访问者是一种设计模式,它允许您在解析树上实现任意操作(实现为访问者)。类型检查),而不必修改解析树的节点的实现。

它可以通过以下方式实现(我使用的是伪代码):

首先,您需要定义所有节点都必须实现的树节点的基类。

代码语言:javascript
运行
复制
abstract class VisitableNode {
   abstract void accept( Visitor v );
}

节点类必须实现的唯一方法是accept方法。

然后,您应该定义解析树的访问者节点的基类。

代码语言:javascript
运行
复制
abstract class Visitor {
   abstract void visit( Root rootNode );
   abstract void visit( Op opNode );
   abstract void visit( Number number );
}

请注意,访问者的基类仅用于解析树,并且应该为您在解析树中定义的每种节点类型提供一个访问方法。

然后,您应该让您的节点类实现以以下方式扩展VisitableNode类:

代码语言:javascript
运行
复制
class Root : VisitableNode {
   [...]
   void accept( Visitor v ) {
      v.visit(this);
   }
}

class Op : VisitableNode {
   [...]
   void accept( Visitor v ) {
      v.visit(this);
   }
}

class Number : VisitableNode {
   [...]
   void accept( Visitor v ) {
      v.visit(this);
   }
}

现在,您有了不应更改的解析树结构,并且可以随意实现任意数量的访问者(操作)。

为了进行类型检查,您必须将一个类型与值一起存储在Number类中,或者为您支持的每个类型创建一个Number类: NumberFloat、NumberInteger、NumberDouble等。

例如,假设您有一种方法可以从Number类中推断出值的静态类型。

我还假设您可以通过getChild(int childIndex)方法访问节点的子节点。

最后,我将使用一个类类型,它简单地表示您打算支持的静态类型(如Float、Integer等)。

代码语言:javascript
运行
复制
class TypeCheckVisitor : Visitor {

   // Field used to save resulting type of a visit
   Type resultType;


   void visit( Root rootNode )
   {
      rootNode.getChild(0).accept( this );
   }

   void visit( Op opNode )
   {
      opNode.getChild(0).accept( this );
      Type type1 = resultType;

      opNode.getChild(1).accept( this );
      Type type2 = resultType;

      // Type check
      if( !type1.isCompatible( type2 ) ){
         // Produce type error
      }

      // Saves the return type of this OP (ex. Int + Int would return Int)
      resultType = typeTableLookup( opNode.getOperator(), type1, type2 );
   }

   void visit( Number number )
   {
      // Saves the type of this number as result
      resultType = number.getType();
   }
}

然后,您可以将Type类实现为一个枚举,其方式类似于:

代码语言:javascript
运行
复制
enum Type {
   Double,
   Float,
   Integer;

   boolean isCompatible(Type type1, Type type2){
      // Lookup some type table to determine types compatibility
   }
}

最后,您只需要实现类型表和运算符表。

编辑:在访问递归中,使用要递归的节点的accept方法进行递归实际上是正确的。

至于用法,您可以通过以下方式对解析树的根节点执行类型检查(同时确定表达式的类型):

代码语言:javascript
运行
复制
TypeCheckVisitor v = new TypeCheckVisitor();
rootNode.accept( v );
print( "Root type is: " + v.resultType );

您还可以以同样的方式对解析树中不同于根的任意节点进行类型检查。

票数 34
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16165640

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档