結構體是c語言中,最為複雜的原生資料結構,它把多種原生結構結合在一起,形成乙個有特點含義的資料結構,要實現乙個完整的c語言編譯器或直譯器,就必須要擁有對結構體的解析能力,本節,我們在當前直譯器的基礎上,增加結構體的解釋執行能力,完成本節後,我們的直譯器可以解析執行下面**:
void main() tag;
struct tag mytag;
struct tag hertag;
mytag.v1 = 1;
hertag.v1 = 2;
printf("set filed v1 of struct mytag to value : %d, and v1 of hertag to value : %d", mytag.v1, hertag.v1);
}
我們先回憶一下結構體的語法表示式:
struct_specifier -> struct opt_tag lc def_list rc
| struct tag;
我們對比下具體的結構體定義和語法表示式的對應關係:
struct tag tag;
上面定義中struct 是關鍵字,對應語法表示式中的struct終結符,tag 是結構體定義名,對應表示式中的opt_tag; int v1;int v2; char v3; 這三個變數定義對應於def_list,.
unary -> unary structop
name
上面表示式用來說明對結構體某個值域的引用,例如語句mytag.v1就可以對應上面的語句,structop是終結符,他對應的文字字元為」.」, 或 「->」.
在前面的課程我們詳細說明過,當直譯器解析到結構體的定義時,它先給結構體變數構建乙個symbol物件,該symbol物件的修飾符,也就是specifier含有乙個結構體叫structdefine, structdefine 會為結構體中的每乙個變數建立乙個symbol物件,然後把這些物件串聯成乙個佇列,仍然以上面的結構體定義為例,我們的直譯器解析後,形成如下結構:
(圖一)
當我們定義乙個結構體變數時,例如語句struct tag mytag; 任何有關變數宣告的語句經過一系列遞迴後,最後對應的語法表示式為:
def
-> specifiers decl_list semi;
當直譯器解析**是,遞迴到上面的表示式時,直譯器要判斷一下,當前宣告的變數是否是結構體,如果是的話,那麼必須為當前結構體變數賦值乙份結構體內部的變數所對應的symbol佇列,也就是說,當直譯器解析到語句 struct tag mytag;時,會把上圖的結構再複製乙份:
(圖二)
這樣一來,對結構體某個變數的值域的讀寫,直接轉換成對某個變數symbol的讀寫就可以了,例如**中的語句:
mytag.v1 = 1;
這相當與把數值1寫入到上圖中最下面v1所對應的symbol物件即可。
我們看看相應的**實現,第一步就是,當解析到結構體的變數宣告時,把結構體定義的符號表資料結構複製乙份,也就是從圖1到圖2的過程:
public
class lrstatetableparser
....
}private
void
handlestructvariable(symbol symbol)
}typelink = typelink.tonext();
}if (isstruct == true) else
original = original.getnextsymbol();
}symbol.setarglist(headcopy);}}
handlestructvariable 這個函式的作用就是把圖一中的結構複製一遍,實現從圖一到圖二的轉換。這樣一來,當宣告同乙個結構體型別的不同變數時,就像我們的示例**中,宣告了兩個結構體變數,分別是mytag,hertag, 那麼對應v1的symbol物件就有兩份,對不同的v1賦值,實際上是把數值賦值到不同的symbol物件中。
我們再看看對結構體變數的讀寫,例如語句:
mytag.v1 = 1;
當執行上面語句時,直譯器先獲得要讀寫的結構體變數對應域的名稱,上面給定**,要賦值的域的名稱是」v1」, 然後在符號表中,找到變數名mytag對應的symbol物件,然後找到specifer,進而找到structdefine物件,在該物件中,找到結構體裡面各個變數所對應的symbol佇列,然後利用域的名稱字串「v1」,在佇列中找到獨有的symbol物件,最後把數值1寫入到該symbol物件中。
相應**如下:
public
class
unarynodeexecutor
extends
baseexecutor
args = args.getnextsymbol();
}if (args == null)
root.setattribute(icodekey.symbol, args);
root.setattribute(icodekey.value, args.getvalue());
break;
....}}
如果通過結構體對應成員的名字字串,在structdefine中的symbol佇列中找不到給定名字的symbol物件,這表明程式要訪問結構體定義中不存在的變數,從而我們的程式就會因此種異常而退出。 pragma pack 結構體對齊編譯器選項
現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但是各個硬體平台對儲存空間的處理上有很大的不同。一些平台對某些特定型別的資料只能從某些特定位址開始訪問。其他平台可能沒有這種情況,但是如果不按照適合其平台要求對資料存放進行對齊,會在訪問效率上帶來損...
從零開始寫個編譯器吧 編譯器的結構
自然,我們還是先從 tao 語言的編譯器下手吧。在動手寫編譯器之前,得容我將編譯器的結構進行進一步的劃分。編譯器可視為乙個黑盒,從其一端輸入源 另一端產出目標 此過程進一步拆分便有了如下形式。首先是 tokenizer 詞法分析器 它讀入乙個乙個字元,並將其合併成乙個乙個token 單詞 這些 to...
C語言的編譯器
c語言的常用編譯器 目前最流行的c語言編譯器有以下幾種 gnu compiler collection 或稱 gcc microsoft c 或稱 ms c borland turbo c 或稱 turbo c 這些c語言版本不僅實現了ansi c標準,而且在此基礎上各自作了一些擴充,使之更加方便 ...