1. 程式人生 > >編譯原理課設實驗報告

編譯原理課設實驗報告

課 程 設 計 報 告

設計題目:編譯器的設計與實現

班    級: 計算機1304

組長學號:20133894

組長姓名:mxp

指導教師:zl

設計時間:2015年12月

設計分工

組長學號及姓名:mxp 20133894

分工:討論文法,設計符號表,資料結構定義,中間程式碼生成,多維陣列,子程式,記憶體管理

組員1學號及姓名:wqg 20133899

分工:語法分析邏輯控制部分,目的碼生成邏輯控制部分,視覺化介面,子程式,中間程式碼生成

組員2學號及姓名:hx 20133910

分工:語法分析,語法分析表示式部分,目的碼生成表示式部分,多維陣列,基本塊劃分

摘  要

在經過一個學期的編譯原理課程學習之後,我們迎來了這次寶貴的課程設計使我們有機會完成一個屬於我們自己的編譯器。最終經過兩週的努力,組內成員在完成了編譯器的設計也收穫良多。

這次編譯器設計,完成了前端,後端的設計與實現,並完成了幾乎所有的附加內容。首先是詞法分析識別Token序列,由於語法較為龐大,所以語法分析中使用相對容易實現的遞迴下降子程式方法,通過插入翻譯動作來填符號表,並形成中間程式碼,即四元式序列,四元式中使用自行設計的符號表得到的較為完善的資料和引數可以為後續的目的碼生成提供便利。

在目的碼生成中,我們選擇了80X86組合語言作為目的碼。演算法進行了基本塊的劃分,並考慮的單暫存器使用情況和多個暫存器進行定址與跳轉時的使用。在跳轉命令生成時,由於各條指令長度不同,因此,使用標號跳轉來替代地址跳轉,最終生成了可直接在8086上執行的彙編程式碼。

拓展內容部分,程式中以函式的形式實現了子程式的設計,並且需有主函式進行呼叫。為此我們對符號表與活動記錄進行了必要的修改使得我們的函式採用堆疊式記憶體管理,支援遞迴呼叫。陣列的實現中在翻譯文法中對陣列進行翻譯,將其在四元式中將下表轉化為記憶體中偏移量。且經過反覆修改,支援高維陣列的實現。在完成了所有設計後,我們還編寫了圖形使用者介面。使得編譯器更加易用。

通過半個月的努力,我們完成了一份具有較好的實用性的編譯器。在程式設計中,大量使用STL泛型程式設計,使得程式的易用性更好,並使用了多種技術與技巧,使得程式的執行記憶體較為安全,魯棒性更強。在掌握編譯原理知識的同時也對程式設計有了更加深入的理解。組內成員通過合作與交流更是獲益良多。

關鍵字: 編譯原理,遞迴下降,四元式,Token,多維陣列,堆疊式記憶體,STL泛型程式設計

目  錄

1. 概述                                                      7

2. 課程設計任務及要求                                        7

   2.1 設計任務                                              7

   2.2 設計要求                                              12

3. 演算法及資料結構                                           12

   3.1演算法的總體思想(流程)                                 13

3.2開發流程                                              13

   3.3 詞法分析模組                                          15

     3.3.1 功能                                              15

3.3.2 資料結構                                          15

     3.3.3 演算法                                              18

   3.4 語法分析模組                                          18

     3.4.1功能                                               18

     3.4.2演算法                                               18

3.5 符號表模組                                            21

3.5.1功能                                               21

3.5.2 資料結構                                          21

3.6 語義分析及中間程式碼生成模組                            24

3.6.1功能                                               24

3.6.2 資料結構                                          25

3.6.3演算法                                               25

3.7 優化及目的碼生成模組                                27

3.7.1功能                                               27

3.7.2 資料結構                                          27

3.7.3演算法                                               28

4. 程式設計與實現                                           32

4.1 程式流程圖                                           32

    4.2 程式說明                                             32

    4.3 實驗結果                                             34

5. 結論                                                     36

6. 參考文獻                                                 37

7. 收穫、體會和建議                                         38


一. 概述 

編譯原理課程兼有很強的理論性和實踐性,是計算機專業的一門非常重要的專業基礎課程,在系統軟體中佔有十分重要的地位。編譯原理課程設計是本課程重要的綜合實踐教學環節,是對平時實驗的一個補充。通過編譯器相關子系統的設計,使學生能夠更好地掌握編譯原理的基本理論和編譯程式構造的基本方法和技巧,融會貫通本課程所學專業理論知識;培養學生獨立分析問題、解決問題的能力,以及系統軟體設計的能力;培養學生的創新能力及團隊協作精神。

二. 課程設計任務及要求

2.1 設計任務

我們選取的題目是:一個簡單文法的編譯器的設計與實現。

我們選擇實現C語言的編譯器,選取的文法是1988年Brian W.Kernighian和Dennis M.Ritchie,Prentice Hall三人所更新的標準C語言的文法,由於課程設計時間有限,我們對文法進行了刪改,在保證文法的實用性的條件下,刪去了列舉,指標,GOTO語句,精簡了資料型別。在後來的語法分析和語義分析以及目的碼生成的過程中,我們還對刪改後的文法進行了進一步的調整和改進。

最終實現的文法如下:

<constant-expression> ::= <conditional-expression>

<conditional-expression> ::=<logical-or-expression> { ? <expression> : <conditional-expression> }*

<logical-or-expression> ::= <logical-and-expression> { || <logical-and-expression> }*

<logical-and-expression> ::= <inclusive-or-expression> { && <inclusive-or-expression> }*

<inclusive-or-expression> ::= <exclusive-or-expression> { | <exclusive-or-expression> }*

<exclusive-or-expression> ::= <and-expression> { ^ <and-expression> }*

<and-expression> ::= <equality-expression>{ & <equality-expression> }*

<equality-expression> ::= <relational-expression>{ w0 <relational-expression> }*

            w0 ::=  == | !=

<relational-expression> ::= <additive-expression> { w1 <additive-expression> }*

            w1 ::=  > | < | <= | >=

<additive-expression> ::= <multiplicative-expression>{ w2 <multiplicative-expression> }*

            w2 ::= + | -

<multiplicative-expression> ::= <unary-expression> { w3 <unary-expression> }*

            w3 ::= * | / | %

<unary-expression> ::= <unary-operator> <unary-expression>

                       | <constant>

                       | ( <constant-expression> )

                       | <primary-expression>

                       | <function-call-expression>

<primary-expression> ::= <identifier> { [ <constant-expression> ] }*

<constant> ::= <integer-constant>

             | <real-constant>

<expression> ::= <assignment-expression> { , <assignment-expression> }*

<assignment-expression> ::= <primary-expression> <assignment-operator> <constant-expression>

<assignment-operator> ::= =

<unary-operator> ::= - | ~ | !

<type-name> ::= { const }? <type-specifier>

<type-specifier> ::= char

                   | int

                   | real

<declarator>  ::= <identifier>  { [ {<constant-expression>}? ] }*

<declaration> ::=  <type-name> <init-declarator> { <init-declarator> }*

<init-declarator> ::= <declarator>

                    | <declarator> = <initializer>

<initializer> ::= <constant-expression>

                | { <initializer-list> }

                | { <initializer-list> , }

<initializer-list> ::= <initializer>

                     | <initializer> , <initializer-list>

<original-statements> ::= <type-specifier><identifier>()<compound_statements>

<compound-statement> ::= { {<declaration>}* {<statement>}* }

<statement> ::=<expression-statement>

              | <area-statement>

              | <selection-statement>

              | <iteration-statement>

              | <return-statement>

<area-statement> ::= { { <statement> }* }

<expression-statement> ::= {<expression>}? ;

<selection-statement> ::= if ( <constant-expression> ) <area-statement>

                        | if ( <constant-expression> ) <statement> else <area-statement>

<iteration-statement> ::= while ( <constant-expression> ) <area-jump-statement>

               | for ( {<expression>}? ; {<constant-expression>}? ; {<expression>}? ) <area-jump-statement>

<jump-statement> ::= continue ;

                  | break ;

                  | <expression-statement>

                  | <area-jump-statement>

                  | <selection-jump-statement>

                  | <iteration-statement>

                  | <return-statement>

<area-jump-statement> ::= { { <jump-statement }* }

<selection-statement> ::= if ( <constant-expression> ) <area-jump-statement>

                        | if ( <constant-expression> ) <statement> else <area-jump-statement>

限制:

const只能宣告變數,後面必須跟立即數,陣列只宣告不初始化

 2.2 設計要求

1保證文法的實用性和正確性。

2、能夠實現完整的從簡單C語言到8086組合語言的編譯過程。

3、實現GUI,保證編譯器良好的人機互動環境。

4、進行必要的型別檢查和自動提升。

5、實現多維陣列的宣告和使用。

6、實現子程式呼叫並進行堆疊式記憶體管理。

7、多樣化的語法語義錯誤提醒。

8、確定測試方案,選擇測試用例,對系統進行測試;

9、執行系統並要通過驗收,講解執行結果,說明系統的特色和創新之處,並回答指導教師的提問;

10、提交課程設計報告。

三、演算法及資料結構

3.1演算法的總體思想(流程)

3.2開發流程

3.3 詞法分析模組

3.3.1 功能

完成從輸入的字元流到輸出的Token序列的轉化過程,能夠識別關鍵字,數字,識別符號和界符,並將識別符號填入符號表。

3.3.2 資料結構

//Token作為基類,成員是tag標號

class Token

{

    private:

        int tag;

    public:

        Token(){}

        Token(int i)

        {

            tag=i;

        }

        int get_tag()

        {

            return tag;

        }

        //虛擬函式部分,用於繼承實現

virtual int get_numvalue(){}

        virtual double get_realvalue(){}

        virtual int get_lexeme(){}

        virtual string get_lexeme_str(){}

};

//Num類是Token的子類,繼承了tag,增加了成員value,儲存整數

class Num: public Token

{

    private:

        int value;

    public:

        Num(int t,int v):Token(t),value(v){};

        int get_numvalue()

        {

            return value;

        }

};

//Real類是Token的子類,繼承了tag,增加了成員value,儲存浮點數

class Real: public Token

{

    private:

        double value;

    public:

        Real(int t,double v):Token(t),value(v){};

        double get_realvalue()

        {

            return value;

        }

};

//Word類是Token的子類,繼承了tag,增加了成員lexeme,儲存字串

class Word: public Token

{

    private:

        int lexeme;

    public:

        Word(int t,int v):Token(t),lexeme(v){};

        int get_lexeme()

        {

            return lexeme;

        }

        string get_lexeme_str()

        {

            //識別符號

            if((this->get_tag())==ID) return idwords[lexeme];

            //型別 

  else if((this->get_tag())==TYPE) return typewords[lexeme];

            //關鍵字 

else if((this->get_tag())==KEY) return keywords[lexeme];

           //字串 

else return strwords[lexeme];

        }

};

3.3.3 演算法

用自動機來識別token,自動機如圖所示:

3.4 語法分析模組

3.4.1功能

根據輸入的Token判斷輸入檔案有無語法錯誤,即判斷輸入檔案是否為該文法的一個句子。

3.4.2演算法

採取遞迴下降子程式法進行判斷,這部分比較簡單,由於我們的文法很大,限於篇幅,這裡不再一一展示每一個產生式對應的程式框圖,文法在2.1已經給出。部分流程圖及對應程式碼如下:

1:表示式語句Expression

void Expression()

{

    Assignment_Expression();

     while  (Token_List[Token_List_Index]->get_tag()==',')

     {

        Token_List_Index++;

        Assignment_Expression();

     }

}

2:選擇語句 Selection_Statement

void Selection_Statement()

{

if(Token_List[Token_List_Index]->get_tag()==KEY&&Token_List[Token_List_Index]->get_lexeme_str()=="if")

        Token_List_Index++;

    else

       synax_error=true;

    if (Token_List[Token_List_Index]->get_tag()=='(')

        Token_List_Index++;

    else

       synax_error=true;

    Constant_Expression();

    if (Token_List[Token_List_Index]->get_tag()==')')

        Token_List_Index++;

    else

       synax_error=true;

    Area_Statement();

    if(Token_List[Token_List_Index]->get_tag()==KEY&&Token_List[Token_List_Index]->get_lexeme_str()=="else")

    {

             Token_List_Index++;

        Area_Statement();

    }

}

3.5 符號表模組

3.5.1功能

符號表是識別符號的動態語義詞典,屬於編譯中語義分析的知識庫。符號表的功能包括定義和重定義檢查,型別匹配校驗,資料的越界和溢位檢查,值單元儲存分配資訊,函式的引數傳遞與校驗等等。

3.5.2 資料結構

符號表一般是通過Token中的指標來訪問的,在我們的開發中,Token裡儲存了必要的資訊,所以為了減少二者的耦合性,我們採用Token序列中識別符號第一次出現的序號,來訪問符號表的表項,可以稱為邏輯意義上的“指標”。

符號表內容裡除了Token序列序號tid,還應該包括typ,指向型別表,cat代表種類,包括常量,變數,函式等等,addr代表地址,根據識別符號種類不同,含義有所不同。

struct Synb

{

    int tid;//token_index

    int typ;

    /*

    i r c a

    1 2 3 4

    */

    int cat;

    /*

    c v f

    1 2 3

    */

    int addr;

    int pid;

    Synb(int tid)

    {

        this->tid=tid;

        this->pid=curpid;

        this->typ=this->cat=this->addr=0;

    }

};

vector<Synb> Synbl;

// function list

struct Pfinf

{

    int Size;

    Node Ret;

};

vector<Pfinf> Pfinfl;

//常量表

vector<double> Consl;

//活動記錄

int Vall=0;

符號表內容裡除了基本內容以外,我們增加了多維陣列的內容,為了實現C語言風格的陣列訪問方法,我們需要保證給陣列分配連續的棧空間,同時記錄下來型別及每一維的長度,從而能夠訪問任意陣列元素。這些資訊我們放在了型別表中來實現:

//型別表

struct Typeu

{

    int typ;

    vector<int> Tlen;

    /*eg:

    itp

    len1

    len2

    ...

    */

    Typeu(int typ)

    {

        this->typ=typ;

    }

};

vector<Typeu> Typel;

符號表結構如圖所示:

3.6 語義分析及中間程式碼生成模組

3.6.1功能

這個部分的主要功能就是生成中間程式碼,同時檢測語義錯誤,進行型別檢查等等。我們選取四元式作為中間程式碼表示形式。

3.6.2 資料結構

struct Middle_Code_Unit{

    int Operator;//

    Node Target1;//運算元1

    Node Target2;//運算元2

    Node Result;//目的運算元

};

struct Node{

    int Ein;   // 0: empty  1:identifier 2:num 3:arr 4.ret 5.fun

    int Addr;

    int Size;

    double Num;

    string Name;

};

3.6.3演算法

通過在語義分析的過程中插入翻譯動作,得到四元式輸出結果。

在我們開發過程中,中間程式碼生成分成了兩個部分,第一個部分是宣告語句的翻譯,這個部分要進行符號表的新增和查詢,訪問陣列元素雖然不是宣告語句,但是也屬於這個部分,原因是動態訪問陣列元素如a[i][j],是無法在編譯時得到地址的,只有執行時才能計算得出地址,所以計算地址的過程也會成為中間程式碼輸出。

第二個部分是操作語句的翻譯,這個部分相對前一部分比較簡單,需要維護符號棧SYN和操作符棧SEM,基本上符合課堂上老師所講的基本方法,需要注意的地方是,我們增加了FOR迴圈,考慮中間程式碼生成的時候還要考慮目的碼生成的地址回填問題,才能避免出現邏輯錯誤。

由於文法較大,大部分邏輯控制和表示式的翻譯文法課堂上已經講過,限於篇幅,這裡僅給出我們的部分文法的翻譯動作例項:

1:訪問多維陣列元素arr[i][j][k]的翻譯動作

演算法:

1. 開始

2. 遇到識別符號 PUSHSEMarr

3. 遇到左括號,PUSHSYN+

4. 遇到運算元iPUSHSEMi

5. 如果不是第一個左括號則執行Quat(),否則跳過

6. 遇到右括號,PUSHSYN,*

7. 遇到運算元jPUSHSEM,j

8. Quat()

9. 如果不是最後一個右括號則跳轉到2,否則Quat_a()

10. 結束。

演算法中的Quat操作即從SYN中取出運算子,從SEM中取出兩個運算元,並計算結構,步驟9中的Quat_a()與這個操作基本類似,區別在於存放運算結果的方式,正常情況下運算結果都是臨時變數,然而考慮到目的碼生成,為了區別於變數,在計算出陣列元素地址後,並不能把它當做臨時變數,而應該是變數的地址,也就是說訪問這個元素就要對運算結果進行兩次取值操作。

2FOR迴圈語句的翻譯動作

文法:

<iteration-statement> ::= for ( { <expression> }? ; { <constant-expression> }? ; { <expression> }? ) <area-jump-statement>

插入翻譯動作:

for ( { <expression> }? ; “GENFR” { <constant-expression> }? ; “GENFC” “GENFD” { <expression> }? ) “GENFJ” <area-jump-statement> “GENFE”

說明:

GENFR是標號,用於表示for迴圈的開始

GENFCFJMP,用於根據constant-expression來跳轉

GENFD是無條件JMP,用於執行area-jump-statement

GENFJ是無條件JMP,用於執行constant-expression

GENFE是無條件JMP,用於執行第二個expression

for迴圈的特殊之處在於後面的area-jump-statement和第二個expression之間的執行順序和讀入順序是不同的,所以順序翻譯的時候就需要多加一些跳轉指令,並藉助地址回填的方法來保證目的碼的正確順序。

3.7 優化及目的碼生成模組

3.7.1功能

首先對中間程式碼劃分基本塊,然後根據中間程式碼,儘可能簡潔地生成對應的8086彙編程式碼,並保證目的碼彙編通過。

3.7.2 資料結構

//目的碼 

struct Final_Code_Unit{

    int   Label_Index;

    string   Op;   //label或者操作

    string   Dst;  //目標運算元或目標暫存器

    string   Sour;  //源運算元或源操作暫存器

};

3.7.3演算法

劃分基本塊的演算法在課堂上講過,比較簡單。得到基本塊後,開始對每一個基本塊進行中間程式碼到目的碼的翻譯工作。在每一塊開始之前清空暫存器,在根據中間程式碼中的操作符進行不同的處理,在每一次處理時均需要考慮暫存器是否被佔用,被誰佔用以進行目的碼的優化。

這部分並沒有太大的難度,主要需要考慮周全以保證各種情況的中間程式碼都能夠準確無誤的翻譯。

for(i=0;i<Block.size();i++)

{

     acc=-1;

     acc_k=-1;

     for(j=0;j<Block[i].size();j++)

     {

        switch(Block[i][j].Operator)

        {

                    case '+':

                    case '&':

                    case '|':

                    case '^':

                       ......

                       break;

                    case '-':

                       ......

                       break;

                    ......

          }

      }

  }

由於程式碼長度各不相同,而我們希望能得到可以直接在8086上執行的程式碼,因此此次課程設計的迴圈,選擇控制邏輯,我們使用標號跳轉的方式來執行,

使用標號棧來對迴圈和跳轉進行控制,我們使用如下三個容器作為棧使用。

static vector<string>Label;//用於ifwhile跳轉

static vector<string>For_Label;//用於FOR的條件檢查跳轉

static vector<string>For_Do_Label;//用於FOR的執行後進行的迭代器操作跳轉。

當在四元式中遇到THEN時將跳轉地址置成“IFEMPTY

遇到ELSEIFEND回填。

當四元式遇到ELSEIFIFEMPTY

遇到IFEND檢查回填。

WHILE 中遇到DO即生成LABEL,然後將當前LABEL壓棧。遇到WHEND將LABEL彈出 回填。

FOR迴圈中,當遇到FOR_CHECK,首先生成標號並將標號壓棧同時將此處跳轉命令的跳轉地址置為FORCHECKEMPTY

遇到FORJUMP生成標號,並將標號壓棧。之後生成新標好並將標號回填。

當遇到FOR_END時,將生成下一跳轉地址並將標號回填。同時通過棧跳轉至FORJUMP.

#include <sstream>//使用一個SSTREAM

string num2str(double t)

{

    stringstream ss;

    ss<<t;

    return ss.str();

}使用此函式將數字轉化為字串,儘管生成的標號可讀性極差,其生成方法卻變得簡單。

FOR_DO舉例

            case FOR_DO:

                t.Op="JMP";

                t.Label_Index=-1;

                t.Dst="FOR_DOEMPTY";//標號預留

                t.Sour="";

                Final_Code.push_back(t);

                t1.Op="LABEL"+num2str(Label_Index);//生成新的標號

                t1.Label_Index=Label_Index;

                t1.Dst="";

                t1.Sour="";

                Label_Index++;

                Final_Code.push_back(t1);

                For_Do_Label.push_back(t1.Op);//將STRING型別的Op壓入FOR_DO_LABEL棧。

                break;  case FOR_JUMP:

                t.Op="JMP";

                t.Label_Index=-1;

                t.Dst=For_Label.back();//將FORLABEL棧中的符號彈出。

                For_Label.pop_back();

                t.Sour="";

                Final_Code.push_back(t);

                t1.Label_Index=Label_Index;

                t1.Op="LABEL"+num2str(Label_Index);//生成新的標號

                Label_Index++;

                t1.Dst="";

                t1.Sour="";

                Final_Code.push_back(t1);

                for (int i=Final_Code.size()-1;i>=0;i--)//找到FOR_DOEMPTY回填。

                    if (Final_Code[i].Dst=="FOR_DOEMPTY")

                    {

                        Final_Code[i].Dst=t1.Op;

                        break;

                    }

                    break;

四、程式設計與實現

 4.1 程式流程圖

程式流程與3.1中的演算法整體流程一致,此處不再贅述。

4.2 程式說明

void Synbl_Push_Name(int tid);//將符號的token_id壓入符號表

void Synbl_Push(Declar_Unit du,inttyp,bool cat,bool arr,double val);   //將符號壓入符號表

int Synbl_Push_Fun_Typ(int tid,int typ);//將函式型別與其tokenid 壓入符號表

void Synbl_Push_Fun_Size(int tid,int Size);

//記錄函式所佔用的空間

void init();

void Pop_Sem();//中間程式碼生成彈出識別符號棧

void Pop_Syn();//中間程式碼生成彈出符號棧

void Push_Sem(int tid);//中間程式碼壓入識別符號棧

void Push_Syn(int op);//中間程式碼壓入識別符號棧

void Quat();//四元式生成

void Quat_a();//四元式陣列生成。

void Assign();//等式生成。。。。後續部分動作省略

void Conditional_Expression();//條件表示式

void Constant_Expression();//常表示式

void Logical_Or_Expression();//邏輯表示式

void Expression();//表示式

void Logical_And_Expression();

void Inclusive_Or_Expression();

void Exclusive_Or_Expression();

void And_Expression();

void Equality_Expression();

void Relational_Expression();

void Additive_Expression();

void Multiplicative_Expression();

void Unary_Expression();//一元表示式

void Primary_Expression();//二元表示式

void Assignment_Expression();//等式表示式

double Real_Constant();

int Integer_Constant();

int Type_Specifier();

Declar_Unit Declarator();//宣告單元宣告

void Declaration();//宣告

void Init_Declarator();//初始化

void Initializer();

void Initializer_List();//初始化表列

void Initializer_End();//初始化結束

void Compound_Statement();//複合表示式

void Statement();

void Expression_Statement();//表示式語句

void Selection_Statement();//選擇語句

void Iteration_Statement();//迭代語句

void Jump_Statement();//跳轉語句

//

void Return_Statement();

void Oringinal_Statements();//最初始語句

int Function_Call_Expression();//函式呼叫語句

4.3 實驗結果

編譯器介面用matlab語言編寫,顯示介面需要首先安裝matlab環境。

五、結論

我們的編譯器能夠實現基本的表示式操作語句,邏輯控制語句,支援多維陣列這樣的高階資料結構,同時實現了堆疊式記憶體管理以及子程式呼叫,並且可以得到8086編譯通過的彙編程式碼,基本上完整的實現了2.2中的預定設計目標,拓展內容部分,程式中以函式的形式實現了子程式的設計,並且需有主函式進行呼叫。為此我們對符號表與活動記錄進行了必要的修改使得我們的函式採用堆疊式記憶體管理,支援遞迴呼叫。陣列的實現中在翻譯文法中對陣列進行翻譯,將其在四元式中將下表轉化為記憶體中偏移量。且經過反覆修改,支援高維陣列的實現。在完成了所有設計後,我們還編寫了圖形使用者介面。使得編譯器更加易用。

通過半個月的努力,我們完成了一份具有較好的實用性的編譯器。我們為了避免記憶體分配受限,大量使用STL泛型程式設計採,同時為了保證程式的可拓展性,每一個模組都明確定義了類或結構體來作為介面。在掌握編譯原理知識的同時也對程式設計有了更加深入的理解。組內成員通過合作與交流更是獲益良多。

六、參考文獻

1、陳火旺.《程式設計語言編譯原理》(第3版). 北京:國防工業出版社.2000.

2、美 Alfred V.Aho Ravi Sethi Jeffrey D. Ullman著.李建中,姜守旭譯.《編譯原理》.北京:機械工業出版社.2003.

3、美 Kenneth C.Louden著.馮博琴等譯.《編譯原理及實踐》.北京:機械工業出版社.2002.

4、金成植著.《編譯程式構造原理和實現技術》. 北京:高等教育出版社. 2002.

七、 收穫、體會和建議。

通過這次課程設計,我們小組的三個人有很多的收穫:

1. 複習了C語言, C++以及資料結構的知識。

2. 更清晰的學習了實際的編譯過程。

3. 鍛鍊了我們分工合作完成專案的能力。

4. 瞭解了符號表在編譯各個階段的作用。

5. 鍛鍊了我們的程式設計能力。

6. 複習了C語言記憶體管理的相關內容。

7. 進一步體會到了老師上課所講的執行時刻和編譯時刻,動態和靜態的區別。

我們的體會是編譯原理雖然是一門理論,但是它非常注重實踐,它是一門在不斷地實踐中總結出來的課程。在實際操作中,編譯過程的細節往往會因為文法以及需求的不同而有差異,但是整體流程幾乎都是不可缺少的。正因如此,每一個編譯步驟都是可以有很多方法來實現的,採取哪種方法並不重要,重要的是能不能解決問題。

最後希望老師在課堂的講解中能夠稍稍再拓展一些實際的我們現實生活中使用的編譯器的編譯實現技術。