zoukankan      html  css  js  c++  java
  • 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;
    }
    

    扩展

    • 自己的代码只是简单的拼接,这里发现一个更好的博客
  • 相关阅读:
    HashMap
    Java内部类应用——静态内部类
    transient关键字和@Transient 注解
    java基本数据类型传递与引用传递区别
    抽象类
    java collection-list详解
    Arrays,ArrayList,以及ArrayList源码分析
    【转载】【剑指offer】面试题40:最小的 k 个数中的优先级队列
    java stack总结
    java Queue
  • 原文地址:https://www.cnblogs.com/mangoyuan/p/6292434.html
Copyright © 2011-2022 走看看