C++程序設計語言(特別版) -- 一個桌面計算器


前言

  • 這里要介紹各種語句和表達式,將通過一個桌面計算器的程序做些事情,該計算器提供四種座位浮點數的中綴運算符的標准算術運算。
  • 這個計算器由四個部分組成:一個分析器,一個輸入函數,一個符號表和一個驅動程序。

分析器

program:
	END
	expr_list END //END表示輸入結束
expr_list:
	expression PRINT // PRINT表示分號
	expression PRINT expr_list
expression:
	expression + term
	expression - term
	term
term:
	term / primary
	term * primary
	primary
primary:
	NUMBER
	NAME
	NAME = expression
	- primary
	(expression)
  • 語法分析器采用通常的遞歸下降的風格。終結符由詞法分析程序get_token()識別,而非終結符由語法分析函數expr(),term()和prim()識別。一旦一個表達式的兩個運算對象都知道,就立即對這個表達式求值。
  • 個人覺得分析器就像是定義程序能接受什么輸入,而這里采用遞歸的形式,例如program代表程序,program可以接受END符號或者expr_list END,而expr_list又繼續遞歸下去可以由其他的組成,直到可以得到一個結束情況。

總代碼

#include<iostream>
#include<string>
#include<map>
#include<cctype>
using namespace std;

// 將token用他們的字符所對應的整數表示,這樣做既方便有效,
// 又能幫助使用排錯系統的人。
enum Token_value {
	NAME, NUMBER, END, PLUS='+',
	MINUS='-', MUL='*', DIV='/',
	PRINT=';', ASSIGN='=', LP='(', RP=')',
};
Token_value curr_tok = PRINT;

double expr(bool);
double term(bool);
double prim(bool);
Token_value get_token();
double error(const string&);
map<string, double> table;

int no_of_errors;

int main()
{
	table["pi"] = 3.1415924535;
	table["e"] = 2.178;

	while(cin) {
		get_token();
		if(curr_tok == END) break;
		if(curr_tok == PRINT) continue;
		cout<<expr(false)<<'\n';
	}
	return no_of_errors;
}

// 每個分析器都有一個bool參數,
// 指明該函數是否需要調用get_token()去取得下一個參數
// expr處理加減,一直到不是加減返回left
double expr(bool get) {
	double left = term(get);
	for(;;) {
		switch (curr_tok){
		case PLUS:
			left += term(true);
			break;
		case MINUS:
			left += term(true);
			break;
		default:
			return left;
		}
	}
}

// 函數term處理乘除,采用的方式與expr()處理方法一樣
double term(bool get) {
	double left = prim(get);
	for(;;) {
		switch (curr_tok){
		case MUL:
			left *= prim(true);
			break;
		case DIV:
			if (double d = prim(true)) {
				left /= d;
				break;
			}
			return error("divide by 0");
		default:
			return left;
		}
	}
}

double number_value;
string string_value;

// prim處理初等項的方式很像expr和term
double prim(bool get) {
	if (get) get_token();

	switch(curr_tok) {
	case NUMBER: {
		double v = number_value;
		get_token();
		return v;
	}
	case NAME: {
		double& v = table[string_value];
		if (get_token() == ASSIGN) v = expr(true);
		return v;
	}
	case MINUS:
		return -prim(true);
	case LP: {
		double e = expr(true);
		if (curr_tok != RP) return error(" ) expected");
		get_token();
		return e;
	}
	default:
		return error("primary expected");
	}
}

Token_value get_token() {
	char ch;
	
	do { // 低級輸入,改進輸入
		if(!cin.get(ch)) return curr_tok = END;
	} while(ch != '\n' && isspace(ch));

	switch(ch) {
	case 0:
		return curr_tok=END;
	case '\n':
		return curr_tok=PRINT;
	case '+':
	case '-':
	case '*':
	case '/':
	case ';':
	case '(':
	case ')':
	case '=':
		return curr_tok = Token_value(ch);
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	case '.':
		cin.putback(ch);
		cin>>number_value;
		return curr_tok = NUMBER;
	default:
		if (isalpha(ch)) {
			string_value = ch;
			while(cin.get(ch) && isalnum(ch)) string_value.push_back(ch);
			cin.putback(ch);
			return curr_tok = NAME;
		}
		error("bad token");
		return curr_tok = PRINT;
	}
}

double error(const string& s) {
	no_of_errors ++;
	cerr<<"error: "<<s<<'\n';
	return 1;
}

擴展

  • 自己的代碼只是簡單的拼接,這里發現一個更好的博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM