|
Bene qui latuit, bene vixit |
繼四則運算、if/else/while 之後, 登場的是函式宣告/定義, 不是函式呼叫, 得先有了函式宣告/定義才能呼叫, 所以先處理函式宣告/定義。當然也要提一提變數的宣告。
我參考的是《
手把手教你构建 C 语言编译器(5)- 变量定义》 ebnf, 不過後來我才注意到我把函式宣告和定義搞在一起了, 這個 ebnf 並沒有區分函式宣告和定義, 雖然有點遺憾, 不過這樣可以簡化整個函式相關的 parser。
所以無法寫 var_func.c L3 的語法, 只能寫 var_func.c L5 ~ 8 這樣的語法, 其實也還好, 所以就先這樣吧! 精簡版的 c 語法嘛!
不過先談一般的變數宣告, 像 int a; 就是變數宣告, char (*(*x[3])())[5] 這種也是變數宣告, 不過很抱歉, 本篇的 ebnf 無法解析這麼複雜的宣告, 也沒打算支援這麼恐怖的宣告, 頂多是 int *********i; 這種。
c_parser.c L823 的 var_decl() 就已經很長了, 已經不容易和 ebnf 對應了, 這是因為我加了不少的東西, 掩蓋了本質, 我需要區分 global/local variable, 並且用一個資料結構紀錄這個變數的型別, 這不容易, 你要怎麼紀錄 char (*(*x[3])())[5] 的型別呢? 我沒想到好方法, 先用個 ObjType 檔一檔。
本質就是
這樣而已, 感覺沒那麼難, 應該寫的出來, 但就算只有支援指標, 也很困難, 看看 ex2 的例子:
這就有點難了吧! 你說沒什麼多個迴圈去跑而已, 但要怎麼把型別記錄下來呢?
要用怎麼樣的方式紀錄這是幾顆星的指標呢?
這便是 {'*'} ebnf 對應的部份, L833 的程式碼就是在對付這個 ebnf, L851 開始的部份則是在對付 { ',' {'*'} id } ebnf, 再來一次而已, 如果這樣的程式碼對你來說很難看, 試試用 gdb 跑一次, 邊跑邊觀察 ASTNode 怎麼長出來, 就會有感覺了, 我也是這樣開始的, 當然如果你找得到朋友詢問, 那是最好的, 如果你在某聚會上遇到我, 也歡迎找我聊聊。
如果還是看不懂, 那該怎麼辦, 沒辦法了, 先放棄吧, 編譯器沒那麼重要, 可以先玩玩別的東西, 等過了一段時間記得回來, 也許就看懂了。有這麼好的事情嗎? 難說, 搞不好就有這樣的好事發生在你身上。
ex1, ex2 解決之後, 就可以來處理函式宣告與定義了。
和變數比較起來, 函式多了後面的 (), 以及 () 裡頭的變數宣告, 所以得要先能處理變數宣告才行。
L748 func_decl() 在處理函式宣告, 和變數不同的是多了
'(' parameter_decl ')' '{' body_decl '}'
L780 parameter_decl() 在處理 parameter, 和變數有點像, 只是沒有 ';' 結尾, 再來是 function body, 裡頭可以想成由變數宣告、四則運算、if/else、while 的組成結構, 越來越複雜了哦, 不過這些之前都對付過了, 把他們組合起來即可。
這就是為什麼我是依照這樣的順序來學習這些項目, 他們有一點點的相互關聯, 而拆解這些項目, 才能專住在某一個小部份, 也才能用零散的時間來學習, 感覺上也沒有那麼難了。
list 1 是這次的成果, 有了函式/變數的 AST, 酷吧!
function 分為 para, func_body; func_body 又分為 "變數宣告" 和 "執行的程式碼" (可以想成四則運算) 兩部份。
list 1 L12 myfunc parameter 的部份是:
變數名稱是 p3, type 是 3 顆星的 int 指標
變數名稱是 a, type 是 int
變數名稱是 p1, type 是 1 顆星的 char 指標
list 1 L12 myfunc func body 的部份是:
變數宣告:
變數名稱是 i, type 是 int
變數名稱是 j, type 是 int
執行的程式碼的部份:
1+2
這樣就把 function, variable 用 AST 表示出來了。
ref:
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。