實現(xiàn)簡易的C語言編譯器(part 2)

? ? ? ? 在上一部分中鼓择,我們分析并實現(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)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末雕沉,一起剝皮案震驚了整個濱河市集乔,隨后出現(xiàn)的幾起案子去件,更是在濱河造成了極大的恐慌坡椒,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尤溜,死亡現(xiàn)場離奇詭異倔叼,居然都是意外死亡,警方通過查閱死者的電腦和手機宫莱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門丈攒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人授霸,你說我怎么就攤上這事巡验。” “怎么了碘耳?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵显设,是天一觀的道長。 經(jīng)常有香客問我辛辨,道長捕捂,這世上最難降的妖魔是什么瑟枫? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮指攒,結(jié)果婚禮上慷妙,老公的妹妹穿的比我還像新娘。我一直安慰自己允悦,他們只是感情好膝擂,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著澡屡,像睡著了一般猿挚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驶鹉,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天绩蜻,我揣著相機與錄音,去河邊找鬼室埋。 笑死办绝,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的姚淆。 我是一名探鬼主播孕蝉,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼腌逢!你這毒婦竟也來了降淮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤搏讶,失蹤者是張志新(化名)和其女友劉穎映琳,沒想到半個月后目木,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年娱俺,在試婚紗的時候發(fā)現(xiàn)自己被綠了社付。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疮鲫。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡残揉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肴盏,到底是詐尸還是另有隱情科盛,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布菜皂,位于F島的核電站贞绵,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏幌墓。R本人自食惡果不足惜但壮,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一冀泻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜡饵,春花似錦弹渔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至焦辅,卻和暖如春博杖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筷登。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工剃根, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人前方。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓狈醉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惠险。 傳聞我的和親對象是個殘疾皇子苗傅,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容