馬上就要挑戰 c parser 嗎? 是也不是, 最主要只處理合法的 c 語言輸入, 並沒有要輸出組合語言、AST 或是語意分析之類的動作, 這樣就簡單很多。
麻煩的是 c yacc 語法規則要怎麼寫, 我自己是寫不出來的, The c programming language 附錄 A 有一個這樣的 yacc 語法, 稍加修改就可以用了。但我不會修改, 也不想輸入那麼多字, 在網路上找到了範例。至於為什麼 The c programming language 附錄 A 會有 yacc 文法, 因為 yacc 也是那時候開發出來的, Dennis MacAlistair Ritchie 沒理由不用的, 不只 c 用了, 連 c++ 也有用 yacc, 畢竟是同時期的貝爾實驗室同事。
在四則運算告一個段後之後, 本來應該是 if/else, 不過 if/else 不是太難, 就先跳過, 來看看 c function 的呼叫應該怎麼產生對應的組合語言。本篇文章介紹怎麼產生 c function 參數傳遞的組合語言。也許有人知道用 stack 用來傳遞參數, 由右而左的順序放進 stack, 除了這些, 還需要其他的知識才能產生對應的組合語言。
把 char c, 傳給 fun1(int a) 時, c 需要做什麼特別的事情嗎?
依照慣例, 先來一個很簡短的 c 程式, 是簡單的 c 函式呼叫, 來看 gcc 會輸出什麼樣的組合語言?
f.c 是 source code, list 1 則是 gcc 輸出的 x86 32bit 組合語言。
有了函式定義之後, 再來就是要呼叫這個函式。function call 的 AST 應該長什麼樣子呢? 像 list 1 這樣。
list 2, list 3 是我早期的版本, 那時候還可以這樣寫, 現在已經不行了。得用 list 1 的寫法才行, 也更像 c 語言了。
c_parser.cpp L334 是 function call 的 EBNF, 比較麻煩的是 function 的參數可不見得只是變數或是整數, 還可能是一個 function 或是運算式。當然, 如果想簡化, 就不要處理這些, 程式會簡單些, 不過我就是不爽不能用 function 或是運算式, 堅持把這個加了進去。
記得你是來學習寫編譯程式, 不是來練習資料結構的。雖然我用的是 c++, 但也沒用到特別的東西, 只用到 if/else, while, function call, 一點 class, vector, stirng, map, 也沒用到多型, 但這樣的武器就已經足夠完成一個玩具版的練習了。在提醒一次, 記得你是來學習寫編譯程式, 不是來把玩 c++ 的。
你還要把時間花在精巧的 c++ 語言特性嗎? 不, 我喜歡把時間用在實作某個東西上, c++ 語言特性有他的好處, 但不一定是必須的, 你能寫出很酷的或是讓人迷惑的 c++ 程式碼, 但我可以寫編譯器, 而裡頭只用到 if/else, function call, while loop, 沒有什麼特別的 c++ 語法, 那個厲害呢? 吾 ... 都很厲害, 看怎麼選擇而己, 當然如果可以兩個都掌握那是最棒的。
if 這部份就是要產生 if node, 特別的部份是 if 區塊和 if 條件式, if 條件式是 expression, 想成「四則運算」就好, 「四則運算」的 AST 很久之前就完成了, 沒問題, if 區塊剩下的 if statement 也可以想成 expression -> 又可以想成「四則運算」, 等於只要處理 if node 而已, 實在輕鬆, 真實世界當然不是這麼簡單, 不過這樣想的話, 似乎就沒那麼難了。
現在你知道為什麼要先處理「四則運算」了吧, 處處都是「四則運算」。
fig1 list 1 範例的 AST, if/else statement AST
如果 else 對你有點難的話, 不要實作 else 也無所謂, 用 if 就夠了, 難度又可以再減少一些。
while node 該怎麼建立呢? if node 會, while 就會, 這是一樣的, 只是關鍵字改成 while 而已嘛! 他們的不同在 eval 階段, if 的 eval 只要執行一次, 而 while 的 eval 可能要執行 0 次或是多次, 但 AST node 是很類似的。
學習的重點在精神, 不在完整, 不然光是要處理 int *********i, 就累死你了, 目前的程式是可以 parse int *********i, 但是無法 eval 它, 要怎麼紀錄這樣的型別, 難倒我。