c语言 函数调用 实参 指针,用C语言写语法分析12,函数调用的分析

函数调用,是编程语言的一个重要语法功能。

一般以函数名加小括号表示,小括号内添加实参列表(如果有参数的话)。

每一个实参,都是一个表达式,多个参数之间以逗号分隔。

函数调用语句,我们也可以把它看作一个特殊的表达式。它可以作为表达式的子表达式存在,它的实参表达式里也可以含有另一个函数调用表达式,即函数可以嵌套调用。

例如,add(1, sub(2, mul(3, 5))),这个函数调用需要逐层的递归分析。

2fab75b49e29ca980f21350ba653d7fe.png

函数调用模块的上下文见上图,每一层函数调用都生成这么一个上下文,嵌套的函数调用通过栈来处理。

这个栈,作为该模块的模块数据,见下图的init_module函数。

ae0a58d2cff40a450de52e4a6be9b9e6.png

它的dfa节点如下:

lp,左小括号,表示函数调用的开始,它前面是函数名。

rp,右小括号,表示函数调用的结束,它之后可以跟分号、逗号、或二元运算符。

comma,逗号,在这里用于分隔实参列表。

lp_stat,用于统计左小括号的个数,与右小括号要匹配。因为实参表达式里也可能出现小括号,这个统计是必要的。

093243dd7c5ee6fa869a379cc4f7dd57.png

语法的编辑规则如上图,305-308行,expr与comma互相连接组成实参列表的递归分析,最后以右小括号结束。

298-303行,是参数里含有new运算符的分析。

统计小括号个数的action函数:

e126c33499f004073fb6f08e78627288.png

它是作为dfa hook来运作的,当出现左小括号时触发,并统计当前函数调用中的左小括号个数。

左小括号的action函数:

e71fdbfdb3f53cdce2790f3c0e099071.png

a98d26e6b35e5ef575308de5e473e850.png

标志符之后跟着小括号,表示函数调用,标志符是被调函数的名字。

这个函数必须能够用函数名在ast语法树上找到,否则要报错:函数未声明。

如果调用的是外部函数,函数的声明必须放在调用代码之前的某个位置,以保证编译器可以找到。C/C++都是放在头文件里,直接包含头文件就行。

不管是通过函数名的直接调用,还是通过函数指针的间接调用,我们都当作函数指针来处理。

只是给前者设置const标志,表示直接调用的函数不会变化,在生成二进制码时可以给它生成一个重定位符号(re-location symbol)。

重定位符号,可以被连接器(Linker)替换为函数的实际地址。

708c74f41f8e25c912b3c43042dac3c4.png

给这个函数指针变量生成一个ast节点:node_pf。

53917ad38f712c8734406a3ca24a72bc.png

然后生成一个函数调用的call节点,它的类型是SCF_OP_CALL,表示函数调用语句。

它的第一个子节点,是被调函数的指针(直接调用加上const标志)。

申请一个dfa_call_data_t的上下文,把这些信息存下来,然后入栈。栈顶永远是当前函数调用的上下文。

添加3个hook,分别检测右小括号、逗号、左小括号。哪个出现的最晚,哪个在最前面,因为hook链表也是栈结构。

逗号的action函数:

56b39029659d450dd064d53caadfddf1.png

逗号出现时,说明分析完了一个实参的表达式。

把它添加到函数调用上下文(在栈顶)的参数数组argv里。

切换到这个dfa节点,继续分析下一个参数,所以给dfa框架返回DFA_SWITCH_TO。

右小括号的action函数:

55a488b6f8b50f6987d4b31616ae22fb.png

右小括号出现时,要比较左右小括号的个数是否匹配。如果匹配,说明函数调用的参数分析完了。不匹配,则继续分析。

0516a175e5f8576f5bea3449da761bbe.png

167-172行,如果这个函数调用是某个表达式的子表达式,则把它添加到母表达式里。

174-187行,添加各个参数到call节点。

然后把它设置为当前的表达式d->expr,因为不确定它是子表达式,还是某个顺序块的单独语句(以分号结尾)。

函数调用模块,实际被当作表达式模块的子模块。它的语法实际被编辑在dfa expr模块的798-802行,可以查看之前的两篇文章:

怎么用C语言写语法分析3,基于有限自动机的表达式分析

怎么用C语言写语法分析4,表达式分析的代码

3e6982456ad3db1087a8b68ab9156dac.png

写完今天这篇,编译器里复杂的语法分析部分就写完了。

当然并不完善,没有包括发现语法错误时怎么打印提示信息,以及怎么继续分析。

否则发现一个错误就会终止,实际的编译器都是发现多个错误后再终止,这样可以一次打印很多错误信息。

(有兴趣的可以自己添加)

接下来的文章,继续说语义分析。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值