Demllie>編譯語言實現的核心問題,就是這裡不明白讓我白白寫6000行垃圾
前言
核心問題就是分析時的迭代,因為不懂怎麼迭代,我白白寫了好多垃圾。下面做一個計算器,弄明白這個東西。
Token定義
typedef enum {
NIL,
ADD,
SUB,
MUL,
DIV,
MOD,
NUM,
LB,
RB
}TokenType;
typedef union {
char c;
double v;
}Var;
class Token {
private:
static int counter;//類變數,不能再此處初始化
int num;
TokenType type = TokenType::NIL;
Var value;
public:
Token(char _value) {
num = counter++;
switch (_value) {
case '+':
type = TokenType::ADD;
break;
case '-':
type = TokenType::SUB;
break;
case '*':
type = TokenType::MUL;
break;
case '/':
type = TokenType::DIV;
break;
case '%':
type = TokenType::MOD;
break;
case '(':
type = TokenType::LB;
break;
case ')':
type = TokenType::RB;
break;
default:
cout << "違法字元>" << _value << endl;
exit(1);
break;
}
value.c = _value;
}
Token(double _value) {
num = counter++;
type = TokenType::NUM;
value.v = _value;
}
TokenType GetTokenType()const {
return type;
}
double GetTokenValue()const {
if (type == TokenType::NUM)return value.v;
return 0;
}
};
int Token::counter = 0;//類變數初始化
主要是兩種,一種是單個char的操作符,另一種是多個字元的整數!!!
next找到下一個Token
定義幾個全域性變數,line陣列是讀取的一行字串,index是其中的索引,currentToken是當前的Token
static Token* currentToken=NULL;
static int index = 0;
static char line[256] = { 0 };
就是輪詢一邊,遇到空字元就跳過,遇到數字就結合起來,然後遇到非數字就把結合的整個數字變成一個Token。
遇到操作符就直接變成Token。
//獲取下一個token
void next() {
char name[256] = { 0 };
char c;
double v=0;
while ((c=line[index++]) != '\0') {
if (c == ' ')continue;
else if (c<='9'&&c>='0' ) {
v = v * 10 + c - '0';
//
char nextChar = line[index];
if (!(nextChar <= '9' && nextChar >='0')) {
if (currentToken != NULL) {
delete currentToken;
currentToken = NULL;
}
currentToken = new Token((double)v);
break;
}
}
else if (c == '(' || c==')' ) {
if (currentToken != NULL)
delete currentToken;
currentToken = new Token((char)c);
break;
}
else if (c == '+' || c == '-' || c == '*' || c == '\\' || c == '%'||c=='/') {
if (currentToken != NULL) {
delete currentToken;
currentToken = NULL;
}
currentToken = new Token((char)c);
break;
}
}
}
factor
factor、term、expr是核心問題。
expr是加減表示式或者一個數,形式是
expr ::= term [ ('+'|'-') term ]
term是乘除取餘表示式或者一個數,形式是
term ::= factor [ ('*'|'/'|'%') factor]
factor是元素或者括號表示式,形式是
factor::= NUM | '(' expr ')'
#define ENUM_TYPE(type) (type==0?"NIL": \
(type==1?"ADD": \
(type==2?"SUB": \
(type==3?"MUL": \
(type==4?"DIV": \
(type==5?"MOD": \
(type==6?"NUM": \
(type==7?"LB":"RB"))))))))
//終結符,是最底層的元素
double factor() {
double value = 0;
if (currentToken->GetTokenType() == TokenType::NUM) {
value = currentToken->GetTokenValue();
}
else if (currentToken->GetTokenType() == TokenType::LB) {
next();
double ans = expr();//獲取括號內的answer
//錯誤處理
if (currentToken->GetTokenType() != TokenType::RB) {
cout << "語法分析出錯,右括號不匹配!";
if (currentToken->GetTokenType() == TokenType::NUM){
cout << "value > "<<currentToken->GetTokenValue() << endl;
}
else {
cout << "value > " << ENUM_TYPE(currentToken->GetTokenType()) << endl;
}
}
value = ans;//終結符為表示式asnwer
}
next();//離開的時候別忘了移動一下token
cout << "factor>" << value << endl;
return value;
}
term
//乘除取餘表示式
double term() {
double value = 0;
double left = factor();//乘除的上一級是單個元素或者表達
double right = 0;
value = left;
//暫時儲存current token前兩個token的型別和值,即前一個OP
Token tempToken = *currentToken;
while (tempToken.GetTokenType() == TokenType::MOD || tempToken.GetTokenType() == TokenType::MUL || tempToken.GetTokenType() == TokenType::DIV) {
next();
right = factor();//乘除的上一級是單個元素或者表達
switch (tempToken.GetTokenType() ) {
case TokenType::MOD:
value = (int)value % (int)right;
break;
case TokenType::MUL:
value = value * right;
break;
case TokenType::DIV:
if (right == 0) {
cout << "除數不能為零"<< endl;
exit(1);
return -1;
}
value = value / right;
break;
}
tempToken = *currentToken;//移動token
}
cout << "term>" << value << endl;
return value;
}
expr
和term是類似的。
//加減表示式
double expr() {
double value = 0;
double left = term();//加減的上一級是乘除取餘
double right = 0;
value = left;
//暫時儲存current token前兩個token的型別和值,即前一個OP
Token tempToken = *currentToken;
while (tempToken.GetTokenType() == TokenType::ADD || tempToken.GetTokenType() == TokenType::SUB ) {
next();
right = term(); //加減的上一級是乘除取餘
switch (tempToken.GetTokenType()) {
case TokenType::ADD:
value = value + right;
break;
case TokenType::SUB:
value = value - right;
break;
}
tempToken = *currentToken;//移動token
}
cout << "expr>" << value << endl;
return value;
}
從檔案中讀取字串
int main(void) {
FILE* file = NULL;
int a = fopen_s(&file, "test.txt", "r");
if (a != 0) {
cout << "檔案打不開!" << endl;
return 1;
}
fgets(line, 256, file);
cout << line << endl;
next();//取得第一個token
cout << "ans="<<expr()<< endl;//迭代下去,返回answer
return 0;
}
建一個文字檔案test.txt,輸入公式執行結果如下:
1+2*3/4 + (1+3*4)*4*5/2*12 - 0 - 100 + 120*2
factor>1
term>1
factor>2
factor>3
factor>4
term>1.5
factor>1
term>1
factor>3
factor>4
term>12
expr>13
factor>13
factor>4
factor>5
factor>2
factor>12
term>1560
factor>0
term>0
factor>100
term>100
factor>120
factor>2
term>240
expr>1702.5
ans=1702.5
相關文章
- Linux下C語言編譯的問題LinuxC語言編譯
- 求助(請教C語言編譯問題)(轉)C語言編譯
- 使用 Rust 語言編寫 Java JNI 實現RustJava
- 用 C 語言編寫一個簡單的垃圾回收器
- [譯] Go 語言實戰: 編寫可維護 Go 語言程式碼建議Go
- 對於現代開發來說,JavaScript就是一種垃圾語言JavaScript
- c語言實現貓吃老鼠的問題C語言
- C語言新手最常見的問題!你在這裡跌倒過嗎?C語言
- nginx 編譯出現的問題Nginx編譯
- 求助(菜鳥請教一C語言編譯問題)(轉)C語言編譯
- 第一個C語言編譯器是怎樣編寫的?C語言編譯
- 第一個 C 語言編譯器是怎樣編寫的?編譯
- 57段讓編譯器崩潰的C語言程式碼編譯C語言
- 這就是選擇排序的問題排序
- 手機寫作業系統之 使用C語言編寫核心作業系統C語言
- 現代編譯原理C語言描述pdf編譯原理C語言
- 這就是OpenAI神秘的Q*?史丹佛:語言模型就是Q函式OpenAI模型函式
- 哦?原來這就是 JVM 垃圾!JVM
- 用C語言實現八數碼問題C語言
- C語言編譯工具C語言編譯
- Go 語言編譯期斷言Go編譯
- 請各位幫我看看javamail的問題(本不應在這裡提這類問題,但在csdn實在沒人幫我解答)JavaAI
- 我就是要用CSS實現CSS
- 在我眼裡的幾種語言
- 巨頭們的程式語言之爭:我的語言就是比你好
- HTML語言編寫指南HTML
- Clojure:讓我興奮的程式語言
- 源語言、目標語言、翻譯器、編譯器、直譯器編譯
- 使用Go語言從零編寫PoS區塊鏈(譯)Go區塊鏈
- 編譯型語言與解釋型語言編譯
- 利用LLVM實現JS的編譯器,創造屬於自己的語言LVMJS編譯
- 實現JavaScript語言直譯器(三)JavaScript
- java編譯、編碼、語言設定Java編譯
- [譯]用javascript實現一門程式語言-語言構想JavaScript
- C語言 - 條件編譯C語言編譯
- 核心編譯後載入音效卡問題(轉)編譯
- 曾經我認為C語言就是個弟弟C語言
- 解釋型語言與編譯型語言的區別?編譯