简易的c语言编译器,实现简易的C语言编译器(part 2)

在上一部分中,我们分析并实现了词法分析的过程。这一部分,我们从头文件和宏定义两个方面入手,来分析前处理过程。

2.1 头文件

让我们先来看一段代码:

#include "stdio.h"

int main(int argc, char* argv[])

{

printf("Hello World!");

return 0;

}

在这段代码中,添加头文件的目的是为了引入函数int printf(char *fmt, ...);,也就是说大可以将上面代码改写成:

int printf(char *fmt, ...);

int main(int argc, char* argv[])

{

printf("Hello World!");

return 0;

}

理论上,经过替换后的这段代码编译没有任何问题,而这就是头文件替换的目的。但是,在没有编译之前,我们根本不知道这个函数里面调用了头文件的什么东西,可能是变量定义,也可能是某个或者多个函数,或者只是习惯性地包含了这个头文件。所以,我们需要将头文件内所有的内容全部展开到当前文件中来。这就非常好实现,只需要记住头文件的名字然后打开对应的文件,然后复制到头文件所在的地方就可以了。Python实现如下:

def modules(self):

result = ''

while self.current_char is not None and self.current_char.isalnum():

result += self.current_char

self.advance()

if result == 'include':

file_name = self.read_lines().strip()

file_name = file_name.lstrip('

file_name = file_name.rstrip('>')

file_name = file_name.replace(' ', '')

# current file path

file_dir = os.path.dirname(os.path.realpath(__file__))

file_name = os.path.join(file_dir, file_name)

try:

with open(file_name, 'r') as f:

new_text = f.read()

new_text += self.text[self.pos:]

self.text = new_text

self.pos = 0

except IOError:

raise Exception()

return self.get_next_token()

头文件引用里存在一个概念:循环依赖。比如,对于"car.h"和"wheel.h"两个文件。

// car.h

#include "wheel.h"

struct Car {

struct Wheel wheels[4];

...

};

// wheel.h

#include "car.h"

struct Wheel {

struct Car car;

...

};

由于两个文件内容相互包含,按照头文件展开的原则,势必会是一个死循环。可以采用前向声明加以避免,但是编译器必须能够及时检测出来。只需要增加一个对头文件内部的头文件计数上的辅助判断即可。

2.2 宏定义

C语言宏定义的规则为:

#define macro_name macro_string

定义之后,会用macro_string去替换代码中所有的macro_name。但是,由于macro_string可以是数字、表达式和函数,不单纯只是替换。还是从代码分析入手,先看下面这段代码中的宏定义及其使用:

#define FOO 0

#define ADD(X, Y) (X + Y)

int main()

{

return ADD(1, 2) - FOO;

}

对于FOO这个宏定义,只需要将后面代码中的同名标志符直接用0替换即可;而对于ADD这种函数式的宏,还需要进行实参的替换。最终,经过替换后的代码为:

int main()

{

return (1 + 2) - 0;

}

这样就实现了前处理过程。

我们使用python中的正则表达式进行内容的替换。其中,宏定义名、待替换的字符串和被替换的字符串分别如下:

macro_name : r'\b\w+(?=[(\s])'

macro_string : r'(?<=[)]).+' , macro_string

replaced_string : r'\bmacro_string[^\w]'

这里会用到一个判断,即是否是函数式的宏定义。是通过判断宏定义名后是否紧接着括号来判断的。如果是普通宏定义,进行完整替换;否则,还需要逐个提取参数,进行参数传递,然后进行替换。完整的代码如下:

def macros(self):

result = ''

while self.current_char is not None and self.current_char.isalnum():

result += self.current_char

self.next()

if result == 'define':

literals = self.read_lines().strip()

marco_name_pattern = re.compile(r'\b\w+(?=[(\s])')

result = re.search(marco_name_pattern, literals)

if result is None:

return None

# obtain the macro name

macro_name = result.group(0)

# obtain the macro string

rest_literals = literals[len(macro_name):]

if rest_literals[0] == '(':

defns_pattern = re.compile(r'(?<=[)]).+')

result = re.search(defns_pattern, rest_literals)

if result is None:

return None

defns = result.group(0)

else:

defns = rest_literals

rest_literals = rest_literals[:len(rest_literals)-len(defns)]

args_list = None

if not rest_literals == '':

args_list = self.extract_args(rest_literals)

# replaced identifier

arg_str = macro_name

if args_list is not None:

arg_str += '\('

for i in range(len(args_list)):

if i < len(args_list) - 1:

arg_str += '\w+,[\s]*'

else:

arg_str += '\w+'

arg_str += '\)'

# match the macro in the text

macro_pattern = r'\b%s[^\w]' % arg_str

original_str = self.text[self.pos:]

result = re.findall(macro_pattern, original_str)

if len(result) > 0:

for node in result:

macro_defns = defns

node_str = node[len(macro_name)+1:len(node)-1]

parms_list = self.extract_args(node_str)

for k in range(len(parms_list)):

macro_defs_parm_pattern = re.compile(r'\b%s\b' % args_list[k])

macro_defns = re.sub(macro_defs_parm_pattern, '%s' % parms_list[k], macro_defns)

replaces_str = ' {}{}'.format(macro_defns, node[-1])

result = re.sub(macro_pattern, replaces_str, original_str, 1)

# reset the text

self.text = result

original_str = self.text

self.pos = 0

return self.get_next_token()

这里用到的一个提取参数的辅助函数:

def extract_args(literal):

literal = literal.lstrip('(')

literal = literal.rstrip(')')

literal = literal.replace(' ', '')

args_pattern = re.compile(r'(?<=,)?(\w+)(?=,)?')

args_list = re.findall(args_pattern, literal)

return args_list

也是用正则表达式进行提取,为:

arg_list : r'(?<=,)?(\w+)(?=,)?'

至此,前处理过程就分析完毕。我们已经过掌握了处理最常用宏定义的过程。至于其它的前处理过程,如预编译指令的内容,这里将不再涉及。下一部分,我们进入语法分析的内容。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值