c语言语法语义大全,用C语言写语法分析9,变量声明的分析

变量声明,是强类型编程语言中的关键语句。

强类型语言之所以叫强类型,就是它对变量的类型检查比较严格。其中一点就是变量在使用前必须先声明(类类型也一样),另一点就是在自动类型升级不可用时,需要明确使用强制类型转换(type cast)。

这点上C++的语法检查很严格,会提示error信息,而C语言一般只提示warning信息。

变量声明语句以基本类型的关键字,或者类类型的标志符开始,之后跟变量的标志符。

变量的标志符之后可以跟中括号,声明数组。

变量的标志符之前,可以跟星号声明指针,多个星号表示多级指针。

然后可以跟赋值运算符=,后接初始化表达式。例如,int a = 1;

当然也可能这样,int a = 1, b = 2, *p;

173f259821ed33c2d7875dadbef0d724.png

变量声明的语法还是比较复杂的。

为了代码的清晰可读,最好还是一行只声明一个变量。但是,语法分析必须可以正常处理int a = 1, b = 2, *p;这样的语句。

下图是标志符模块的代码:

c34feae1313640932588abfd0316baf0.png

在之前(关于词法分析)的文章里说过,标志符在源代码中具有多义性,必须到语法分析时才可以确定语义。

用C语言实现一个真正的词法分析器

词法分析器的简单思路

在词法分析中,会把关键字之外的字符序列都叫做标志符,并且给它们编号。

编号从SCF_LEX_WORD_ID开始,所以type大于等于这个值的单词都是标志符。

cbac88005df935ec0333a9e3fa01eb41.png

单纯的一个标志符没法确定语义,先把它存在一个栈里,继续分析。它有可能是函数、类型、变量。

接下来看类型模块:

03278db31b1e9a423da899ffa38aa814.png

类型可以是基本类型,也可以是类类型(前面的关键字struct或class可以省略,也可以带着)。星号可以没有,也可以有多个。具体的语法编辑见上图。

51a01d685381ea2ac2b012d1461791b9.png

基本类型base_type的action函数:

a44d154c26aba39a0219cd1ea2e71606.png

如果是基本类型,那么可以在ast语法树中找到它的数据结构。在语法分析开始之前,我们就把基本类型添加到了语法树的根节点(全局作用域)。

type模块的标志符的action函数:

6c972220ea962b7c336de9acc70e51ea.png

在编辑type模块的语法时,我们把identity模块的identity作为类型名,把type模块的identity作为变量名。其他情况下,一般引用identity模块的identity。

标志符的语义很复杂,还有可能是变量和函数。

当连续两个标志符出现的时候,前面那个肯定是类型。

如果这时候栈不空(前一个标志符已存在),就可以使用_type_find_type()函数查找所需的类型(确定前一个标志符的语义)。

不管栈空不空,当前标志符的语义是没法确定的。因为可以是变量,或函数,例如:

class A {};

A* p;

A* p();

p到底是函数还是变量,没法确定,必须等待后续的分析。

_type_find_type()函数:

13b33abe6462c2d33758184cb9624030.png

我们优先当作类型查找,然后当作函数查找,所以类型名和函数名不能一样。为了简单,先这么规定。

3378378a0da359e4d86623ff1f784b98.png

星号的action函数:

3922387d2017dfa1fa1e5b4a003163ae.png

当出现星号时,它前面的标志符肯定是类型。要记录星号出现的次数,每一个表示一级指针。

乘号只能出现在表达式里,所以这里出现的肯定是星号,当作指针处理。

变量声明模块:

da74f9860872b5fd97aecfc4c036c014.png

变量声明模块的dfa节点不多,主要使用其他模块的节点,例如identity模块和type模块。

但是它的语法很复杂,如下:

206150d8a5733e22f0b7ed6d4193f2bf.png

type模块的identity,实际上是变量的标志符。

28c82457e8f2227b27ab18fceea33892.png

360-366行,是数组的声明,变量之后跟左中括号,然后是表示数组大小的表达式(可以省略,根据初始化表达式自动计算)。

右中括号再接左中括号,就可以递归分析多维数组。例如int a[ 2] [ 2],黑体的两个字符就是。这个递归机制由dfa框架实现:说说字符串处理和有限自动机

之后可以是逗号,继续声明其他变量,也可以跟分号结束。

369-370,是变量或数组变量的初始化,跟赋值运算符=。

373-375,跟new关键字创建类的对象。

之后是普通变量的初始化,和结构体或数组的初始化。

左中括号的action函数:

29b5cd0eee22775b2593a74afd505fbb.png

当发现了左中括号之后,它前面的就是变量。这时运行函数_var_add_var()把它加入语法树。

在这之前,没法确定这个标志符是不是变量。

到了这里,可以确定它是数组变量,所以要准备记录数组的维数和每一维的大小,暂时设置为-1。

挂载hook,检查右中括号。

右中括号的action函数:

9cec7016e0c9e0f5b3c74d817c1694d8.png

右中括号出现的时候,说明表示数组当前维的大小的表达式分析完了,使用scf_expr_calculate()函数把它算出来。

赋值符号的action函数:

aad24ce3ea6ff298defd5e2eb3533df9.png

出现赋值的时候,之后跟初始化表达式。

先申请一个表达式,把变量和等号先添加进去,然后分析初始化表达式。

逗号的action函数:

cbf262eed1c4196267fc82e754f1d066.png

逗号出现,说明前面的分析完了,后续还有同一个类型的变量、指针、或数组。

把变量和初始化表达式加到语法树,如果有的话。

分号的action函数:

4a262dcff921ba6f58dc45c64742dd52.png

分号出现,说明真分析结束了。也把变量或初始化表达式加入语法树,然后返回OK。

最后,看看添加变量的函数_var_add_var():

eb3e97f7eca5690129715bfcb8c5729e.png

变量不能在同一个作用域里重复声明,58行是检查这个的。

往上查找所属的函数或者类,69-74行。

在函数里声明的是局部变量,在类里声明的是成员变量,其他的是全局变量:加static为文件作用域,不加为全局作用域。

如果在类里加static,则是类的静态变量。

515f5cfd0f05bb05e9613b6fff3019e5.png

95-105行,如果是声明的类的成员变量,它的类型不能递归。例如:

struct S {

struct S s; //成员变量的类型递归了,没法计算结构体的大小,语法错误。

struct S* next; //可以定义同类型的成员指针,因为指针大小是固定的。

ae654868682e6666e0ef0c03d96f0d25.png

最后是为这个变量生成一个结构,scf_variable_t,添加到语法树。

判断类型递归的函数:

94cba62c6dc6c5e9d3ae71761f96366a.png

想了解更多精彩内容,快来关注闲聊代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值