LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1479|回复: 7

看过《C++程序设计语言》第6章写的那个“桌面计算器“的朋友请进。

[复制链接]
发表于 2006-4-24 21:41:09 | 显示全部楼层 |阅读模式
在《C++程序设计语言》的第6章有一个“桌面计算器“的程序,输入一个四则运算的表达式可以求出值来。我知道这可算是一个最简单的编译器(解释器)。我想以此为基础写一个MIX虚拟机的汇编语言的编译器。
可是三四天下来进展不大,请求大家给些思路启发一下。
发表于 2006-4-24 21:45:56 | 显示全部楼层
see code below, may be help for you.
  1. #include<iostream>
  2. #include<map>
  3. #include<string>
  4. #include<cctype>
  5. #include<math.h>
  6. using namespace std;
  7. class expr;
  8. enum Token_value{
  9. SQRT,SIN,COS,
  10. NAME,NUMBER,END,
  11. PLUS='+',MINUS='-',MUL='*',DIV='/',
  12. PRINT=';',ASSIGN='=',LP='(',RP=')',MIN='^'
  13. };
  14. typedef double (*dfdp)(double);
  15. dfdp math[]={sqrt,sin,cos};
  16. int i;
  17. typedef Token_value (expr::*get_token_p_p)();
  18. get_token_p_p get_token_p;
  19. class expr{
  20. public:
  21. expr(const char* str);
  22. expr(void);
  23. void jisuan(void);
  24. double eval();
  25. void print(){}

  26. private:
  27. int line_no;

  28. string string_expr;
  29. double (*dfddp)(double,double);
  30. double number_value;
  31. string string_value;
  32. map<string,double> table;
  33. Token_value curr_tok;
  34. double my_expr(bool get);
  35. double term(bool get);
  36. double prim(bool get);
  37. double min(bool get);
  38. Token_value get_token(void);
  39. Token_value get_token_ios(void);

  40. double error(char *str);
  41. };
  42. expr::expr(void)
  43. {
  44. line_no=0;
  45. i=-1;
  46. number_value=0;
  47. table["pi"]=3.1415;
  48. table["e"]=2.718281;

  49. }
  50. expr::expr(const char* str)
  51. {
  52. string_expr=str;
  53. expr();
  54. }
  55. double expr::error(char *str)
  56. {
  57. cerr<<"line:"<<line_no<<" "<<str<<endl;
  58. return 1;
  59. }
  60. double expr::my_expr(bool get)
  61. {
  62. double left=term(get);
  63. for(;;)
  64. switch(curr_tok){
  65. case PLUS:
  66. left+=term(true);
  67. break;
  68. case MINUS:
  69. left-=term(true);
  70. break;
  71. default:
  72. return left;
  73. }
  74. }
  75. double expr::term(bool get)
  76. {
  77. double left=min(get);
  78. for(;;)
  79. switch(curr_tok){
  80. case MUL:
  81. left*=min(true);
  82. break;
  83. case DIV:
  84. if(double d=min(true)){
  85. left/=d;
  86. break;
  87. }
  88. return error("divide by 0");
  89. default:
  90. return left;
  91. }
  92. }
  93. double expr::min(bool get)
  94. {
  95. double left=prim(get);
  96. dfddp=pow;
  97. for(;;)
  98. {
  99. switch(curr_tok){
  100. case MIN:
  101. left=dfddp(left,prim(true));
  102. break;
  103. case SQRT:
  104. left=math[SQRT](left);
  105. (this->*get_token_p)();
  106. return left;
  107. case SIN:
  108. left=math[SIN](left);
  109. (this->*get_token_p)();
  110. return left;
  111. case COS:
  112. left=math[COS](left);
  113. (this->*get_token_p)();
  114. return left;
  115. default:
  116. return left;
  117. }
  118. }
  119. }
  120. double expr::prim(bool get)
  121. {
  122. if(get) (this->*get_token_p)();
  123. switch(curr_tok){
  124. case NUMBER:
  125. {
  126. double v=number_value;
  127. (this->*get_token_p)();
  128. return v;
  129. }
  130. case NAME:
  131. {
  132. if(strcmp(string_value.c_str(),"sqrt")==0)
  133. {
  134. (this->*get_token_p)();
  135. if(curr_tok!=LP) return error("(expected");
  136. double e=my_expr(true);
  137. if(curr_tok!=RP) return error(")expected");
  138. //get_token(); // delete RP;
  139. curr_tok=SQRT;
  140. return e;
  141. }
  142. else if(strcmp(string_value.c_str(),"sin")==0)
  143. {
  144. (this->*get_token_p)();
  145. if(curr_tok!=LP) return error("( expected");
  146. double e=my_expr(true);
  147. if(curr_tok!=RP) return error(")expected");
  148. curr_tok=SIN;
  149. return e;
  150. }
  151. else if(strcmp(string_value.c_str(),"cos")==0)
  152. {
  153. (this->*get_token_p)();
  154. if(curr_tok!=LP) return error("( expected");
  155. double e=my_expr(true);
  156. if(curr_tok!=RP) return error(")expected");
  157. curr_tok=COS;
  158. return e;
  159. }
  160. else {

  161. double& v=table[string_value];
  162. if((this->*get_token_p)()==ASSIGN)
  163. { line_no++; v=my_expr(true);}
  164. return v;
  165. }
  166. }
  167. case MINUS:
  168. return -prim(true);
  169. case LP:
  170. {
  171. double e=my_expr(true);
  172. if(curr_tok!=RP) return error(")expected");
  173. (this->*get_token_p)(); // delete RP;
  174. return e;
  175. }
  176. return error("primay expected");
  177. }
  178. }
  179. Token_value expr::get_token(void)
  180. {
  181. char ch=0;
  182. double power=1;
  183. number_value=0.0;
  184. do{
  185. if(!(ch=string_expr[++i])) return curr_tok=END;
  186. }while(ch!='\n' && isspace(ch));
  187. switch(ch){
  188. case 0:
  189. return curr_tok=END;
  190. case ';':
  191. case '*':
  192. case '/':
  193. case '+':
  194. case '-':
  195. case '(':
  196. case ')':
  197. case '=':
  198. case '^':
  199. return curr_tok=Token_value(ch);
  200. case '0':
  201. case '1':
  202. case '2':
  203. case '3':
  204. case '4':
  205. case '5':
  206. case '6':
  207. case '7':
  208. case '8':
  209. case '9':
  210. case '.':
  211. //string_expr.putback(ch);
  212. while((isdigit((int)ch)))
  213. {
  214. number_value=number_value*10+(ch-'0');
  215. ch=string_expr[++i];
  216. }
  217. if(ch=='.')
  218. ch=string_expr[++i];
  219. while((isdigit((int)ch)))
  220. {
  221. number_value=number_value*10+(ch-'0');
  222. ch=string_expr[++i];
  223. power*=10;
  224. }
  225. i--;
  226. number_value=number_value/power;
  227. // cout<<number_value<<endl;
  228. return curr_tok=NUMBER;
  229. default:
  230. if(isalpha(ch)){
  231. string_value=ch;
  232. while((ch=string_expr[++i]) && isalnum(ch)) string_value.push_back(ch);
  233. i--;
  234. return curr_tok=NAME;
  235. }
  236. //error("bad token");
  237. return curr_tok=PRINT;
  238. }
  239. }
  240. Token_value expr::get_token_ios(void)
  241. {
  242. char ch=0;
  243. do{
  244. if(!cin.get(ch)) return curr_tok=END;
  245. }while(ch!='\n' && isspace(ch));
  246. switch(ch){
  247. case 0:
  248. return curr_tok=END;
  249. case ';':
  250. case '*':
  251. case '/':
  252. case '+':
  253. case '-':
  254. case '(':
  255. case ')':
  256. case '=':
  257. case '^':
  258. return curr_tok=Token_value(ch);
  259. case '0':
  260. case '1':
  261. case '2':
  262. case '3':
  263. case '4':
  264. case '5':
  265. case '6':
  266. case '7':
  267. case '8':
  268. case '9':
  269. case '.':
  270. cin.putback(ch);
  271. cin>>number_value;
  272. return curr_tok=NUMBER;
  273. default:
  274. if(isalpha(ch)){
  275. string_value=ch;
  276. while(cin.get(ch) && isalnum(ch)) string_value.push_back(ch);
  277. cin.putback(ch);
  278. return curr_tok=NAME;
  279. }
  280. //error("bad token");
  281. return curr_tok=PRINT;
  282. }
  283. }
  284. void expr::jisuan(void)
  285. {
  286. get_token_p=&expr::get_token_ios;
  287. while(cin){
  288. (this->*get_token_p)();
  289. if(curr_tok==END)break;
  290. if(curr_tok==PRINT)continue;
  291. cout<<my_expr(false)<<'\n';
  292. // cout<<number_value<<endl;
  293. }
  294. //return 0;
  295. }
  296. double expr::eval(void)
  297. {
  298. get_token_p=&expr::get_token;
  299. //while(string_expr){
  300. (this->*get_token_p)();
  301. //if(curr_tok==END)break;
  302. //if(curr_tok==PRINT)continue;
  303. return my_expr(false);
  304. // cout<<number_value<<endl;
  305. // }
  306. //return 0;
  307. }
  308. int main(void)
  309. { /*
  310. expr x("123/3+123*4-3");
  311. cout<<"x="<<x.eval()<<"\n";
  312. x.print();
  313. */
  314. expr a("1+3+9/3");
  315. cout<<a.eval()<<endl;
  316. a.jisuan();
  317. }
复制代码
回复 支持 反对

使用道具 举报

发表于 2006-4-24 21:49:41 | 显示全部楼层
上面的代码,是我做的练习,写这种程序需要时间的.可以看看<<虚拟机的设计和实现--c/c++>>
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-4-24 22:47:03 | 显示全部楼层
谢谢ywchen2000兄了,不过你的程序和书上的程序差不多。我现在遇到的困难是汇编的语法和这个计算器的语法差别很大,不知道该怎么该这个程序适应汇编的语法。另一方面这个程序是直接求值,汇编编译器却要生成代码。

我以前光是靠想,以为汇编其实就是一一对应的翻译,应该不难,没想到自己想做一个,却发现比原来想的要难得多。我不是科班出身,没有学过编译原理,只是大略的看了下《编译原理》的书,知道翻译有几个阶段。还有一个难点是我想只用一次扫描来实现,有的符号是在“未来”定义的,这个还不知道该如何处理。
回复 支持 反对

使用道具 举报

发表于 2006-4-25 08:52:44 | 显示全部楼层
上面的代码就是展示了递归分析的技术.可以看看nasm的代码,也可以看看<<虚拟机的设计和实现--c/c++>>这本书,这本书上说的就是自己写个虚拟机,然后在上面实现个汇编编译器.多数的编译器是用二次扫描来实现,
回复 支持 反对

使用道具 举报

发表于 2006-4-25 08:53:01 | 显示全部楼层
上面的代码和书上的有很大的不同.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-4-25 22:48:18 | 显示全部楼层
谢谢ywchen2000
你的代码虽说和书上的有很大的不同,但是所解析的语法还是基本一致的。

我想解析的语法是这样的:

LABEL OPCODE ADDRESS,I(F)
其中LABEL可以没有,OPCODE有两种:机器代码和伪指令;ADDRESS是一个表达式或者没有,I是一个表达式或者没有,F也使一个表达式或者没有;
如果OPCODE是一个伪指令那么,应该是这样
LABEL OPCODE E(F),E(F)

我觉得困难在于如何一次扫描就可以把Address里的还没有定义的符号(但会在下文中定义)解析出值来。

不过慢慢的好像有一点门路了。
回复 支持 反对

使用道具 举报

发表于 2006-4-25 23:08:12 | 显示全部楼层
呵呵,直接下个nasm的代码去看看,那是个好东东
http://blog.csdn.net/wenfengmtd/可能有帮助.
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表