編譯原理之詞法分析程序

實驗課上實現了對詞法分析程序的編寫。整個過程中,最爲困難的就是對整個編譯過程的設計。在這裏整理一下我的整個從設計到實現過程以及中間出現的問題。

問題描述

假定一種高級程序設計語言中的單詞主要包括關鍵字begin、end、for、if、then、else標識符整數六種關係運算符,試構造能識別這些單詞的詞法分析程序。

下面是各類單詞符號以及編碼對應表

clipboard.png

最後我們將實現輸入一段代碼,然後輸出鍵值對的形式:

輸入:
if myid >= 15 then x = y
輸出:
(IF, " ")
(ID, "myid")
(GE, " ")
(INT, "15")
(THEN, " ")
(ID, "x")
(EQ, " ")
(ID, "y")

狀態轉換圖

首先同時也是最爲複雜的就是如何設計狀態轉換圖。

根據我們過去使用過的各種編程語言,再結合上面所給的類型表格,可以簡單分爲幾種情況:

1.字母開頭
1.1 關鍵字
1.2 標識符(字母開頭的字母數字串)
2.數字開頭: 整數
3.比較符號: 關係運算符

那麼現在就是如何將上面的情況轉換成圖了。

clipboard.png

通過對第一種情況的翻譯,我們會得到上面的狀態轉換圖。可以發現,當我們將各種情況都列好以後,轉換圖好像也沒有想象的那麼難了。

然後跟上面的方法一樣,我們將得到一個較爲完整的轉換圖:

clipboard.png

可以看到,上面的轉換圖不僅包含了轉換的條件,同時,也標明瞭轉換過程中用到的方法。這也是我們下一步要解決的。

功能函數

狀態轉換圖中一共有這麼幾個功能函數:GETCHAR(讀取一個字符)CAT(拼接單詞)LOOKUP(檢查是否爲關鍵字)RETRACT(回退一個字符)OUT(輸出)

然後就到我們要實際編寫代碼的時候了。

1.GETCHAR

由於在實現這個程序的時候,設計是由文件中讀取出源程序,然後輸出到控制檯上。所以會用到一些文件的讀取操作。相應的,C語言的類庫中有一些已經寫好的函數,所以我們可以直接拿來用:

char ch = fgetc(fp);

fp是一個函數指針,fgetc函數的作用就是讀取一個字符,然後將指針向後移動一個。

2.CAT

拼接的實現是利用了字符數組實現的:

char TOKEN[20];

定義一個數組用來存放單詞,然後就可以將上面獲取的單詞依次存如數組中。

TOKEN[i] = ch;      // 保存字符
i++;

當然,這需要一個循環:

while (isalnum(ch)) {
    TOKEN[i] = ch;      // 保存字符
    i++;                // 長度加一
    ch = fgetc(fp);     // 讀取下一個字符
}

3.LOOKUP

這個的實現也比較簡單,只需要按順序比價保留字列表,看時候與剛剛掃描出的單詞匹配就可以了。

int lookup(char *token) {
    int n = 0;

    // 依次比較所有保留字
    while (strcmp(KeyWordTable[n], KEY_WORD_END)) {

        // 比較token所指向的關鍵字和保留字表中哪個關鍵字相符
        if (!strcmp(KeyWordTable[n], token)) {
            return n + 1;     // 返回關鍵字對應的編碼
        }
        n++;
    }
    return 0;               // 單詞不是關鍵字,而是標識符
}

4.RETRACT

同讀取一樣,這個的實現也是藉助於類庫中的方法:

fseek(fp, -1, 1);       // 將文件指針指向當前位置的前一個位置

5.OUT

最後輸出的功能就比較簡單了,只要將所有的情況都不遺漏,就可以了。

switch (code) {
        case BEGIN:
            cout << "(BEGIN, \" \")" << endl;
            break;
        case END:
            cout << "(END, \" \")" << endl;
            break;
        case IF:
            cout << "(IF, \" \")" << endl;
            break;
        case THEN:
            cout << "(THEN, \" \")" << endl;
            break;
        case ELSE:
            cout << "(ELSE, \" \")" << endl;
            break;
        case ID:
            cout << "(ID, \"" << value << "\")" << endl;
            break;
        case INT:
            cout << "(INT, \"" << value << "\")" << endl;
            break;
        case LT:
            cout << "(LT, \" \")" << endl;
            break;
        case LE:
            cout << "(LE, \" \")" << endl;
            break;
        case EQ:
            cout << "(EQ, \" \")" << endl;
            break;
        case NE:
            cout << "(NE, \" \")" << endl;
            break;
        case GT:
            cout << "(GT, \" \")" << endl;
            break;
        case GE:
            cout << "(GE, \" \")" << endl;
            break;
        default:
            cout << "編碼錯誤!" << endl;
            break;
    }
}

總結

通過對編譯原理的學習,越發感覺思維的重要性。

當我們有了思想後,代碼實現不過只是一個工具而已。

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