语义分析程序 c语言,语义分析的C语言代码

之前写了12篇使用有限自动机(DFA)分析语法的文章,今天说一下语义分析。

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

怎么用C语言写语法分析

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

语义分析,也是编译器前端的一个模块。

一般来说,它比语法分析要简单。

程序源码的各种复杂逻辑,在经过语法分析之后,就成了一棵抽象语法树(AST)。

生成这个语法树比较难,因为源代码里互相耦合的概念太多。

生成语法树之后,把树遍历一次,就可以完成语义分析。

不同的语句和运算符,构成了语法树的不同节点。不同的节点有不同的处理函数。在处理函数里检查类型是否一致、计算中间结果、化简常量表达式、添加类型的自动转换(type cast)。

之后,就可以生成三地址码了。从生成三地址码开始,已经是编译器的后端了。

e0236e0a7610c3c345a5af439e964476.png

如上图,是语法树的节点数据结构。

因为是多叉树,所以有个父节点的指针parent,子节点组成一个指针数组,由nodes和nb_nodes表示。

31行的scf_operator_t* op,表示节点的操作符。不管是语句还是运算符,一律看作一种操作符。

把所有的操作符组成一个大数组,就是支持的所有语义。

(把人能看懂的源码文本,终于变成了机器能处理的数据结构了)

6fad23a42e5d3ca9043952e077f6681d.png

第1列,用一个整数表示操作符的类型。

第2列,是操作符的名字字符串。

第3列,是运算优先级。

小括号表示的表达式,优先级最高的,语句的优先级最低。先乘除,后加减。

第4列,是操作数(子节点)的个数。双目运算符是2个,单目是1个,语句的子节点个数不确定(设置为-1)。

第5列,是结合性。大多数都是左结合,单目运算符和赋值运算符是右结合。

ee99384ec97b0aaea2607a97cdb7c631.png

776f877bf74ea31f4d08a5c2bd0397c4.png

整个语义分析,分多步处理,每一步给它设置一个处理函数句柄。如下图:

9ccb2b091f9a96c62fb6d9e0aa4bb3e2.png

type,就是操作符的类型。

func,就是处理函数的指针。

第8行的最后一个参数void* data,是处理函数需要的私有数据。

把每一步的处理函数句柄也组成一个大数组,如下:

9c1156316a238db240167c7ee52d078d.png

最后一列,就是每类节点的处理函数。

语义分析比语法分析简单,因为语义分析是个填空题,语法分析是个问答题。

想多支持一个运算符,就在这里填个空就行(笑)。不像语法,多加个符号就可能出现二义性了。

接下来,以表达式的语义分析做个例子:

d50f45d38d4448f5f11a090136c8faa2.png

表达式只有一个子节点,这个子节点的result字段是变量(scf_variable_t类型的指针),用于存放计算的中间结果(实际在生成机器码的情况下,主要是记录中间结果的类型)。

它调用_scf_expr_calculate_internal()函数,递归处理表达式。

bf684200c97b334c035897cf2edf4034.png

递归结束的时候,叶子节点有可能是变量,也有可能是标号(goto 语句)。

编程语言里的表达式,是扩展了的表达式,比四则运算复杂一点。

603ee49fd5507b98bf4bc46c3bb20192.png

如果不是叶子节点,那么就一定是操作符(运算符或语句)。

a6ee1dbf0517e0062f3b87fa4ee3a490.png

417-426行,如果有子节点,就递归处理。

如果是左结合,子节点的处理顺序从0 ~ nb_nodes - 1。

如果是右结合,子节点的处理顺序从nb_nodes - 1 ~ 0。

处理完子节点之后,再处理父节点。语法树上,父节点的运算优先级肯定比子节点低。

处理父节点时,要先查找它当前步骤需要的处理函数句柄,然后调用。

e152dd345259f517086054bf166c2e30.png

实际上,语法树的处理,类似一个简单的计算器。

在简单计算器的代码上,稍微修改一下就可以做语义分析。

难写的还是语法分析,从源码开始生成这个语法树。

再看看加法的语义分析:

821282312302e3a1138bce2beea1efb1.png

加法是双目运算符,分析代码可以和减法通用。

首先,获取它的两个操作数变量:v0和v1。

然后,如果这两个变量至少有1个是结构体或类的指针,那么按运算符重载处理。

如果没有定义重载函数,则当作普通指针的加减运算。

d0885de5a968583b44c67ce07523d784.png

不是类的对象指针的情况下,就只能是整数或浮点数。

指针,也属于无符号整数。

如果第一个变量是指针,那么第二个变量要么是同类型的指针,要么是个整数。否则,报错类型不一致。

类型检查,都在这个阶段处理。

如果可以自动升级,就自动升级(例如32位整数扩展到64位)。

指针运算的结果还是指针,结果类型与第一个变量一致。包括是否为const指针。

a393d970382145809cfb6739cab554ee.png

如果第二个变量是指针,第一个变量就要是整数,与上面的类似。

如果两个变量类型相同,不管它们是什么类型,都是合法的运算。

47fad63b1b5fa088a24bc432eedbda06.png

如果类型不同,则自动类型升级,查找一个可以升级的类型。找不到则报错,提示程序员自己处理。找到的情况下,自动添加type cast节点。

a41da8a8b50d1ec15df76faa4ae6897f.png

最后,生成一个结果变量,用于记录运算结果的类型。

if语句的语义分析:

1976c855ee5ca21316fd548be7a66a7a.png

if语句的条件表达式,计算结果必须是一个整数(0表示false,非0表示true)。

如果计算出浮点数来,一般表示程序有BUG,可以直接报错。

d0195443723d808315576cdd712a3016.png

然后分析if的主体部分,主体部分有可能嵌套各种语句类型,递归分析。

实际上,语义分析是特别无聊的代码,很多运算符的代码都是大同小异。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值