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

手把手寫 C 語言編譯器(五):用 Bison 實現四則運算解析器

     編輯:關於C語言

前言

系列第五篇,使用 **Bison (GNU yacc)** 定義文法,生成 LALR(1) 解析器,實現一個支持浮點數、四則運算、一元負號的迷你計算器。完整 `.y` 文件與編譯運行步驟均已包含。

Bison 文法文件 calc.y

%{
#define YYSTYPE double
#include 
#include 
int yylex(void);
void yyerror(const char *);
%}

%token NUM

%%
input:  /* empty */
      | input line
      ;

line: '\n'
    | exp '\n'   { printf("\t%.10g\n", $1); }
    ;

exp: NUM                 { $$ = $1; }
   | exp exp '+'         { $$ = $1 + $2; }
   | exp exp '-'         { $$ = $1 - $2; }
   | exp exp '*'         { $$ = $1 * $2; }
   | exp exp '/'         { $$ = $1 / $2; }
   | exp 'n'             { $$ = -$1; }   /* 一元負號,lexer 返回 'n' */
   ;
%%

/* 詞法分析器 */
int yylex(void) {
    int c;
    while ((c = getchar()) == ' ' || c == '\t'); // 跳過空白
    if (c == '.' || isdigit(c)) {
        ungetc(c, stdin);
        scanf("%lf", &yylval);
        return NUM;
    }
    if (c == EOF) return 0;
    if (c == '-') { /* 區分減號與負號:簡單策略,上一 token 為數字或 ')' 視為減號,否則負號 */
        int next = getchar();
        if (next == ' ' || next == '\t' || next == '\n' || next == EOF) return 'n'; // 簡化處理
        ungetc(next, stdin);
    }
    return c; // 返回單字符 token (+ - * / \n)
}

void yyerror(const char *msg) {
    fprintf(stderr, "Error: %s\n", msg);
}

int main() {
    return yyparse();
}

編譯與運行步驟 (Ubuntu/Linux)

# 1. 安裝工具
sudo apt-get install bison flex gcc

# 2. 生成解析器 C 代碼
bison -d calc.y        # 生成 calc.tab.c 和 calc.tab.h

# 3. 編譯鏈接
gcc -o calc calc.tab.c -lfl  # -lfl 鏈接 flex 庫 (若 yylex 用 flex 生成則需),純手寫 yylex 可省略

# 4. 運行測試
./calc
# 輸入 (後綴/逆波蘭表達式,因文法為 exp exp op):
1 4 +     → 輸出 5
3 10 *    → 輸出 30
5 2 -     → 輸出 3
6 2 /     → 輸出 3
4 n       → 輸出 -4   (一元負號)
Ctrl+D 退出

關鍵點解析

項目說明
%define YYSTYPE double語義值類型為 double,支持浮點運算
%token NUM聲明終結符 NUM,對應浮點數
文法 exp exp '+'**後綴表達式** (逆波蘭),天然無歧義、無需優先級聲明
exp 'n'一元負號,lexer 中將負號映射為字符 'n'
yyerror語法錯誤回調,這裡僅打印
$$ = $1 + $2語義動作:$1/$2 為左右子節點值,$$ 為本節點值

為什麼選後綴表達式文法?

  • 無需 %left/%right 聲明運算符優先級與結合性。
  • 文法極簡,生成的解析器無移進/歸約衝突。
  • 適合教學演示「語法分析 → 語義計算」流程。

擴展方向

  1. 中綴表達式:引入 %left '+' '-'、%left '*' '/'、%right 'n',文法改為 exp '+' exp 等。
  2. 變量賦值:加入符號表 (HashMap),文法增加 ID '=' exp。
  3. 函數調用:ID '(' arglist ')',語義動作查表調用 sin/cos/pow 等。
  4. 代碼生成:語義動作發射 AST 或三地址碼,而非直接計算。
  5. Flex 配合:用 Flex 寫 lexer.l 替換手寫 yylex,更規範。

常見坑點

  • bison -d 必須加 -d 生成 .h,否則外部調用 yylex 找不到 YYSTYPE 定義。
  • 手寫 yylex 時,**一定要處理換行符** 作為 line 的終結符,否則解析器永遠不歸約。
  • 一元負號與減號衝突:正式項目請在 lexer 中通過**上下文狀態**區分,勿用簡化邏輯。

總結

本篇演示了從 Bison 文法定義到可執行計算器的完整鏈路。掌握 .y 文件結構、YYSTYPE、語義動作、詞法分析器接口,即具備了構建領域特定語言 (DSL) 或完整編譯器前端的基礎能力。下一篇將介紹 AST 構建與中間代碼生成。

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