手把手教你做一个 C 语言编译器(5):变量定义

本章中我们用 EBNF 来大致描述我们实现的 C 语言的文法,并实现其中解析变量定义部分。

由于语法分析本身比较复杂,所以我们将它拆分成 3 个部分进行讲解,分别是:变量定义、函数定义、表达式。

本系列:

  1. 手把手教你做一个 C 语言编译器(0):前言
  2. 手把手教你做一个 C 语言编译器(1):设计
  3. 手把手教你做一个 C 语言编译器(2):虚拟机
  4. 手把手教你做一个 C 语言编译器(3):词法分析器
  5. 手把手教你做一个 C 语言编译器(4):递归下降

EBNF 表示

EBNF 是对前一章提到的 BNF 的扩展,它的语法更容易理解,实现起来也更直观。但真正看起来还是很烦,如果不想看可以跳过。

其中 expression 相关的内容我们放到后面解释,主要原因是我们的语言不支持跨函数递归,而为了实现自举,实际上我们也不能使用递归(亏我们说了一章的递归下降)。

P.S. 我是先写程序再总结上面的文法,所以实际上它们间的对应关系并不是特别明显。

解析变量的定义

本章要讲解的就是上节文法中的 enum_decl 和 variable_decl 部分。

program()

首先是之前定义过的 program 函数,将它改成:

我知道 global_declaration 函数还没有出现过,但没有关系,采用自顶向下的编写方法就是要不断地实现我们需要的内容。下面是 global_declaration 函数的内容:

global_declaration()

即全局的定义语句,包括变量定义,类型定义(只支持枚举)及函数定义。代码如下:

看了上面的代码,能大概理解吗?这里我们讲解其中的一些细节。

向前看标记 :其中的 if (token == xxx) 语句就是用来向前查看标记以确定使用哪一个产生式,例如只要遇到 enum 我们就知道是需要解析枚举类型。而如果只解析到类型,如 int identifier 时我们并不能确定 identifier 是一个普通的变量还是一个函数,所以还需要继续查看后续的标记,如果遇到 ( 则可以断定是函数了,反之则是变量。

变量类型的表示 :我们的编译器支持指针类型,那意味着也支持指针的指针,如 int **data;。那么我们如何表示指针类型呢?前文中我们定义了支持的类型:

所以一个类型首先有基本类型,如 CHAR 或 INT,当它是一个指向基本类型的指针时,如int *data,我们就将它的类型加上 PTR 即代码中的:type = type + PTR;。同理,如果是指针的指针,则再加上 PTR

enum_declaration()

用于解析枚举类型的定义。主要的逻辑用于解析用逗号(,)分隔的变量,值得注意的是在编译器中如何保存枚举变量的信息。

即我们将该变量的类别设置成了 Num,这样它就成了全局的常量了,而注意到上节中,正常的全局变量的类别则是 Glo,类别信息在后面章节中解析 expression 会使用到。

其它

其中的 function_declaration 函数我们将放到下一章中讲解。match 函数是一个辅助函数:

它将 next 函数包装起来,如果不是预期的标记则报错并退出。

代码

本章的代码可以在 Github 上下载,也可以直接 clone

本章的代码还无法正常运行,因为还有许多功能没有实现,但如果有兴趣的话,可以自己先试着去实现它。

小结

本章的内容应该不难,除了开头的 EBNF 表达式可能相对不好理解一些,但如果你查看了 EBNF 的具体表示方法后就不难理解了。

剩下的内容就是按部就班地将 EBNF 的产生式转换成函数的过程,如果你理解了上一章中的内容,相信这部分也不难理解。

下一章中我们将介绍如何解析函数的定义,敬请期待。

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

任选一种支付方式

1 3 收藏 评论

关于作者:LotAbout

简介还没来得及写 :) 个人主页 · 我的文章 · 26 ·    

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部