掃描輸入字符串,識別 Token:
// 運算符優先級
int precedence(char op) {
switch(op) {
case '+': case '-': case '=': case ')': return 1;
case '*': case '/': return 2;
case '^': return 3;
default: return 0; // 函數、'('
}
}
// 核心計算引擎:雙棧法(操作數棧 OPRD、運算符棧 OPRT)
double calculate(Node *tokens) {
double OPRD[200]; int top_oprd = 0;
char OPRT[200]; int top_oprt = 0;
// 處理一元負號:表達式首或 ( 後的 -,壓入 0 作為左操作數
// 主循環:讀 Token
// 操作數 → 壓 OPRD
// 運算符 →
// 若 OPRT 為空或棧頂優先級 < 當前 → 壓 OPRT
// 若棧頂優先級 ≥ 當前 → 彈出計算(雙目/單目),結果壓回 OPRD
// 左括號 → 壓 OPRT,遞歸計算括號內表達式
// 右括號/= → 彈出計算至匹配左括號/棧底
// 最終 OPRD[0] 為結果
}
// 單目:sin/cos/tan/asin/acos/atan/ln/lg
int single_op(char op, double *x) {
switch(op) {
case 's': *x = sin(*x); break;
case 'c': *x = cos(*x); break;
case 't': *x = tan(*x); break;
case 'a'+'s': if(*x<-1||*x>1) return 0; *x=asin(*x); break;
case 'a'+'c': if(*x<-1||*x>1) return 0; *x=acos(*x); break;
case 'a'+'t': if(*x<-M_PI_2||*x>M_PI_2) return 0; *x=atan(*x); break;
case 'n': if(*x<=0) return 0; *x=log(*x); break;
case 'g': if(*x<=0) return 0; *x=log10(*x); break;
}
return 1;
}
// 雙目:+ - * / ^
int double_op(char op, double *a, double b) {
switch(op) {
case '+': *a += b; break;
case '-': *a -= b; break;
case '*': *a *= b; break;
case '/': if(fabs(b)<1e-8) return 0; *a /= b; break;
case '^': *a = pow(*a, b); break;
}
return 1;
}
typedef enum { Opd=0, Opt=1 } Type;
typedef struct {
int opt; // 運算符編碼
double opd; // 操作數值
Type tp; // 類型標記
} Node; // 表達式 Token 鏈節點
// 輸入緩衝區
char input_buf[1000];
Node tokens[500];
int main() {
while (1) {
// 1. 清空狀態
memset(tokens, 0, sizeof(tokens));
bracket_stack_clear();
// 2. 輸入
printf("Enter: ");
if (!read_and_tokenize(tokens)) { puts("Input Error!"); continue; }
// 3. 計算
double result;
if (calculate(tokens, &result)) {
if (bracket_mismatch) { puts("Bracket mismatch!"); continue; }
// 4. 輸出:整數顯示無小數,浮點保留 4 位
if (floor(result) == result) printf("%d\n", (int)result);
else printf("%.4f\n", result);
}
// 5. 命令處理
int c = getchar();
if (c == 'q') break;
else if (c == 'c') system("clear"); // Windows 用 "cls"
if (c != '\n') getchar(); // 吞換行
}
puts("Quit...");
}
gcc calc.c -o calc -lm -std=c99
# Windows MSVC
cl calc.c /link /subsystem:console
# 運行
./calc
Enter: 2+3*4
14
Enter: sin(pi/2)
1
Enter: 2^10
1024
Enter: q
Quit...
| 模塊 | 亮點 | 擴展方向 |
|---|---|---|
| 詞法分析 | 統一 `translate()` 分發,`translateopd()` 處理小數、`e`、`pi` | 支持十六進制、變量賦值(`x=2`) |
| 語法分析 | 算符優先 + 遞歸處理括號,單/雙目統一框架 | 引入 Shunting Yard 算法生成顯式後綴,支持更多運算符 |
| 錯誤處理 | 定義域檢查、除零、括號匹配、非法字符 | 錯誤位置報告、恢復策略 |
| 交互 | 非阻塞 `getch()` 實現 ESC 切換(客戶端代碼),主循環命令 | 歷史記錄(上下鍵)、變量存儲、腳本模式 |
這是一個教學級工業強度的 C 語言表達式計算器:無外部依賴(僅標準庫),手寫詞法/語法/語義全流程,展示了編譯器前端核心技術在微型項目中的完整應用。代碼結構清晰,擴展性強,極適合作為編譯原理實踐、數據結構棧應用、C 語言綜合訓練的參考項目。