從零寫一個編譯器(三):語法分析之幾個基礎數據結構

項目的完整代碼在 C2j-Compilerjava

寫在前面

這個系列算做爲我本身在學習寫一個編譯器的過程的一些記錄,算法之類的都沒有記錄原理性的東西,想知道原理的在龍書裏都寫得很是清楚,可是我本身一開始是不怎麼看得下來,到如今都尚未完整的看完,它像是一本給已經有基礎的人寫的書。git

在parse包裏一共有8個文件,就是語法分析階段寫的全部東西啦github

  • Symbols.java
  • Production.java
  • SyntaxProductionInit.java
  • FirstSetBuilder.java
  • ProductionManager.java
  • ProductionsStateNode.java
  • StateNodeManager.java
  • LRStateTableParser.java

項目的完整代碼在 C2j-Compiler算法

SyntaxProductionInit 語法初始化

在上一篇說了,居然要驗證句子正確與否,天然就須要語法,也就是給出相應的語法推導式數據結構

全部語法的初始化工做都在SyntaxProductionInit裏完成工具

///EXT_DECL_LIST ->EXT_DECL_LIST COMMA EXT_DECL
right = getProductionRight(new int[]{Token.EXT_DECL_LIST.ordinal(), Token.COMMA.ordinal(), Token.EXT_DECL.ordinal()});
production = new Production(productionNum, Token.EXT_DECL_LIST.ordinal(), 0, right);
productionNum++;
addProduction(production, false);

好比下面這個就對應C語言的變量聲明語句的推導式,PROGRAM是整個推導式的開始符號,EXT_DEF_LIST就是聲明列表,這裏的EXT_DEF_LIST -> EXT_DEF_LIST EXT_DEF須要注意一下是左遞歸狀況,LR語法是能夠處理的,關於這個能夠看以前的博文。學習

好比EXT_DECL_LIST -> EXT_DECL -> VAR_DECL 多個變量名稱聲明能夠推導是一個變量名稱聲明或者多個變量名稱聲明 + 逗號 + 變量名稱聲明ui

VAR_DECL 則能夠是一個標識符或者一個多重指針this

即一個從葉子節點不斷的推導,讀入終結符,最後推導到開始符號,且輸入流也已經讀完指針

/*
*   PROGRAM -> EXT_DEF_LIST
*
*  EXT_DEF_LIST -> EXT_DEF_LIST EXT_DEF
*
*  EXT_DEF -> OPT_SPECIFIERS EXT_DECL_LIST  SEMI
*             | OPT_SPECIFIERS SEMI
*
*
*  EXT_DECL_LIST ->   EXT_DECL
*                   | EXT_DECL_LIST COMMA EXT_DECL
*
*  EXT_DECL -> VAR_DECL
*
*  OPT_SPECIFIERS -> CLASS TTYPE
*                   | TTYPE
*                   | SPECIFIERS
*                   | EMPTY?
*
*  SPECIFIERS -> TYPE_OR_CLASS
*                | SPECIFIERS TYPE_OR_CLASS
*
*
*  TYPE_OR_CLASS -> TYPE_SPECIFIER
*                   | CLASS
*
*  TYPE_SPECIFIER ->  TYPE
*
*  NEW_NAME -> NAME
*
*  NAME_NT -> NAME
*
*  VAR_DECL -> | NEW_NAME
*
*              | START VAR_DECL
*
*/

在語法推導式初始化的過程一共要構建三個數據結構

private HashMap<Integer, ArrayList<Production>> productionMap = new HashMap<>();
private HashMap<Integer, Symbols> symbolMap = new HashMap<>();
private ArrayList<Symbols> symbolArray = new ArrayList<>();
  • ProductionMap的key就是推導式的左邊,value即對應的一個或多個產生式
  • SymbolMap相似ProductionMap,key是推導式的左邊,value是一個或者多個產生式的右邊

Symbol相似Production,也是用來表示產生式的,可是稍有點不一樣,也還包括終結符,在後面也會有不一樣的做用,

//Symbols
public int value;
public ArrayList<int[]> productions;
public ArrayList<Integer> firstSet = new ArrayList<>();
public boolean isNullable;

若是一個非終結符,它能夠推導出空集,那麼這樣的非終結符咱們稱之爲nullable的非終結符

  • symbolArray存儲着每個Symbols對象,在後面也會有不同的做用

Production 產生式類

在上一篇裏說到驗證語法的過程就是在一堆對應語法產生式中推導出答案,Production類就是來表示一個產生式

private int dotPos = 0;
    private int left;
    private ArrayList<Integer> right;
    private ArrayList<Integer> lookAhead = new ArrayList<>();
    private int productionNum = -1;

    public Production(int productionNum, int left, int dot, ArrayList<Integer> right) {
        this.left = left;
        this.right = right;
        this.productionNum = productionNum;
        lookAhead.add(Token.SEMI.ordinal());

        if (dot >= right.size()) {
            dot = right.size();
        }
        this.dotPos = dot;
}
  1. left和right就是產生式的左邊和右邊,都是用以前Token的值來表示
  2. lookahead向前看集合,後面用到再提
  3. dos就像構造這個自動機的輔助工具,以後用到詳細說
  4. productionNum是這個產生式對應編號,這個編號是在初始化語法的時候給定的

小結

這一篇主要介紹了幾個數據結構,這幾個數據結構都是以後構建有限狀態自動機的基礎。原本想把狀態機的構建也寫在這一篇,可是若是加上去以後,篇幅太長,加一部分會感受不成模塊,有點分散,因此自動機的構建就寫在下一篇。

另外個人github博客:https://dejavudwh.cn/

相關文章
相關標籤/搜索