Go语言底层原理剖析
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.4 语法解析

词法解析阶段结束后,需要根据Go语言中指定的语法对符号化后的Go文件进行解析。Go语言采用了标准的自上而下的递归下降(Top-Down Recursive-Descent)算法,以简单高效的方式完成无须回溯的语法扫描,核心算法位于syntax/nodes.go及syntax/parser.go中。图1-4为Go语言编译器对文件进行语法解析的示意图。在一个Go源文件中主要有包导入声明(import)、静态常量(const)、类型声明(type)、变量声明(var)及函数声明。

图1-4 Go语言编译器对文件进行语法解析的示意图

源文件中的每一种声明都有对应的语法,递归下降通过识别初始的标识符,例如_const,采用对应的语法进行解析。这种方式能够较快地解析并识别可能出现的语法错误。每一种声明语法在Go语言规范中都有定义[2]

函数声明是文件中最复杂的一类语法,因为在函数体的内部可能有多种声明、赋值(例如:=)、表达式及函数调用等。例如defer语法为defer Expression,其后必须跟一个函数或方法。每一种声明语法或者表达式都有对应的结构体,例如a:=b+f(89)对应的结构体为赋值声明AssignStmt。Op代表当前的操作符,即“:=”,Lhs与Rhs分别代表左右两个表达式。

语法解析丢弃了一些不重要的标识符,例如括号“(”,并将语义存储到了对应的结构体中。语法声明的结构体拥有对应的层次结构,这是构建抽象语法树的基础。图1-5为a:=b+c(12)语句被语法解析后转换为对应的syntax.AssignStmt结构体之后的情形。最顶层的Op操作符为token.Def(:=)。Lhs表达式类型为标识符syntax.Name,值为标识符“a”。Rhs表达式为syntax.Operator加法运算。加法运算左边为标识符“b”,右边为函数调用表达式,类型为CallExpr。其中,函数名c的类型为syntax.Name,参数为常量类型syntax.BasicLit,代表数字12。

图1-5 特定表达式的语法解析示例