如何访问 Q# 编译器生成的 AST?

How to access the AST generated by the Q# compiler?

背景

我正在从事的部分项目要求我分析 Q# 源代码并在遇到某些语法元素时执行特定操作。例如,假设我想计算整个程序中使用了多少种不同的门类型。现在,这可以通过遍历程序的抽象语法树并根据当前语法节点执行操作来实现。

我试过的

我从分析 qsharp-compiler 存储库开始,但是,编译器的内部工作原理缺乏在线文档,浏览所有 C# 和 F# 源代码真的很乏味。

当然,我可以为该语言编写自己的解析器,但这对于手头的任务来说可能有点矫枉过正。 必须 有一种从编译器内部提取 AST 的方法。

问题

是否可以使用 Q# 编译器以编程方式(从 C# 或 F#)编译 Q# 源代码,并提取内部 AST?

是的,完全可以通过编程方式编译 Q# 源代码。如果您想重复更新编译,这将特别有用 - 您可以 add/remove/edit(部分)内存中的源和引用,并查询有关编译当前状态的各种有用信息,例如IDE 关心(例如,在某个文件的特定位置定义了哪些符号)。

但是,如果您只想为 Q# 编译处理 AST,那么有一种更简单的方法! Q# 编译器具有我认为完全符合您需要的可扩展性机制。 此 blog post 简要概述了该功能。 还有一个example for an extension on the compiler repo. This readme (and possibly this one)也可能派上用场。我相信这回答了你一半的问题,即如何轻松访问内置的 AST。

根据我的理解,问题的另一半是如何方便地分析或转换AST。为此,还提供了一种机制;语法树转换框架。该框架由几个 class 元素组成,它们为不同类型的节点定义 walk/transformation,以及将它们连接在一起的包装 class。

与其从查看转换的定义开始,不如直接查看一些使用它的示例可能更直观。可以找到一个与您想要做的非常接近的示例 here. The implemented transformation adds a comment to each callable listing all identifiers used within the callable. It is invoked as as part of a compilation step (see here),它在我上面已经链接的示例中定义。

还有几个简单转换的好例子,它们与您想做的有点远,但如果您有兴趣,应该让您了解整个设置的工作原理:this one allows to attach attributes to callables, and this one 是用于内联变位(U*VU 形式的模式)。

最后但同样重要的是,Gitter for the Q# community 也可能是您工作时参与的好资源。