![Go语言底层原理剖析](https://wfqqreader-1252317822.image.myqcloud.com/cover/131/40795131/b_40795131.jpg)
1.4 语法解析
词法解析阶段结束后,需要根据Go语言中指定的语法对符号化后的Go文件进行解析。Go语言采用了标准的自上而下的递归下降(Top-Down Recursive-Descent)算法,以简单高效的方式完成无须回溯的语法扫描,核心算法位于syntax/nodes.go及syntax/parser.go中。图1-4为Go语言编译器对文件进行语法解析的示意图。在一个Go源文件中主要有包导入声明(import)、静态常量(const)、类型声明(type)、变量声明(var)及函数声明。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_22_1.jpg?sign=1738882359-YTatbr6wbtRCO63Zoq8KHs2vej5y8PfD-0-3b6d0499b6ae80e014b29eba9de654b4)
图1-4 Go语言编译器对文件进行语法解析的示意图
源文件中的每一种声明都有对应的语法,递归下降通过识别初始的标识符,例如_const,采用对应的语法进行解析。这种方式能够较快地解析并识别可能出现的语法错误。每一种声明语法在Go语言规范中都有定义[2]。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_22_2.jpg?sign=1738882359-rMpKvCxm7pHgsxfN0Ong4xx02I1U9i81-0-475c10ce4e3c640103ebf09f0794b89e)
函数声明是文件中最复杂的一类语法,因为在函数体的内部可能有多种声明、赋值(例如:=)、表达式及函数调用等。例如defer语法为defer Expression,其后必须跟一个函数或方法。每一种声明语法或者表达式都有对应的结构体,例如a:=b+f(89)对应的结构体为赋值声明AssignStmt。Op代表当前的操作符,即“:=”,Lhs与Rhs分别代表左右两个表达式。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_22_3.jpg?sign=1738882359-Rc0iXx7zcXF2kV5WsnQYSWUiDjfR413H-0-5555230ab100522d00c83c25ef1d85e3)
语法解析丢弃了一些不重要的标识符,例如括号“(”,并将语义存储到了对应的结构体中。语法声明的结构体拥有对应的层次结构,这是构建抽象语法树的基础。图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。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_23_1.jpg?sign=1738882359-fvfOgkzzNGQ3z1MdR1M4kNH6wvRp90B8-0-854bc217694977b3a9bad97884f3ff8c)
图1-5 特定表达式的语法解析示例