日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从零写一个编译器(七):语义分析之符号表的数据结构

發布時間:2023/12/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从零写一个编译器(七):语义分析之符号表的数据结构 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

項目的完整代碼在 C2j-Compiler

前言

有關符號表的文件都在symboltable包里

前面我們通過完成一個LALR(1)有限狀態自動機和一個reduce信息來構建了一個語法解析表,正式完成了C語言的語法解析。接下來就是進入語義分析部分,和在第二篇提到的一樣,語義分析的主要任務就是生成符號表來記錄變量和變量的類型,并且發現不符合語義的語句

描述變量

在C語言里對變量聲明定義里,主要有兩種描述

  • 說明符(Specifier)

    說明符也就是對應C語言的一些描述變量類型或者像static,extern的關鍵字(像extern這些關鍵詞在這次實現的編譯器里并沒有用到,因為extern可能還要涉及到多個源文件的編譯和鏈接)
  • 修飾符(Declarator)

    修飾符則是由變量名或者代表指針類型的星號,數組的中括號組成,修飾符屬于可以復雜的一部分,因為修飾符可以進行組合。所以對于組合的修飾符就可以創建多個Declarator,按順序鏈接起來

這樣就可以完成兩個類,這兩個類的邏輯都比較簡單:

Declarator類

  • declareType:用來表示當前的Declarator是一個指針還是數組或者函數
  • numberOfElements、elements:如果當前的類型是個數組的話,它們就表示數組的元素個數和數組元素
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類

Specifier的屬性會比較多一點,但是在后面編譯器可能只支持int, char, void, struct四種類型

  • 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; }

描述符號表

在前面定義兩個描述變量的類,但是僅靠這兩個類還是無法準確的表達一個符號,所以我們需要包裝一下這兩個類,讓它更具表達力

編程很多時候都是根據特定的需求完成特定的數據結構,符號表在計算機里本質上也只是用來描述變量的數據結構而已

這個數據結構作為符號表有幾個基本的條件:

  • 速度
    因為符號表需要頻繁的插入和查找,所以查詢和插入速度必須要足夠的快
  • 靈活
    因為變量的定義的可能會很復雜,比如說多個修飾符再加上指針((long int, long doube *),所以在設計上必須足夠靈活
  • 因為學習編譯器一直是跟著陳老師的課,所以符號表的設計也沿用老師的設計

    為了保證上面兩個條件,我們選用鏈式哈希表來實現

    這張圖是我網上找的,實際上沒有那么復雜

    所有的變量都存儲到這個哈希表中,同名變量被哈希會被同一個地方,當然它們要屬于不同作用域,而區分不同作用域就在于這張圖上面一部分,它會把同一個作用域的變量連接起來

    symboltable.Symbol

    這個類用來描述符號表里的一個符號

    如果從github下載源文件的話,里面有許多是在后面代碼生成才需要用到的,現在可以忽略

    主要屬性有:

    • level: 用來表明變量的層次
    • duplicate:是否是一個同名變量
    • args:如果該符號對應的是函數名,那么args指向函數的輸入參數符號列表
    • next: 指向下一個同層次的變量符號
    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];

    就可以這樣表示

    Symboldeclartordeclartorspecifer
    name:edeclareType = PONITERdeclareType = arraybasicType = INT isLong = TRUE
    ->->->->

    結構體符號的定義

    StructDefine這個文件還沒講過,這個文件是用來描述結構體的,因為結構體本身的復雜性,所以就需要對它進行特殊處理,但是結構體本質上還是一堆變量的組合,所以依舊可以用上面的方法描述

    • tag: 結構體的名稱
    • level: 結構體的嵌套層次
    • Symbol:對應結構體里的變量
    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/

    轉載于:https://www.cnblogs.com/secoding/p/11373929.html

    總結

    以上是生活随笔為你收集整理的从零写一个编译器(七):语义分析之符号表的数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。