從零寫一個編譯器(七):語義分析之符號表的數據結構

来源:https://www.cnblogs.com/secoding/archive/2019/08/18/11373929.html
-Advertisement-
Play Games

項目的完整代碼在 "C2j Compiler" 前言 有關符號表的文件都在symboltable包里 前面我們通過完成一個LALR(1)有限狀態自動機和一個reduce信息來構建了一個語法解析表,正式完成了C語言的語法解析。接下來就是進入語義分析部分,和在第二篇提到的一樣,語義分析的主要任務就是生成 ...


項目的完整代碼在 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;
}

描述符號表

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

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

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

  1. 速度
    因為符號表需要頻繁的插入和查找,所以查詢和插入速度必須要足夠的快
  2. 靈活
    因為變數的定義的可能會很複雜,比如說多個修飾符再加上指針((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];

就可以這樣表示

Symbol declartor declartor specifer
name:e declareType = PONITER declareType = array basicType = 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,所以這樣的結構就相當於開頭描述的那個哈希表

這一節主要是描述了符號表的數據結構,兩個關鍵點是

  1. 描述變數

    所以定義了修飾符和描述符來描述一個變數

  2. 關聯變數

    定義了Symbol鏈表來串聯各個變數

另外我的github博客:https://dejavudwh.cn/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 冒泡排序 原理: 取序列的第一個元素,與序列剩餘的元素比較,如果第一個元素大於剩餘序列的某個元素,那麼就交換他們的位置。 代碼展示: 選擇排序 原理: 首先在未排序的序列中找到最小或最大的元素,存放到序列的起始或末尾位置,然後在從剩餘未排序元素中繼續尋找最小或最大的元素,然後放到剩餘未排序序列的起始 ...
  • 一 什麼是進程 ​ 進程:正在進行的一個過程或者說一個任務。而負責執行任務則是cpu。 ​ 舉例(單核+多道,實現多個進程的併發執行): ​ 太白金星在一個時間段內有很多任務要做:python備課的任務,寫書的任務,交女朋友的任務,王者榮耀上分的任務, ​ 但太白金星同一時刻只能做一個任務(cpu同 ...
  • 今天是第一天學習Python課程,主要從電腦基礎,Python的歷史,環境 ,變數,常量,註釋,用戶交互,基礎數據類型 ,簡單的if條件語句和while迴圈語句這幾個來學習,重點的掌握內容是python的環境,還有python2和python3的區別,常量等。、 1.電腦基礎 cpu:相當於人的 ...
  • Dbutils,db utils,顧名思義,是一個資料庫工具,體積很小,算是一個dao層的小框架。 DbUtils是Apache的開源項目,對JDBC進行了輕量級封裝,極大地簡化了JDBC編程。 DbUtils可以將結果集映射到JavaBean中,這一點和Hibernate很相似,但比Hiberna ...
  • 本文以一個簡單的小例子,簡述SpringMVC開發中RequestMapping的相關應用,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 1、什麼是跨域問題? 跨域問題是瀏覽器對於ajax請求的一種安全限制:一個頁面發起的ajax請求,只能是用當前頁同功能變數名稱同埠的路徑,這能有效的阻止跨站攻擊。 2、跨域問題出現的條件: 1、跨域問題是ajax請求特有的問題。 2、前後端的功能變數名稱、埠不一致。 3、CORS跨域解決原理簡單分析: CORS ...
  • 原文: https://medium.com/netflix techblog/re architecting the video gatekeeper f7b0ac2f6b00 想法 我們決定部署一個全高密度近場緩存(Hollow)來解決我們的IO瓶頸。對於我們的每個上游系統,我們要建一個能讓Ga ...
  • 對const map使用std::map::[]產生的bug研究了一會兒,發現了const, non-const的各自獨特的用處。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...