? ? ? ? 在上一部分中鼓择,我們分析并實現(xiàn)了詞法分析的過程三幻。這一部分,我們從頭文件和宏定義兩個方面入手呐能,來分析前處理過程念搬。
2.1 頭文件
? ? ? ? 讓我們先來看一段代碼:
#include "stdio.h"
int main(int argc, char* argv[])
{
printf("Hello World!");
return 0;
}
? ? ? ? 在這段代碼中,添加頭文件的目的是為了引入函數(shù)int printf(char *fmt, ...);
,也就是說大可以將上面代碼改寫成:
int printf(char *fmt, ...);
int main(int argc, char* argv[])
{
printf("Hello World!");
return 0;
}
? ? ? ? 理論上锁蠕,經(jīng)過替換后的這段代碼編譯沒有任何問題夷野,而這就是頭文件替換的目的。但是荣倾,在沒有編譯之前悯搔,我們根本不知道這個函數(shù)里面調(diào)用了頭文件的什么東西,可能是變量定義舌仍,也可能是某個或者多個函數(shù)妒貌,或者只是習慣性地包含了這個頭文件。所以铸豁,我們需要將頭文件內(nèi)所有的內(nèi)容全部展開到當前文件中來灌曙。這就非常好實現(xiàn),只需要記住頭文件的名字然后打開對應的文件节芥,然后復制到頭文件所在的地方就可以了在刺。Python實現(xiàn)如下:
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()
? ? ? ? 頭文件引用里存在一個概念:循環(huán)依賴。比如头镊,對于"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;
...
};
由于兩個文件內(nèi)容相互包含,按照頭文件展開的原則相艇,勢必會是一個死循環(huán)颖杏。可以采用前向聲明加以避免坛芽,但是編譯器必須能夠及時檢測出來留储。只需要增加一個對頭文件內(nèi)部的頭文件計數(shù)上的輔助判斷即可。
2.2 宏定義
? ? ? ? C語言宏定義的規(guī)則為:
#define macro_name macro_string
定義之后咙轩,會用macro_string
去替換代碼中所有的macro_name
获讳。但是,由于macro_string
可以是數(shù)字活喊、表達式和函數(shù)赔嚎,不單純只是替換。還是從代碼分析入手胧弛,先看下面這段代碼中的宏定義及其使用:
#define FOO 0
#define ADD(X, Y) (X + Y)
int main()
{
return ADD(1, 2) - FOO;
}
? ? ? ? 對于FOO
這個宏定義尤误,只需要將后面代碼中的同名標志符直接用0
替換即可;而對于ADD
這種函數(shù)式的宏结缚,還需要進行實參的替換损晤。最終,經(jīng)過替換后的代碼為:
int main()
{
return (1 + 2) - 0;
}
這樣就實現(xiàn)了前處理過程红竭。
? ? ? ? 我們使用python中的正則表達式進行內(nèi)容的替換尤勋。其中喘落,宏定義名、待替換的字符串和被替換的字符串分別如下:
macro_name : r'\b\w+(?=[(\s])'
macro_string : r'(?<=[)]).+' , macro_string
replaced_string : r'\bmacro_string[^\w]'
這里會用到一個判斷最冰,即是否是函數(shù)式的宏定義瘦棋。是通過判斷宏定義名后是否緊接著括號來判斷的。如果是普通宏定義暖哨,進行完整替換赌朋;否則,還需要逐個提取參數(shù)篇裁,進行參數(shù)傳遞沛慢,然后進行替換。完整的代碼如下:
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()
這里用到的一個提取參數(shù)的輔助函數(shù):
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+)(?=,)?'
? ? ? ? 至此团甲,前處理過程就分析完畢。我們已經(jīng)過掌握了處理最常用宏定義的過程黍聂。至于其它的前處理過程躺苦,如預編譯指令的內(nèi)容,這里將不再涉及产还。下一部分匹厘,我們進入語法分析的內(nèi)容。
實現(xiàn)簡易的C語言編譯器(part 0)
實現(xiàn)簡易的C語言編譯器(part 1)
實現(xiàn)簡易的C語言編譯器(part 3)
實現(xiàn)簡易的C語言編譯器(part 4)
實現(xiàn)簡易的C語言編譯器(part 5)
實現(xiàn)簡易的C語言編譯器(part 6)
實現(xiàn)簡易的C語言編譯器(part 7)
實現(xiàn)簡易的C語言編譯器(part 8)
實現(xiàn)簡易的C語言編譯器(part 9)
實現(xiàn)簡易的C語言編譯器(part 10)
實現(xiàn)簡易的C語言編譯器(part 11)