友情提示:本文共有 1953 个字,阅读大概需要 4 分钟。
语法分析的目的,是构建抽象语法树(AST)。
在语法分析之前,需要先准备与AST有关的数据结构,也就是编程语言教程里经常提到的类型、变量、运算符、表达式、语句、函数、作用域等。
编译器内还多了一个,抽象语法树的节点。
这些数据结构设计起来复杂的地方在于,他们之间是有关联的,而且有时候还比较紧密。
怎么解耦合,是设计时的要点。
1,类型与变量,
类型声明了变量,变量也就有了类型。
类型与变量,是互相关联的。
乍一看是用类型去声明变量,但不是所有的类型都是基本类型,还有自定义的类。
类里也是可以有变量的,同时类还可以声明变量(即类的对象),类的对象还可以作为其他类的成员变量。
类型与变量,是在数据结构设计时,需要第一个解耦合的点。否则,就只能支持简单类型了,而没法支持复杂数据结构。
一般的办法就是,在类型type和变量variable的数据结构里都包含一个整数字段,当这个整数相同时,表示变量的基本类型是某个类型。
变量还可以是指针、数组等扩展类型,所以变量variable的数据结构里还需要记录是几级指针,数组的话还要同时记录数组的维数和每一维的大小。
如果变量声明时没有指定数组大小,则需要后续初始化时确定,可以先把数组大小设置为-1,然后留到语义分析时再回填。
如果是多维数组,没有指定的维数只能有一维,否则程序没法根据初始化数据确定数组的真实大小。
常量,就是设置了const标示的变量。因此,变量variable里需要有一个const标示。
variable还需要有一个literal标示用来区分是常量字面值,还是用const关键字声明的变量。
字面值是不能赋值的,但const指针是可以用同类型变量的地址给它赋值的,只是不能修改它的内容,即不能赋值解引用。
int a = 1;
const int * p = &a;
是可以的,但不能*p = 2;
这个检查可以留到语义分析时去做,语法分析以构建AST为主。
2,变量与表达式,
变量可以在声明之后紧跟着赋值运算符=进行初始化,初始化的数据可以来自于一个表达式expr。
表达式,是由变量、常量、运算符组成,这里就有了耦合。
一个可行的解决办法是,借助AST的节点。
变量声明,和变量初始化是两个步骤。
声明的变量只需要记录在当前作用域scope,并不需要生成运算代码,如果没有语句使用它的话。
变量的初始化,是一个含有赋值运算的表达式语句,是需要生成代码的。
需要生成代码,就需要为它生成一个AST节点,并且标示该节点是需要生成代码的。
表达式对应的是一个AST的节点,该节点的子节点就是表达式的内容,可以有多级子节点来表示复杂的表达式。
AST的节点,也可以只指向一个变量,它是表达式的叶子节点。这还解决了同一个变量被多个表达式多次使用的问题。
即,表达式的节点使用AST的节点node,然后用node指向变量,而不是直接使用变量本身。
变量的初始化表达式,是当前块的一个节点。变量本身,则记录在当前块的作用域里。
例如,如下代码,main函数的代码块里要有表达式a = 1的节点,它的作用域里要记录变量a和常量1。
int main()
{
int a = 1;
}
3,表达式与语句,
if语句的条件就是一个表达式,返回一个bool。
if块与else块,则是一个语句块,也是可以含有表达式的,还可以含有其他语句。
4,函数,
函数是一个特殊的节点,他含有一个作用域,和一个语句块。
在书写代码时,这两个是写到一起的,更符合人的读写习惯,但在语法分析时则是分开的。
因为一个变量不能多次声明,但可以多次使用,所以同一个作用域里的同名变量只能有一个,但指向该变量的节点可以有多个,并且在AST中出现在不同的位置。
5,节点的类型,
AST的节点node自然要有不同的类型,以分别对应函数、表达式、运算符、if语句、while语句、for语句,以及最常见的顺序语句,即顺序块block。
main函数的函数体就是一个顺序块,他里面可以包含各种分支和循环语句定义的其他块,形成复杂的程序流程。
6,AST所需的数据结构列表:
type ,variable,expr,block,scope,function,node。
function和block都有scope。
expr,block,function,都是特别的node。
type和variable,则隶属于不同的scope。
node是AST的基类,它还需要有指向父节点的指针parent,和子节点的指针列表childs。
7,运算符,
是一个单独的结构operator,里面记录运算符的类型,名字,优先级,结合性,等等。
在构成表达式时,和变量一样用node节点去指向它,只是运算符的node可以有子节点,且子节点个数等于运算符的目数。
单目运算符1个子节点,双目运算符2个子节点,etc.
本文如果对你有帮助,请点赞收藏《编译器入门 语法分析 数据结构》,同时在此感谢原作者。