实现一个计算器

摘要:
(2)我找到了很多编译原理的教程,但始终觉得内容晦涩,理解不了,所以先尝试写一个简单的,比如:计算器。(3)网上有很多关于计算器的实现,但大多需要有编译原理的基础,对于我这种小白实在难以理解。(4)我决定采用暴力模拟的方式,需要用正则表达式,但我不想自己实现,所以用js。最终要实现什么效果计算器接受一串字符,处理后返回结果。

产生原因:

(1)一直以来,我都想写一门语言,但无从下手。
(2)我找到了很多编译原理的教程,但始终觉得内容晦涩,理解不了,所以先尝试写一个简单的,比如:计算器。
(3)网上有很多关于计算器的实现,但大多需要有编译原理的基础,对于我这种小白实在难以理解。
(4)我决定采用暴力模拟的方式,需要用正则表达式,但我不想自己实现,所以用js。

最终要实现什么效果

实现一个计算器第1张

计算器接受一串字符,处理后返回结果。

我们来看一下要做什么:

首先需要知道有哪些“元素”,比如“12+34×56"的元素有整数12,加号,整数34,乘号,整数56,这个过程称为词法分析

然后根据符号的优先级进行组合,其过程相当于加括号,12+(34*56),这个过程称为语法分析

借用正则表达式,可以简单暴力的实现词法分析。

什么是正则表达式

正则表达式的概念,和编译原理一样,都要费好大功夫来理解,当初也是各种查资料。

尽量简单的讲一讲吧。

定义:正则表达式是一种生成器,可以生成大量相同模式的字符串。

字符串的概念大家都懂,来看一下正则表达式是怎么生成字符串的。

例子:正则表达式 ab* 可以生成'a' , 'ab' , 'abb' , 'abbb' ...

正则表达式有三种规则(并,或,闭包),其他规则都是由这三个规则组合而成。

并,直接连在一起,表示相连,例如:abc 生成’abc'

或,以符号|分隔,表示或,例如:a|b生成'a','b'

闭包,加符号*,表示重复任意次,例如:a* 生成 '','a', 'aa', 'aaa'...

一些常用的规则:

[],例如:[abcd]等价于a|b|c|d

+,例如:a+等价于aa*

?,例如:a?等价于 a|空

d,等价于[0123456789],还可以用[0-9]

w,等价于[A-Za-z0-9]

W,非w

,表示w与W的交界处

词法分析

计算器用到的正则表达式:

+:+

-:-

*:*

/:/

(:(

):)

number:d+

注意:规则中有的字符要转义,如:+

不断用正则来匹配输入的字符串,就可以。

使用js来实现:

1 functionget_words(buf){
2     patterns =[
3         ['(',        /(/],
4         [')',        /)/],
5         ['+',        /+/],
6         ['-',        /-/],
7         ['*',        /*/],
8         ['/',        ///],
9         ['number',     /d+(.d+)?/]
10 ];
11     
12     words =[];
13     flag = true;
14     while (buf &&flag) {
15         flag = false;
16         for (p inpatterns) {
17             buf =buf.trimLeft();
18             ex = patterns[p][1].exec(buf);
19             if (ex && ex['index'] == 0) {
20                 str = ex[0];
21                 flag = true;
22                 buf =buf.slice(str.length,Infinity);
23                 words.push([patterns[p][0], parseFloat(str)]);
24                 break;
25 }
26 }
27 }
28     returnwords;
29 }

对于'12+34 * (78-56)',会得到:

number,12
+,NaN
number,34
*,NaN
(,NaN
number,78
-,NaN
number,56),NaN

至此,词法分析完成。

语法分析

我们采用类似于正则的方式来描述语法分析的过程,可称其为文法

分析一波。

括号优先级最高,所以遇见括号就要计算,文法为:

<factor> => ( '(' <expr> ')' ) | 'number'

引号的称终结符,尖括号的称非终结符

非终结符表示可以继续推导,终结符表示推导终点。

其中<expr>表示整个算式

乘除优先级比加法高,所以遇见乘除就要计算,文法为:

<term> => <factor> (('*' <factor>) | ('/' <factor>))*

然后是加减:

<expr> => <term> (('+' <term>) | ('-' <term>))*

这些都可以用正则来理解。

其中每个非终结符都做成一个函数,翻译过来就成。

1 functionparse(words) {
2     //<expr>     => <term> (('+' <term>) | ('-' <term>))*
3     //<term>     => <factor> (('*' <factor>) | ('/' <factor>))*
4     //<factor> => ('(' <expr> ')')  | 'number'
5     p = 0;  
6     
7     functiontype() {
8         if (p >= words.length) return null;
9         return words[p][0];
10 }
11     functionmatch(sym) {
12         if (words[p][0] ==sym) {
13             return words[p++][1];
14 }
15         console.log('
error
');
16 }
17     functionexpr() {
18         value =term();
19         while (type() == '+' || type() == '-') {
20             if (type() == '+') {
21                 match('+');
22                 value +=term();
23             } else{
24                 match('-');
25                 value -=term();
26 }
27 }
28         returnvalue;
29 }
30     functionterm() {
31         value =factor();
32         while (type() == '*' || type() == '/') {
33             if (type() == '*') {
34                 match('*');
35                 value *=factor();
36             } else{
37                 match('/');
38                 value /= factor();
39 }
40 }
41         returnvalue;
42 }
43     functionfactor() {
44         if (type() == '(') {
45             match('(');
46             value =expr();
47             match(')');
48         } else if (type() == 'number') {
49             value =  match('number');
50 }
51         returnvalue;
52 }
53     
54     returnexpr();
55 }

写完了,哈哈。

总结

用node.js可以简单的跑起来:

折起来吧,效果在前面。

实现一个计算器第2张实现一个计算器第3张
1 var readline = require('readline');
2 var  rl =readline.createInterface(process.stdin, process.stdout);
3 
4 rl.setPrompt('calc> ');
5 rl.prompt();
6 
7 rl.on('line', function(line) {
8     words =get_words(line);
9     //console.log(words.join('
'));
10     ans =parse(words);
11 console.log(ans);
12 rl.prompt();
13 });
14 
15 rl.on('close', function() {
16     console.log('
bye bye!');
17     process.exit(0);
18 });
19 
20 functionget_words(buf){
21     patterns =[
22         ['(',        /(/],
23         [')',        /)/],
24         ['+',        /+/],
25         ['-',        /-/],
26         ['*',        /*/],
27         ['/',        ///],
28         ['number',     /d+(.d+)?/]
29 ];
30     
31     words =[];
32     flag = true;
33     while (buf &&flag) {
34         flag = false;
35         for (p inpatterns) {
36             buf =buf.trimLeft();
37             ex = patterns[p][1].exec(buf);
38             if (ex && ex['index'] == 0) {
39                 str = ex[0];
40                 flag = true;
41                 buf =buf.slice(str.length,Infinity);
42                 words.push([patterns[p][0], parseFloat(str)]);
43                 break;
44 }
45 }
46 }
47     returnwords;
48 }
49 
50 functionparse(words) {
51     //<expr>     => <term> (('+' <term>) | ('-' <term>))*
52     //<term>     => <factor> (('*' <factor>) | ('/' <factor>))*
53     //<factor> => ('(' <ecpr> ')')  | 'number'
54     p = 0;
55     
56     functiontype() {
57         if (p >= words.length) return null;
58         return words[p][0];
59 }
60     functionmatch(sym) {
61         if (words[p][0] ==sym) {
62             return words[p++][1];
63 }
64         console.log('
error
');
65 }
66     functionexpr() {
67         value =term();
68         while (type() == '+' || type() == '-') {
69             if (type() == '+') {
70                 match('+');
71                 value +=term();
72             } else{
73                 match('-');
74                 value -=term();
75 }
76 }
77         returnvalue;
78 }
79     functionterm() {
80         value =factor();
81         while (type() == '*' || type() == '/') {
82             if (type() == '*') {
83                 match('*');
84                 value *=factor();
85             } else{
86                 match('/');
87                 value /= factor();
88 }
89 }
90         returnvalue;
91 }
92     functionfactor() {
93         if (type() == '(') {
94             match('(');
95             value =expr();
96             match(')');
97         } else if (type() == 'number') {
98             value =  match('number');
99 }
100         returnvalue;
101 }
102     
103     returnexpr();
104 }
View Code

免责声明:文章转载自《实现一个计算器》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Java日期格式化(DateFormat类和SimpleDateFormat类)wifi物联网ESP8266开发板V3 ESP-12N F NodeMcu LuaCP2012 的环境搭建下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

SecureCRT优化调整、永久设置、保护眼睛和配色方案

您可以根据个人喜好调整字体大小。我已经习惯了4号字体。到目前为止,SecureCRT优化已经完成。...

mongodb 占用内存及解决方法

解决方案是限制Swap的使用:[root@mongodb~]#Sysctl wvm。swap=0查看内存最常用的命令是空闲的:[root@mongodb~]#Free totalused freesharedbuff/cacheavailableEm:78250931925992443Swap:000当新手看到used列中的值太大而Free列中的数值太小时,...

菜鸟初学Linux——Ubuntu系统中,用root权限进行复制粘贴操作

longlongago,积累了一些Linux的小知识,拿出来分享一下,希望以后能够在工作上带来一些帮助。...

etcd集群日常维护

配置文件和启动参数说明命令行|配置文件|说明data-dir|ETCD_DATA_DIR|指定节点的数据存储目录,包括节点ID,集群ID,集群初始化配置,Snapshot文件,若未指定—wal-dir,还会存储WAL文件;wal-dir|ETCD_WAL_DIR|指定节点的was文件的存储目录,若指定了该参数,wal文件会和其他数据文件分开存储。增加一个新的...

二项式分布

二项式分布一个试验只有成功和失败两种可能性,这样的试验是伯努利试验。n个独立的伯努利试验中成功的次数的离散概率分布就是二项式分布。N次试验中正好得到k次成功的概率:$$Binomleft=p^{k}left^{N-k}$$其中$=dfrac{N!...

eri

本地主机。crt-bakvim/etc/netplan/50云初始化。yaml写入网卡root@master:~#cat/etc/netplan/50 cloud init.yaml#此文件是根据#thedatasource提供的信息生成的。对其所做的更改不会在任何时候持续...