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

C 語言遞歸下降解析器實現四則運算計算器:EBNF 文法、浮點數支援、錯誤處理

     編輯:關於C語言

C 語言遞歸下降解析器實現四則運算計算器

基於 EBNF 文法 編寫遞歸下降解析器,是編譯原理教學的經典實踐。本實現支援浮點數、括號、四則運算、空白字符跳過,代碼簡潔且可直接擴展為腳本引擎前端。

EBNF 文法定義

    ::=  { ('+' | '-')  }
   ::=  { ('*' | '/')  }
 ::= '('  ')' | 
 ::= 浮點數字面值 (含可選符號、小數點、科學記數法)

完整代碼 (C99)

#include 
#include 
#include 

char token;  // 當前看頭符號

double exp(void);   // 前向聲明
double term(void);
double factor(void);

char get_token(void) {
    char c;
    do c = getchar(); while (isspace(c));  // 跳過空白
    return c;
}

void match(char expected) {
    if (token == expected) token = get_token();
    else { fprintf(stderr, "Syntax error: expected '%c'\n", expected); exit(1); }
}

double exp(void) {
    double result = term();
    while (token == '+' || token == '-') {
        if (token == '+') { match('+'); result += term(); }
        else              { match('-'); result -= term(); }
    }
    return result;
}

double term(void) {
    double result = factor();
    while (token == '*' || token == '/') {
        if (token == '*') { match('*'); result *= factor(); }
        else              { match('/'); result /= factor(); }
    }
    return result;
}

double factor(void) {
    double result;
    if (token == '(') {
        match('(');
        result = exp();
        match(')');
    } else if (isdigit(token) || token == '.' || token == '-') {
        ungetc(token, stdin);           // 退回首字符
        scanf("%lf", &result);          // 交給 scanf 解析浮點數
        token = get_token();            // 讀取下一符號
    } else {
        fprintf(stderr, "Syntax error: unexpected '%c'\n", token);
        exit(1);
    }
    return result;
}

int main(void) {
    puts("Expression Calculator (enter 'q' to quit)");
    for (;;) {
        token = get_token();
        if (token == 'q') break;
        double result = exp();
        if (token == '\n') printf("Result: %g\n", result);
        else { fprintf(stderr, "Trailing garbage\n"); exit(1); }
    }
    return 0;
}

運行示例

$ ./calc
Expression Calculator (enter 'q' to quit)
3.14 * (2 + 1.5) / 0.5
Result: 21.98
(1 + 2) * 3 - 4 / 2
Result: 7
q
Bye

關鍵技術點

技術點實現細節
看頭符號全局 token 存儲下一未處理字符,get_token 跳過空白
匹配消耗match() 驗證並推進 token
浮點數解析ungetc 退回首字符 → scanf("%lf") 利用庫函數完整解析
錯誤處理語法錯誤即刻 exit(1),生產環境可改為 longjmp 或異常結構
左遞歸消除EBNF 用 `{...}` 迭代替代左遞歸,自然映射為 while 循環

擴展方向

  • 變量/賦值:引入符號表 map,factor 識別標識符。
  • 函數調用:factor 檢測 identifier '(' 進入參數列表解析。
  • AST 構建:每個解析函數返回 Node* 而非 double,構建語法樹供下一階段優化/代碼生成。
  • 錯誤恢復:match 失敗時同步至下一分隔符 (如 ;、\n) 繼續解析。

小結

遞歸下降解析器將 EBNF 文法一對一映射為函數,代碼結構清晰、易於擴展。本計算器以 80 行代碼實現浮點四則運算,展示了「文法驅動代碼」的工程美感。掌握此模式,即可為 DSL、配置語言、腳本引擎快速構建前端解析器。

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