項目的完整代碼在 C2j-Compilerjava
有關符號表的文件都在symboltable包裏git
前面咱們經過完成一個LALR(1)有限狀態自動機和一個reduce信息來構建了一個語法解析表,正式完成了C語言的語法解析。接下來就是進入語義分析部分,和在第二篇提到的同樣,語義分析的主要任務就是生成符號表來記錄變量和變量的類型,而且發現不符合語義的語句github
在C語言裏對變量聲明定義裏,主要有兩種描述編程
說明符(Specifier)數組
說明符也就是對應C語言的一些描述變量類型或者像static,extern的關鍵字(像extern這些關鍵詞在此次實現的編譯器裏並無用到,由於extern可能還要涉及到多個源文件的編譯和連接)修飾符(Declarator)數據結構
修飾符則是由變量名或者表明指針類型的星號,數組的中括號組成,修飾符屬於能夠複雜的一部分,由於修飾符能夠進行組合。因此對於組合的修飾符就能夠建立多個Declarator,按順序連接起來函數
這樣就能夠完成兩個類,這兩個類的邏輯都比較簡單:學習
public class Declarator { public static int POINTER = 0; public static int ARRAY = 1; public static int FUNCTION = 2; private int declareType; private int numberOfElements = 0; HashMap<Integer, Object> elements = null; public Declarator(int type) { this.declareType = type; } ... }
Specifier的屬性會比較多一點,可是在後面編譯器可能只支持int, char, void, struct四種類型this
basicType:用來代表當前變量的類型設計
storageClass:表示變量的存儲方式(fixed,auto),這裏咱們把typedef的信息也放在這裏,也就是說若是碰見typedef,那麼storageClass會被設置爲TYPEDEF
constantValue和vStruct:這兩個屬於比較特殊的兩個屬性,它們表示枚舉類型和結構體,之因此特殊是由於它們以後要進行特殊處理。若是碰見枚舉類型至關於構造一個basicType是CONSTANT的Specifier,對應的值也就是constantValue了
public class Specifier { /** * Variable types */ public static int NONE = -1; public static int INT = 0; public static int CHAR = 1; public static int VOID = 2; public static int STRUCTURE = 3; public static int LABEL = 4; /** * storage */ public static int FIXED = 0; public static int REGISTER = 1; public static int AUTO = 2; public static int TYPEDEF = 3; public static int CONSTANT = 4; public static int NO_OCLASS = 0; public static int PUBLIC = 1; public static int PRIVATE = 2; public static int EXTERN = 3; public static int COMMON = 4; private int basicType; private int storageClass; private int outputClass = NO_OCLASS; private boolean isLong = false; private boolean isSigned = false; private boolean isStatic = false; private boolean isExternal = false; private int constantValue = 0; private StructDefine vStruct = null; }
在前面定義兩個描述變量的類,可是僅靠這兩個類仍是沒法準確的表達一個符號,因此咱們須要包裝一下這兩個類,讓它更具表達力
編程不少時候都是根據特定的需求完成特定的數據結構,符號表在計算機裏本質上也只是用來描述變量的數據結構而已
這個數據結構做爲符號表有幾個基本的條件:
由於學習編譯器一直是跟着陳老師的課,因此符號表的設計也沿用老師的設計
爲了保證上面兩個條件,咱們選用鏈式哈希表來實現
這張圖是我網上找的,實際上沒有那麼複雜
全部的變量都存儲到這個哈希表中,同名變量被哈希會被同一個地方,固然它們要屬於不一樣做用域,而區分不一樣做用域就在於這張圖上面一部分,它會把同一個做用域的變量鏈接起來
這個類用來描述符號表裏的一個符號
若是從github下載源文件的話,裏面有許可能是在後面代碼生成才須要用到的,如今能夠忽略
主要屬性有:
public class Symbol { String name; String rname; int level; boolean duplicate; Symbol args; Symbol next; }
這時候用Symbol加上以前的Specifier和Declarator就有足夠的表達力來描述一個符號,那麼就須要把這三個類聯繫起來,先增長一個TypeLink
TypeLink表示一個Specifier或者一個Declarator,這裏用繼承來實現可能會顯得更好看一點
public class TypeLink { public boolean isDeclarator; /** * typedef int */ public boolean isTypeDef; /** * Specifier or Declarator */ public Object typeObject; private TypeLink next = null; public TypeLink(boolean isDeclarator, boolean typeDef, Object typeObj) { this.isDeclarator = isDeclarator; this.isTypeDef = typeDef; this.typeObject = typeObj; } public Object getTypeObject() { return typeObject; } public TypeLink toNext() { return next; } public void setNextLink(TypeLink obj) { this.next = obj; } }
這樣在Symbol裏就要加入兩個屬性
typeLinkBegin和typeLinkEnd就是用來描述變量的說明符和修飾符的整個鏈表,也就是以前說的把這些修飾符或者說明符按順序鏈接起來
public class Symbol { String name; String rname; int level; boolean implicit; boolean duplicate; Symbol args; Symbol next; TypeLink typeLinkBegin; TypeLink typeLinkEnd; }
這樣完成以後,例如
long int (*e)[10];
就能夠這樣表示
Symbol | declartor | declartor | specifer |
---|---|---|---|
name:e | declareType = PONITER | declareType = array | basicType = INT isLong = TRUE |
-> | -> | -> | -> |
StructDefine這個文件還沒講過,這個文件是用來描述結構體的,由於結構體自己的複雜性,因此就須要對它進行特殊處理,可是結構體本質上仍是一堆變量的組合,因此依舊能夠用上面的方法描述
public class StructDefine { private String tag; private int level; private Symbol fields; public StructDefine(String tag, int level, Symbol fields) { this.tag = tag; this.level = level; this.fields = fields; } }
看一個結構體定義的例子
struct dejavidwh { int array1[5]; struct dejavudwh *pointer1; } one;
因此最後只須要
private HashMap<String, ArrayList<Symbol>> symbolTable = new HashMap<>(); private HashMap<String, StructDefine> structTable = new HashMap<>();
就能夠描述一個符號表
symbolTable裏的key至關於變量的名字,然後面的ArrayList存放着同名變量,由於每一個Symbol都有一個next指針來指向同級的其它Symbol,因此這樣的結構就至關於開頭描述的那個哈希表
這一節主要是描述了符號表的數據結構,兩個關鍵點是
描述變量
因此定義了修飾符和描述符來描述一個變量
關聯變量
定義了Symbol鏈表來串聯各個變量
另外個人github博客:https://dejavudwh.cn/