基於《編譯原理及實踐》中的整數計算器,擴展支持:
double 類型)/)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
char token; // 當前 token
double exp(void); // 處理加減
double term(void); // 處理乘除
double factor(void); // 處理括號和數字
// 獲取下一個可打印字符(跳過空白)
char getPrintableChar(void) {
char temp;
do temp = getchar(); while (isblank(temp));
return temp;
}
// 匹配期望字符,不匹配報錯
void match(char expectedToken) {
if (expectedToken == token) token = getPrintableChar();
else { fprintf(stderr, "Error!\n"); exit(EXIT_FAILURE); }
}
// 表達式:exp → term { (+|-) term }
double exp(void) {
double temp = term();
while (token == '+' || token == '-') {
switch (token) {
case '+': match('+'); temp += term(); break;
case '-': match('-'); temp -= term(); break;
}
}
return temp;
}
// 項:term → factor { (*|/) factor }
double term(void) {
double temp = factor();
while (token == '*' || token == '/') {
switch (token) {
case '*': match('*'); temp *= factor(); break;
case '/': match('/'); temp /= factor(); break;
}
}
return temp;
}
// 因子:factor → ( exp ) | Number
double factor(void) {
double temp;
if (token == '(') {
match('('); temp = exp(); match(')');
} else if (isdigit(token)) {
ungetc(token, stdin); // 退回去讓 scanf 讀完整浮點數
scanf("%lf", &temp);
token = getPrintableChar();
} else {
fprintf(stderr, "Error!\n"); exit(EXIT_FAILURE);
}
return temp;
}
int main(void) {
double result;
for (;;) {
token = getPrintableChar();
if (token == 'q') break; // 輸入 q 退出
result = exp();
if (token == '\n') printf("Result is: %g\n", result);
else { fprintf(stderr, "Error!\n"); exit(EXIT_FAILURE); }
}
return 0;
}
<exp> → <term> { (+|-) <term> }
<term> → <factor> { (*|/) <factor> }
<factor> → ( <exp> ) | Number
exp() 對應 <exp>,處理最低優先級(加減)term() 對應 <term>,處理中等優先級(乘除)factor() 對應 <factor>,處理最高優先級(括號、數字)每個非終結符對應一個函數,函數調用鏈直接映射文法推導過程。代碼結構清晰,易於擴展(如添加乘方、函數調用)。
getPrintableChar():詞法分析器,跳過空白字符match():語法分析器核心,驗證並消費 tokentoken 充當「向前看一個符號」的緩衝ungetc(token, stdin); // 把已讀的首位數字退回流
scanf("%lf", &temp); // 讓 scanf 完整讀取浮點數
避免手寫浮點數解析邏輯,利用標準庫優勢。
統一使用 error() 輸出錯誤並退出,簡單直接。生產環境可改為拋出異常或返回錯誤碼。
factor() 中處理一元 -factor() 識別標識符查表