編譯原理語義分析階段,現在已經做到了能生成語法樹,基於語法樹怎樣實現語義分析和中間代碼生成?

看了好幾遍書,太抽象了不好理解,希望能得到一個具體的分析流程。


如果你能做到生成語法樹了,其實我覺得你前端基本就完成了,如果你再使用LLVM的話,我想你接下來的工作就非常輕鬆了,可以參看:3. Kaleidoscope: Code generation to LLVM IR,你會發現利用你已經有的語法樹生成中間代碼,乃至生成目標代碼有多麼的輕鬆。

上面的鏈接教程沒有介紹語義分析,所以我補充語義分析部分。對於語法分析來說,它是構建出一個抽象語法樹(Abstract Syntax Tree),而在這個樹形結構中,只是一個框架與骨骼,卻並沒有包含節點內容的檢查。比如你現在擁有二元操作符號+的一個抽象語法樹,你的葉節點分別為3與5.6,那麼這個時候,如果你是在實現類似C語言的編譯器,這裡就允許類型自動提升,你需要把3提升到5.6同樣的double類型,這裡的工作就是在語意分析部分來做。而做的方法非常簡單,就是遍歷這個抽象語法樹,然後從符號表裡面取出這兩個葉節點對應的數據類型,然後來完成這種提升轉換。所以,其實你只要完成了抽象語法樹的生成,後面很多時候就是來利用這個樹做文章,無論是語義分析抑或生成中間代碼等。我這裡也說的比較簡略,希望對你有所幫助。


語義分析:

int a;

string b;

return a*b; // FUCK!

所以很容易看出來是一個遞歸的過程,我覺得你先別查資料,自己擼一個出來,然後再說。

代碼生成

int a=0;

int b=0;

return a*b; // mov eax, 0

這個你得看《engineering a compiler》,很大的篇幅在講這個過程,他還會告訴你什麼是SSA,告訴你如何選取合適的指令,如何安排寄存器。


語義分析用binary operator的AST來開始做語義分析應該是最容易理解和實現了,取一個binary operator的AST,把左右子樹的類型都取出來,根據你的需要判斷類型是否一致,如果這個operator要求類型一致,類型不一致就直接報錯;如果允許類型不一致,根據情況判斷是否需要類型上升轉型,並把這個分析結果記錄下來,代碼生成階段就根據這個轉型數據插入轉型代碼。把這個做好理解透了,在其他的AST類型里加上符合語言語義的操作也就好辦了。

如果是編譯成可執行文件,可以使用LLVM,看看例子使用,鏈接 @藍色已經給出了。如果打算自己做VM,那binary operator就可以根據operator的類型定義一個專屬的VM指令,比如加法就對應一條加法指令。代碼生成的時候,只需要根據AST,左子樹生成指令把左子樹的結果存放到一個地方a(假設是寄存器),右子樹也生成指令把結果放到另一個寄存器b,這個binary operator的AST代碼生成就把左右子樹的寄存器a和b作為指令的操作數生成出來,並把結果存放到寄存器c。VM執行的時候就根據指令知道操作數是在寄存器a和b里,再根據指令類型計算出結果並把結果放到寄存器c里。

大概過程就是這樣了,LLVM tutorial里有很好的例子。


可以分享一下 您的語法樹生成的思路嗎?我也在研究這個但是有點亂


推薦閱讀:

不斷發展的語音識別和語義分析技術對互聯網世界會有怎樣的影響?它們更可能成為大公司屏蔽競爭對手的護城河還是創業者顛覆市場格局的殺手鐧?

TAG:編譯原理 | 語義分析 |