9
// Create a scanner that reads from the input stream passed to us
 CSLexer lexer = new CSLexer(new ANTLRFileStream(f));
tokens.TokenSource = lexer;

// Create a parser that reads from the scanner
CSParser parser = new CSParser(tokens);

// start parsing at the compilationUnit rule
CSParser.compilation_unit_return x = parser.compilation_unit();
object ast = x.Tree;

我可以用 compiler_unit_return 类型的 x 做什么来提取它的根、它的类、它的方法等?我必须将其适配器提取出来吗?我怎么做?请注意,compile_unit_return 在我的 CSParser(由 ANTLR 自动生成)中是这样定义的:

public class compilation_unit_return : ParserRuleReturnScope
    {
        private object tree;
        override public object Tree
        {
            get { return tree; }
            set { tree = (object) value; }
        }
    };

但是我得到的树是对象类型。我使用调试器运行,似乎看到它属于 BaseTree 类型。但是BaseTree是一个接口!我不知道它与 BaseTree 有什么关系,也不知道如何从这棵树中提取细节。

我需要编写一个访问者,它访问了它的类、方法、变量等。 ParserRuleReturn 类从 RuleReturnScope 扩展并有一个开始和停止对象,我不知道它是什么。

此外,ANTLR 提供的 TreeVisitor 类看起来令人困惑。它需要一个适配器作为参数传递给它的构造函数(如果没有,它将使用默认的 CommonTreeAdaptor),这就是为什么我询问如何获取适配器早期的原因。还有其他问题。API可以参考http://www.antlr.org/api/CSharp/annotated.html

4

3 回答 3

6

您可以在文件顶部的语法选项中设置 AST 树类型,如下所示:

tree grammar CSharpTree;
options { 
    ASTLabelType = CommonTree
}

我会构建第 3 个语法或将其用于您现有的解析器语法,将树变成您创建的类。例如,假设您有一个与加号运算符匹配的规则,并且它是 2 个参数。您可以定义一个匹配该树的规则,以创建您编写的类,我们将其称为 PlusExpression,如下所示:

plusExpr returns [PlusExpression value]
   : ^(PLUS left=expr right=expr) { $value = new PlusExpression($left.value, $right.value); }

expr 将是您的语法匹配表达式中的另一条规则。left 和 right 只是树值的别名。{ } 之间的部分几乎被逐字转换为 C# 代码,除了替换变量引用。$left 和 $right 的 .value 属性来自创建它们的规则中指定的返回值。

于 2009-08-30T02:35:20.803 回答
3

我从来没有使用过 C# 中的 ANTLR,但是按照你到 API 的链接,BaseTree显然不是一个接口 - 它是一个,并且它具有公共属性:Type获取节点的类型,Text获取(我假设)对应的源文本到它,并Children获取子节点。走路还需要什么?

于 2009-08-18T00:32:45.413 回答
-2

如果我今天要制作一个 C# 编译器,我会这样做尝试作为第一次尝试:

  1. 从 ANTLR C# 3 目标开始(当然我在这里有偏见——说真的,您可以使用 CSharp2 或 CSharp3 目标)。
  2. 使用 .NET Framework 4 获取 Visual Studio 2010。这里的关键是 .NET 4,它是新的表达式树。
  3. 构建一个基本的组合解析器。尽可能在解析器中放置尽可能少的逻辑。它应该有很少的(如果有的话)动作,并且输出应该是一个可以用 LL(1) walker 遍历的未修饰的 AST。
  4. 构建一个树语法来遍历树并识别所有声明的类型。它还应该保留member_declaration子树以供以后使用。
  5. 构建一个遍历单个member_declaration并将成员添加到TypeBuilder. 跟踪方法主体,但不要深入了解它们。
  6. 构建一个遍历方法主体的 tree walker。生成一个Expression<TDelegate>匹配的方法,并使用CompileToMethod方法_我自己的 API(参见 Pavel 和我的评论)来生成 IL 代码。

如果您按此顺序执行操作,那么当您最终解析表达式(方法体、字段初始值设定项)时,您可以在类中使用像这样string的参数化方法来节省解析成员的工作。Expression

于 2009-08-18T00:41:16.417 回答