1. 程式人生 > >自制指令碼語言(12) 作用域與符號表

自制指令碼語言(12) 作用域與符號表

摘要:介紹了自制語言的編譯器對符號表的處理。

YF語言中,符號表的基本結構是hash表。每個AST,附帶了3個hash表,變量表,型別表,函式表。例如<String, R_Variable>就是變量表,記錄變數的名稱和值。型別表記錄各種型別,array、class、interface、function、generic。函式表則記錄了函式名和函式體、函式型別的對應關係。

一個檔案有若干class和interface定義,此外的部分是指令碼,可以定義變數、函式等。同時,class或指令碼都沒有main()函式。指令碼本身就是main()。指令碼內的函式可以訪問指令碼範圍的變數與函式。而型別的作用域都是全域性的(全域性是指本檔案範圍)。檔案頭部import其他檔案之後,可以訪問其class、interface,但不能訪問其指令碼變數和函式。

作用域跟AST的層次有關以及訪問者有關。舉例來說,每個stmt_list都是一個單獨的作用域。codegenerator保留一個棧list<AST>,訪問AST時,遇到stmt_list,就把這個AST加入棧頂。遇到define variable,就生成一個新的符號,這個符號加入棧頂AST的符號表中。當後面要查詢variable,就從棧頂開始依次查詢符號表。當訪問者離開這個AST,把棧頂AST退出。這樣,不在作用域內的變數引用就是非法的。而作用域範圍可以巢狀。內層作用域可以訪問外層作用域定義的變數或者函式。

查詢函式的方法,除了函式的名稱,還要有函式的型別簽名,如int(int, int)。這可以用於函式過載。

class內部不允許訪問外部的變數及函式。但是子類可以訪問父類。這裡就有一個作用域控制問題。class範圍內,每個定義的field和method都記錄入type物件中,同時遞迴地記錄了全部super class以及interface。在class範圍內查詢變數或者函式時,優先在codegenerator的符號表相關AST棧中查詢。AST棧中每個AST,在codegenerator初次訪問時,都打上了一個標記,標明此處是什麼範圍。比如全域性範圍,標記是0b1,class範圍標記是0b10,區域性變數的標記是0b100,等等。當codegenerator發起一個查詢時,自身的標記如果不符合,就不被允許查詢。這樣就杜絕了從class內部訪問外部的指令碼的變數。那麼,當class內部查詢不到變數時,就會訪問型別表,查詢當前class的成員記錄。若當前class型別中查詢不到記錄,則繼續向super型別中查詢。直到找到符號或者找不到而throw exception。

值得一提的是this關鍵字。遇到this關鍵字,查詢順序發生變化。省略前面從AST查詢的步驟而是直接通過型別表中找。因為在class範圍內的AST的符號表,無非有兩種情況,class的成員,或者區域性變數。沒有this關鍵字,那麼符號可能是類成員也可能僅僅是區域性變數,有了this關鍵字,符號只能是成員。所以必須從class型別中去找。前面一種情況下區域性變數對型別成員的覆蓋,也是通過AST棧來實現。