TINY編譯器《編譯原理》

華南理工大學

編譯原理》課程實驗報告

 

實驗題目        實現TINY+編譯器       

源代碼及報告書: (爲了大家的學習着想,此處就不放github的地址了 可以在我的個人上傳資源中找到這份文檔)。


(此處圖片未顯示,可以在github中下載報告書觀看。報告書即爲如下內容)

【實驗目的】

構造一個將TINY+高級程序設計語言轉換爲TINY+虛擬機上的中間代碼的編譯器。

整個實驗包括兩個部分:實驗一完成TINY+編譯器的詞法分析器部分;實驗二完成TINY+編譯器的語法分析器部分、語義分析器部分及中間代碼生成器部分。

【實驗環境】

Microsoft Visual Studio 2015

C++語言實現

分爲實驗一、二。


實驗一、 實現TINY+編譯器的詞法分析器

【實驗目的及要求】

 學生在該實驗中構造TINY+語言的詞法分析程序。具體要求如下:

1   詞法分析器的輸入是源程序文件,輸出是單詞(token)流。

2   構造的詞法分析器必須遵循最長子串原則,例如字符串‘:=’ 應該被識別爲一個代表賦值符號的單詞,而不是識別爲兩個單詞‘:’和 ‘=’。

3   詞法分析器識別出的單詞應該被表示成二元組的形式(Kind, Value),其中Kind表示單詞的種類,Value表示單詞的實際值。要求用如下符號表示不同種類的單詞:

l  KEY 表示關鍵字;

l  SYM 表示特殊符號;

l  ID 表示標識符;

l  NUM 表述數字常量

l  STR 表示字符串常量

4   詞法分析器的任務除了完成對單詞的識別之外,還要檢查程序中的詞法錯誤,給出錯誤的信息以及錯誤在源程序中出現的位置(所在的行號)。詞法錯誤的種類包括:

l  Ø 非法符號,詞法分析器可能識別到一個TINY+程序的字母表中不允許的符號,例如,詞法分析器在一個源程序中識別到$,應報告一個詞法錯誤,發現了一個非法符號;

l  Ø 字符串類型的單詞STRING,單引號不匹配。例如,源程序中出現' scanner,詞法分析分析器應報告錯誤“字符串缺少右引號”;

l  註釋的括號不匹配。例如源程序中出現{this is an example,詞法分析器應報告錯誤“註釋缺少右括號”。

 

 詞法分析實驗過程

 

  1. main函數中將NO_ANALYZENO_CODEPARSE設置爲TRUE,使程序只進行詞法分析過程。
  2. 由於增加了關鍵字(keyword)TINY語言關鍵字如下:

Tiny關鍵字

ifthenelseend  repeatuntilreadwrite

Tiny+新增關鍵字

Orand intboolcharwhiledo

 

其中所有的關鍵字是程序設計語言保留使用的,並且用小寫字母表示,用戶自己定義的標識符不能和關鍵字重複。

  1. 特殊符號定義:

Tiny符號

   :=        =

Tiny+新增符號

>     <=    >=     

 

  1. 詞法分析器中識別的單詞用二元組形式表示(Kind,Value),其中Kind表示單詞的種類,Value表示單詞的實際值。要求用如下符號表示不同種類的單詞:

KEY

關鍵字

SYM

特殊符號

ID

標識符

NUM

數字常量

STR

字符串常量

  1. 詞法分析器的任務除了完成對單詞的識別之外,還要檢查程序中的詞法錯誤,給出錯誤的信息以及錯誤在源程序中出現的位置(所在的行號)。詞法錯誤的種類包括:
  • 非法符號,詞法分析器可能識別到一個TINY+程序的字母表中不允許的符號,例如,詞法分析器在一個源程序中識別到$,應報告一個詞法錯誤,發現了一個非法符號;
  • 字符串類型的單詞STRING,單引號不匹配。例如,源程序中出現' scanner,詞法分析分析器應報告錯誤“字符串缺少右引號”;

註釋的括號不匹配。例如源程序中出現{this is an example,詞法分析器應報告錯誤“註釋缺少右括號”。

實驗過程:

  • GLOBALS.h中的TokenType枚舉中增加相應的類型:

關鍵詞

特殊符號

新增枚舉定義

對應的關鍵詞

新增枚舉定義

對應特殊符號

OR

or

RT

AND

and

LTEQ

<=

INT

Int

RTEQ

>=

BOOL

bool

COMMA

,

CHAR

char

CO

WHILE

while



DO

do



  • SCAN.CgetToken()函數中進行相應修改,讓程序能夠正確識別新添加的關鍵詞以及特殊符號,並且對程序出錯進行相應提示)

詞法分析測試結果

測試一:

 

 

輸入

 

輸入右邊所示文件,運行程序後,結果如下圖:

(下面兩張圖爲同一輸出結果)

 

輸出

 

 

 

測試二:

輸入

 

輸入右邊所示文件,運行程序後,結果如下圖:

(下面兩張圖爲同一輸出結果)

輸出

  

 

 

測試三:(測試錯誤提示)

 

輸入

輸入右邊所示文件,運行程序後,結果如下圖:

其中有兩處錯誤:

  1. 缺少右引號配對
  2. 缺少一個右大括號} 配對

輸出

 

 

 

 

實驗二、實現TINY+的語法分析器、語義分析器以及中間代碼生成器

【實驗目的及要求】

 學生在該實驗中構造TINY+語言的語法分析器、語義分析器以及中間代碼生成器,具體要求如下:

  1. TINY+構造一個遞歸下降語法分析器。該語法分析器對詞法分析器生成的單詞序列進行語法分析,產生一顆抽象語法樹作爲語法分析器的輸出。
  2. 構造TINY+的語義分析器,該語義分析器構建符號表,然後檢查程序中的語義錯誤。
  3. 構造TINY+的中間代碼生成系,該中間代碼生成器將TINY+程序翻譯爲三地址中間代碼。
  4. 要求能檢查程序中語法和語義錯誤,具體包括:
  1. 語法錯誤:
    1. 語法結構的開始符號以及跟隨符號錯誤;
    2. 標識符錯誤,例如在程序的變量聲明中,關鍵字int 後沒有跟隨標識符;
    3. 括號不匹配的錯誤,例如左括號(和右括號 )不匹配。
    4. 符號錯誤,例如賦值語句中要求使用的正確符號是:=,而在關係比較表達式要求使用的正確符號是=
  2. 語義錯誤:
  • 一個標識符沒有聲明就使用,以及一個標識符被聲明不止一次。
  • 一個條件表達式的類型不是布爾類型bool
  • 一個二元操作符的兩個操作數的類型不相等。
  • 賦值語句左右部的類型不相等。

 語法分析實驗過程:

  1. main函數中將NO_ANALYZENO_CODE 設置爲TRUE,使程序只進行PARSE(語法分析)過程。
  2. Tiny+的語法EBNF定義如下(其中紅色是指Tiny+Tiny上新增的語法):
  1. program    -> declarations stmt-sequence
  2. declarations   -> decl ; declarations |ε
  3. decl    -> type-specifier varlist
  4. type-specifier -> int | bool | char
  5. varlist -> identifier { , identifier }
  6. stmt-sequence  -> statement { ; statement }
  7. statement  -> if-stmt | repeat-stmt | assign-stmt | read-stmt | write-stmt | while-stmt
  8. while-stmt -> while bool-exp do stmt-sequence end
  9. if-stmt -> if  bool-exp then stmt-sequence [else stmt-sequence] end
  10. repeat-stmt -> repeat stmt-sequence until bool-exp
  11. assign-stmt -> identifier:=exp
  12. read-stmt  -> read identifier
  13. write-stmt -> write exp
  14. exp -> arithmetic-exp | bool-exp | string-exp
  15. arithmetic-exp -> term { addop term }
  16. addop   -> + | -
  17. term -> factor { mulop factor }
  18. mulop   -> * | /
  19. factor  ->  (arithmetic-exp) | number | identifier

20 bool-exp   -> bterm { or bterm }

21 bterm   -> bfactor { and  bfactor}

22 bfactor -> comparison-exp

23 comparison-exp -> arithmetic-exp comparison-op arithmetic-exp

24 comparison-op  -> < | = | > | >= | <=

  1. tring-exp      -> string

 

  1. 根據EBNF的定義,修改PARSE.C文件,構造遞歸下降語法分析器,產生一棵語法樹。
  • 基本語法樹木的結構如下:

語句系列

語句間通過鏈表相連,除了第一條語句,其他語句均可通過前一個語句的->slibing查找到。

If語句

test位於If語句的child[0],

then-partchild[1],

else-partchild[2]

Repeat

Body位於repeat語句中的child[0],

test位於chlid[1]

Assign

Expression 位於assign語句child[0]

write

 

Expression位於writechild[0]

Op

邏輯運算

Left-operand 位於op語句的child[0],

Right-opreand位於語句的child[1];

while

Test位於while語句的child[0]

Do-part位於child[1]

 

Define

如:

int AB

 

B位於Aslibing中,

其中AB的子節點均未賦值(此處留給將來拓展用)

 

  • 新建一個define的結點類型,用來識別定義語句.
  • 重寫tiny中不適合的語法分析樹枝,按照新的ENBF定義寫出合適的分析函數,從而構建出正確語法樹。

 

語法分析測試結果:

測試一:

輸入

 

 

輸入右邊所示文件,運行程序後,結果如下圖:

   

 

輸出

 

 

語義分析實驗過程:

1main函數中將 NO_CODE 設置爲TRUE,使程序進行語義分析過程。

2一個用TINY+語言編寫的程序包括變量的聲明和語句序列兩個部分。變量聲明部分可以爲空,但一個TINY+程序至少要包含一條語句。

    1. 所有的變量在使用之前必須聲明,並且每個變量只能被聲明一次。
    2. 變量以及表達式的類型可以是整型int,布爾類型bool或者字符串類型char,必須對變量的使用和表達式進行類型檢查。

3構建語義分析器主要包括

  • 符號表的設計以及符號表的相應操作
  1. 符號表保存信息:變量的地址信息、變量被訪問的行號
  2. 變量以其變量名ID,通過鏈式hash儲存查找到相應數據。
  • 語義分析程序本身的操作,包括符號表的構建以及類型檢查。
  1. 符號表的創建:
  • 前序遍歷語法樹:

當遇到定義的語法結點時候,將其變量新增到哈希表中,並存儲其行號位置;

遇到含有變量的非定義語句的結點時,查找該變量位於哈希表中的位置,將該行號儲存到哈希相應表項的next位置。

  1. 類型檢查:
  • 後續遍歷語法樹,對每一個結點進行語義判斷:

1.  if語句結點中,if-test(child[0])的類型必定爲boolean

 

2.  while語句結點中,while-test(child[0])的類型必定爲boolean

3.  op算術運算結點中,child[0]child[1]必須爲integer類型

4.  op邏輯運算結點中,child[0]child[1]必須爲boolean

5.  賦值運算結點中,child[0]child[1]的類型必須一致

6.  ……

  • 在後續遍歷語法樹中,查看變量是否非法使用:
  1. 當結點爲ID結點時候,在哈希表中查看是否已經定義。
  2. 如果該結點爲定義語句結點,則判斷此變量是否重複定義。
  3. 如果該變量已經定義,則將變量的type(類型)賦於定義時候的類型
  4. 進入上一層遍歷,當其類型不符合上一層所需類型時候提示錯誤。(即如在賦值語句中,該變量的類型與賦值語句另一個子節點的類型不一致,提示語義錯誤)。

詞法分析測試結果

測試一:

輸入

 

輸入右邊所示文件,運行程序後,結果如下圖:

 

輸出

  

 

 

測試二:

輸入

 

 

輸入右邊所示文件,運行程序後,結果如下圖:

 

 

輸出

 

 

測試三:(測試錯誤提示)

輸入

輸入右邊所示文件,運行程序後,結果如下圖:

代碼中有三處錯誤:

    1. A重複定義
    2. A>3Achar型,比較錯誤
    3. A:=1中賦值錯誤。類型不一致

輸出

 

中間代碼生成實驗過程:

1 main函數中將 NO_PARSENO_ANALYZENO_CODE 設置爲FALSE,使程序進行中間代碼生成過程。

2 構造TINY+的中間代碼生成系,該中間代碼生成器將TINY+程序翻譯爲三地址中間代碼。

3在原tiny中間代碼生成器的實現位於cgen.ccgen.h中。其接口爲codeGen(TreeNode* syntaxTree,char* codefile).

主要功能爲:

  • 函數的開始,產生一些註釋以及建立運行時環境的指令。
  • 對語法樹調用cGen函數,對每一個語法樹結點生成相應指令
  • 最後生成HALT指令,結束目標代碼生成。

註釋以及指令均寫在某指定.tm文件上,供TM程序調用並且運行。

  1. Tiny+tiny的基礎上進行修改。其中codeGen函數將保持不變,修改其cGen函數即可。
  • 修改cGen函數調用的getExp()函數。
  • 增加OpK的類型,即在原有功能代碼基礎上,增加新增符號其操作的中間代碼生成步驟。即新增符號RT,EQ,LTEQ,RTEQ.

如增加“>”的中間代碼生成過程

  • 增加LogicOpK的處理(即增加ANDOR的中間代碼的生成)

此處中間代碼生成比算術運算的中間代碼複雜得多,根據TM程序的指令以及其操作原理,寫出ANDOR中間生成代碼功能:

  1. 修改cGen函數調用的getStmt()函數。增加語句while的中間代碼生成。

此處通過Tiny+文法規則產生對於的三地址中間代碼屬性,完成while生成中間代碼功能。

 

 

中間代碼測試結果

測試一:

輸入

輸入右邊所示sample.tny文件到TINY+,運行程序後,獲得example.tm文件。

 

example.tm文件輸入TM程序中,運行結果如圖:

輸入x=5,輸出120(正確)

輸入x=101,跳出if(正確)

輸入x=4,輸出24(正確)

輸入x=-1,跳出if(正確)

輸出

Example.TM文件如下截圖:

 

輸入上述文件到TM程序中運行,結果如下:


  • TINY+編譯器的設計編寫中,將書上學到的知識運用到實踐中,從而完成TINY編譯器的拓展。
  • 加深對文法的理解、詞法分析中的無窮自動機(NFA)轉爲有窮自動機(DFA)再轉成最間DFA的過程以及原理有了新的認識。
  • 語法分析是耗費我最多時間的地方。TINY+語法定義稍有不準確,將爲後期代碼調試帶來很大的麻煩。並且語法的定義在精簡壓縮方面需要下很大功夫。從給定的EBNF定義,到真正可運行的程序代碼,期間需要大量的測試調試。
  • 在構建語法生成樹中,曾多次修改之前寫的詞法分析器,並且重寫了之前寫的質量不高的語法分析器,將代碼結構進行解耦,方便了後續的調試以及改進。
  • 在中間代碼生成中,深入瞭解生成三地址的中間代碼方式。其中就if語句的中間代碼生成進行了大量的測試,最終將原先不太瞭解的彙編代碼看懂,並且寫出了邏輯表達式ANDOR、以及While的中間代碼生成功能。
  • TM程序中,通過一步步調試程序代碼,學會執行彙編語言,其中學會自定義寄存器、內存緩衝區、指令緩衝區來執行彙編語言。也在此過程中,改正了中間代碼生成中的許多bug
  • TINY+編譯器存在着許多不足,仍需要進行改造進化。但完成該編譯器後,已經向編譯原理世界邁進一大步了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章