首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

小語言詞法分析器的設計與實現

     編輯:關於VC++

前言

近日在 VC 知識庫中看到一個 Pascal 語言的詞法分析程式,代碼結構較為複雜。其實詞法分析器的核心原理大同小異,只要掌握了簡單語言的詞法分析方法,擴展到複雜語言只需增加關鍵字與條件判斷。詞法分析是編譯器的基礎階段,也是最容易入手的部分。本文將展示一個針對簡單語言的詞法分析器完整實現過程。

語言文法定義

目標語言的文法規則如下(採用 BNF 範式):

G[<程序>]:
<程序> ::= <程序首部> ; <分程序> .
<程序首部> ::= program <標識符>
<分程序> ::= <復合語句>
<復合語句> ::= begin <語句序列> end
<語句序列> ::= <語句> { ; <語句> }
<語句> ::= <賦值語句> | <復合語句> | <條件語句>
<賦值語句> ::= <標識符> := <表達式>
<條件語句> ::= if <布爾表達式> then <語句> else <語句>
<表達式> ::= <項> { (+|-) <項> }
<項> ::= <因式> { (*|/) <因式> }
<因式> ::= <標識符> | <無正負號常量> | '(' <表達式> ')'
<布爾表達式> ::= <表達式> <關系運算符> <表達式>
<關系運算符> ::= = | < | <= | > | >= | <>
<標識符> ::= <字母> { <字母> | <數字> }
<無正負號常量> ::= <數字> { <數字> } [ . <數字> { <數字> } ]
<字母> ::= a | b | c | ... | z
<數字> ::= 0 | 1 | 2 | ... | 9

輸入以 "#" 作為結束符。

單詞符號與類別編碼表

根據文法提取關鍵字、運算符、界符,建立單詞符號表:

單詞符號類別編號
標識符1
常數2
if3
then4
else5
program6
begin7
end8
+9
-10
*11
/12
(13
)14
>15
>=16
<17
<=18
<>19
:=20
;21
.22
,23

核心分析函數實現

詞法分析器的核心是 parse 函數,負責從輸入文件讀取字符流,識別單詞並將結果寫入輸出文件。主要邏輯包含:

  • 跳過空白字符(空格、Tab、換行)並記錄行號
  • 識別數字常量:連續讀取數字字符,遇到非數字時回退文件指針,輸出類別 2
  • 識別標識符與關鍵字:以字母開頭,後接字母或數字,查找關鍵字表,若匹配則輸出對應類別,否則輸出類別 1
  • 處理雙字符運算符::= 、>= 、<= 、<> 需向前看一字符
  • 處理單字符運算符與界符:+ - * / ( ) ; . , 等直接輸出對應類別
  • 遇到 "#" 結束分析

關鍵代碼片段

void parse(FILE* fpin, FILE* fpout) {
    char arr[MAXBUF];
    int i = 0, j = 0;
    while (1) {
        fscanf(fpin, "%c", &ch);
        if (ch == ' ' || ch == '\t') continue;
        else if (ch == '\n') lineno++;
        else if (IsDigit(ch)) {
            while (IsDigit(ch)) { arr[j++] = ch; fscanf(fpin, "%c", &ch); }
            fseek(fpin, -1L, SEEK_CUR);
            char* temp = (char*)malloc(j + 1);
            memcpy(temp, arr, j); temp[j] = '\0'; j = 0;
            fprintf(fpout, "%s\t\t%d\n", temp, 2); free(temp);
        }
        else if (IsAlpha(ch)) {
            while (IsAlpha(ch) || IsDigit(ch)) { arr[i++] = ch; fscanf(fpin, "%c", &ch); }
            fseek(fpin, -1L, SEEK_CUR);
            char* temp = (char*)malloc(i + 1);
            memcpy(temp, arr, i); temp[i] = '\0'; i = 0;
            if (FindOK(temp)) fprintf(fpout, "%s\t\t%d\n", temp, FindOK(temp));
            else fprintf(fpout, "%s\t\t%d\n", temp, 1);
            free(temp);
        }
        else if (ch == ':') { /* 處理 := */ }
        else if (ch == '>') { /* 處理 > 和 >= */ }
        else if (ch == '<') { /* 處理 <、<=、<> */ }
        else {
            switch (ch) {
                case '-': fprintf(fpout, "-\t\t10\n"); break;
                case ';': fprintf(fpout, ";\t\t21\n"); break;
                case '+': fprintf(fpout, "+\t\t9\n"); break;
                case '*': fprintf(fpout, "*\t\t11\n"); break;
                case '/': fprintf(fpout, "/\t\t12\n"); break;
                case '(': fprintf(fpout, "(\t\t13\n"); break;
                case ')': fprintf(fpout, ")\t\t14\n"); break;
                case '.': fprintf(fpout, ".\t\t22\n"); break;
                case ',': fprintf(fpout, ",\t\t23\n"); break;
                case '#': return;
                default: fprintf(fpout, "error in compileing %d lines unknown character %c\n", lineno, ch);
            }
        }
    }
}

總結與展望

本程式展示了詞法分析器的基本框架,代碼結構清晰,註釋詳細。雖然針對的是極簡語言,但擴展性良好:只需在關鍵字表中添加新保留字,在符號處理邏輯中補充新運算符即可支援更豐富的語言特性。歡迎各位指正交流。作者郵箱:brilliant_zhang@21cn.com,QQ:110902663。

本文配套完整源碼可供下載參考。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved