佛山市拓洲网络科技有限公司
拓洲科技,贴近未来

软件开发语言Go的代码是怎么样编译成机器码的之二:Parser阶段

在上一篇文章中,我们大致了解了Go 1.11 的编译器,是如何地把Go的软件开发项目的源代码编译成执行文件的整个过程,以及Scanner阶段的深入了解。

在这篇文章当中,我们会继续深入了解整个过程中的第二步,Parser阶段。

Parser

在Scanner扫描软件项目的源代码后,它将被传递给解析器Parser。解析器Parser是编译器的一个阶段,它将标记Token转换为抽象语法树(AST)。AST是源代码的结构化表示。在AST中,我们将能够看到程序结构,例如函数和常量声明。

Go再次为我们提供了解析程序并查看AST的包:go / parser go / ast 。我们可以这样使用它们来打印完整的AST:

输出:

在此输出中,您可以看到有关该程序的一些信息。在Decls 字段中,有一个文件中所有声明的列表,例如导入,常量,变量和函数。在这个例子的情况下,我们只有两个:我们导入fmt 包和main函数。

为了进一步消化它,我们可以看一下这个图,它是上述数据的表示,但只包含类型,红色代表与节点对应的代码:

主要功能由三部分组成:名称,声明和正文。该名称表示为值为main的标识符。由Type 字段指定的声明将包含参数列表和返回类型(如果我们指定了any)。正文包含一个包含我们程序所有行的语句列表,在这种情况下只有一行。

我们的单个fmt.Println 语句由AST中的很多部分组成。该语句是一个ExprStmt ,它表示一个表达式,例如,它可以是一个函数调用,就像它在这里一样,或者它可以是文字,二进制操作(例如加法和减法),一元操作(用于实例否定一个数字)等等。可以在函数调用的参数中使用的任何东西都是表达式。

我们的ExprStmt 包含一个CallExpr ,它是我们实际的函数调用。这又包括几个部分,其中最重要的部分是Fun Args 。Fun包含对函数调用的引用,在这种情况下,它是一个SelectorExpr ,因为我们从fmt包中选择Println 标识符。但是,在AST中,编译器还不知道fmt 是一个包,它也可能是AST中的一个变量。

Args包含一个表达式列表,它是函数的参数。在这种情况下,我们将一个文字字符串传递给函数,因此它由一个类型为STRING BasicLit 表示。

很明显,我们能够从AST中推断出很多。这意味着我们还可以进一步检查AST并查找文件中的所有函数调用。为此,我们将使用ast 包中的Inspect 函数。此函数将递归遍历树,并允许我们检查来自所有节点的信息。

要提取所有函数调用,我们将使用以下代码:

我们在这里做的是查找所有节点以及它们是否为* ast.CallExpr 类型,我们刚才看到它代表了我们的函数调用。如果是,我们将使用printer包打印Fun 成员中存在的函数的名称。

此代码的输出将是:

fmt.Println

这确实是我们简单程序中唯一的函数调用,所以我们确实找到了所有函数调用。

构建AST后,将使用GOPATH或Go 1.11及更高版本的module解析所有导入。然后,将检查类型,并应用一些初步优化,使得编译后的程序执行得更快。


邮箱:vyloy@yiqishare.com

地址:广东省佛山市顺德区容桂街道高黎村19号首层之二 邮编:528303

Copyright © 2014-2018 yiqishare.com. All rights reserved. 粤ICP备15033752号