我们在语义分析阶段完成语法树节点类型的推导,并完成数组维度常量表达式的计算以及数组访问的修改。
定义类型
首先我们需要明确有哪些类型,我们简化的C语言只有如下类型:int, char, short, bool, long, double, float, void, pointer
。还可以通过组合组合出很多类型出来,比如多维的指针,多维的数组
关于类型定义相关代码,请参照
https://github.com/huanghongxun/Compiler/blob/master/compiler/type_base.h
https://github.com/huanghongxun/Compiler/blob/master/compiler/type_primitive.h
https://github.com/huanghongxun/Compiler/blob/master/compiler/type_array.h
https://github.com/huanghongxun/Compiler/blob/master/compiler/type_pointer.h
我们对每个类型起一个标记符方便我们判断类型是否相等,比如int
的助记符是i
,const int
的助记符是Ki
,double
的助记符是d
,int[32][32]
的助记符是A32_A32_i
,int const*const*
的助记符是PKPKi
(P
表示指针,K
表示指向常量,所以意思是[指向[指向[常整数]的常量指针]的指针])等。这个助记符的由来可以使用c++的typeid(i).name()
查看。
对于typedef
,仅仅是一个alias而已,我们在记录类型的时候记录一下就好。
类型推导
C语言是强类型语言,我们在操作变量的时候必须确认运算符两侧的类型是否合法,比如指针不能和指针相加,但可以相减,结构体和其他类型的变量都不能运算,布尔类型以及无符号类型不能取负,整数与整数的除法和浮点数的除法的行为不同等。有这么多理由,我们必须要实现类型推导。
我们在初期可以限定类型必须为int
来跳过类型推导以便迅速实现一个可用的解释器,不过要实现更高级的功能类型推导是不可缺少的。
首先我们一开始已经能知道语法树部分节点的类型,一般是常量和变量,还有函数调用的返回值的类型。我们根据这些已知的类型进行推导。
二元运算符的类型推导
对于一个语法树节点,如果我们知道节点子树的类型和运算符(这里只讨论二元运算符),那么会有2种情况,分别是类型相同和类型不同。对于类型不同的情况,我们需要做类型转换使得类型匹配,我们在匹配类型的时候先推导出两种类型的公共超类型,比如int
和double
的公共超类型是double
,char
和bool
的公共超类型是int
(小于int
的类型计算都规约到int
再计算)(经过我的试验发现long long int