使用ANTLR构建自己的C#编译器:编译单元

//创建一个扫描程序,从传递给我们的输入流中读取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; 

我怎么能用compilation_unit_return类型的x来提取它的根,它的类,它的方法等? 我必须提取其适配器吗? 我怎么做? 请注意,compilation_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类看起来令人困惑。 它需要一个Adapter作为参数传递给它的构造函数(如果不是它将使用默认的CommonTreeAdaptor),这就是为什么我问abt如何获取Adapter eariler。 还有其他问题……对于API,你可以参考http://www.antlr.org/api/CSharp/annotated.html

现在我被震撼了……如果你知道任何事情,请帮忙。 太感谢了。

我从未使用过来自C#的ANTLR,但是根据你的API链接, BaseTree显然不是一个接口 – 它是一个类 ,它有公共属性:输入Type获取节点,获取Text (我假设)与之对应的源文本,以及获取子节点的Children 。 还有什么需要走的吗?

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

 tree grammar CSharpTree; options { ASTLabelType = CommonTree } 

我会构建第3个语法或将其用于现有的解析器语法,将树转换为您创建的类。 例如,假设您有一个与plus运算符匹配的规则,它是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属性来自它们创建的规则指定的返回值。

如果我今天要制作一个C#编译器,这就是我想要的 尝试作为第一次尝试:

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

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