服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C指针原理教程之编译原理-小型计算器实现

C指针原理教程之编译原理-小型计算器实现

2021-07-21 14:57myhaspl C/C++

本文给大家分享的是如何使用C语言编写一个小型计算器的实例代码,有需要的小伙伴可以参考下

1、打开cygwin,进入home目录,home目录在WINDOWS系统的cygwin安装目录映射为home目录。

2、首先,在home目录中新建文件夹,在文件夹中放置如下内容的test1.l

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*统计字数*/
 
%{
 
int chars=0;
 
int words=0;
 
int lines=0;
 
%}
 
%%
 
[a-zA-Z]+ {words++;chars+=strlen(yytext);}
 
\n {chars++;lines++;}
 
.  {chars++;}
 
%%
 
main(int argc,char**argv)
 
{
 
  yylex();
 
  printf("%d%d%d\n",lines,words,chars);
 
}

然后调用flex生成词法分析器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Administrator@2012-20121224HD /home/flexlinux
 
$ cd /home
 
Administrator@2012-20121224HD /home
 
$ cd flexlinux
 
Administrator@2012-20121224HD /home/flexlinux
 
$ flex test1.l
 
Administrator@2012-20121224HD /home/flexlinux
 
$

可以看到目录中的lex.yy.c就是刚生成的C源码,可分析词法。

?
1
2
3
4
5
Administrator@2012-20121224HD /home/flexlinux
 
$ ls
 
lex.yy.c test1.l

二、flex和bison联合工作

1 、我们开始构造一个计算器程序。

创建flex代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/*计算器*/
 
%{
 
enum yytokentype{
 
   NUMBER=258,
 
 ADD=259,
 
 SUB=260,
 
 MUL=261,
 
 DIV=262,
 
 ABS=263,
 
 EOL=264
 
};
 
int yylval;
 
%}
 
%%
 
"+"  {return ADD;}
 
"-"  {return SUB;}
 
"*"  {return MUL;}
 
"/"  {return DIV;}
 
"|"  {return ABS;}
 
[0-9]+ {yylval=atoi(yytext);return NUMBER;}
 
\n {return EOL;}
 
[ \t] {/*空白忽略*/}
 
. {printf("非法字符 %c\n",*yytext);}
 
%%
 
main(int argc,char**argv)
 
{
 
  int tok;
 
  while(tok=yylex()){
 
   printf("%d",tok);
 
 if (tok==NUMBER) printf("=%d\n",yylval);
 
 else printf("\n");
 
  }
 
}

2、编译

?
1
2
3
4
5
6
7
Administrator@2012-20121224HD /home/flexlinux
 
$ flex test2.l
 
Administrator@2012-20121224HD /home/flexlinux
 
$ gcc lex.yy.c -lfl

3、运行

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Administrator@2012-20121224HD /home/flexlinux
 
$ ./a
 
- 12 66
 
260
 
258=12
 
258=66
 
264
 
Administrator@2012-20121224HD /home/flexlinux
 
$ ./a
 
/ 56 2 + |32
 
262
 
258=56
 
258=2
 
259
 
263
 
258=32
 
264
 
Administrator@2012-20121224HD /home/flexlinux
 
$

(2)计算器的BISON程序

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
%{
#include <stdio.h>
%}
 
%token NUMBER
%token ADD SUB MUL DIV ABS
%token EOL
 
%%
 
calclist:/**/
 |calclist exp EOL{printf ("=%d\n",$2);}
 ;
 
exp:factor {$$ = $1;}
 |exp ADD factor{$$=$1+$3;}
 |exp SUB factor{$$=$1-$3;}
 ;
 
factor:term {$$=$1;}
 |factor MUL term{$$=$1*$3;}
 |factor DIV term{$$=$1/$3;}
 ;
term:NUMBER {$$=$1;}
 |ABS term {$$=$2>=0?$2:-$2;}
 ;
%%
main(int argc,char **argv){
yyparse();
}
yyerror(char *s)
{
 fprintf(stderr,"error:%s\n",s);
}
$ bison -d test2.y
t$ ls
 
test2.tab.c test2.tab.h test2.y test2.y~

然后,修改刚才的flex文件,将其命名为test21.l

test2.tab.h中包含了记号编号的定义和yylval的定义,因此,将其第一部分的相关定义删除,并改为:

/计算器/

%{

  #include "test2.tab.h"

%}

然后删除,其第三部分的main函数。

最后,进行编译。

?
1
2
3
4
5
bison -d test2.y
 
flex test21.l
 
gcc test2.tab.c lex.yy.c -lfl

可以测试一下

?
1
2
3
4
5
6
7
8
9
root@myhaspl:~# ./a.out
 
12 + 36 * 2
 
=84
 
12 / 6 + 2 * 3
 
=8

(2)扩充计算器

加入对括号和注释的支持,

首先修改flex文件,在第二部分加入更多的词法规则(对于注释直接忽略):

"("   {return LEFTBRACKET;}

")"   {return RIGHTBRACKET;}

"#". /忽略注释*/

然后,修改bison文件,在第二部分加入更多的语法规则:

term:NUMBER {$$=$1;}

  |ABS term {$$=$2>=0?$2:-$2;}

  |LEFTBRACKET exp RIGHTBRACKET {$$=$2;}

  ;

我们的注释以“#”表示

测试结果

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
myhaspl@myhaspl:~/flex_bison/2$ make
 
bison -d calculator.y
 
flex calculator.l
 
gcc calculator.tab.c lex.yy.c -lfl
 
myhaspl@myhaspl:~/flex_bison/2$ ls
 
a.out     calculator.tab.c calculator.y makefile
 
calculator.l calculator.tab.h lex.yy.c
 
myhaspl@myhaspl:~/flex_bison/2$ ./a.out
 
12-36*10/(1+2+3)#compute
 
=-48
 
^C
myhaspl@myhaspl:~/flex_bison/2$

前面都是以键盘输入 的方式进行计算器运算,我们下面以文件方式提供给该解释器进行计算,首先,将flex文件改为(将其中中文去除,然后对于非法字符的出现进行忽略):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
%{
#include "calculator.tab.h"
%}
 
%%
"+"  {return ADD;}
"-"  {return SUB;}
""  {return MUL;}
"/"  {return DIV;}
"|"  {return ABS;}
"("  {return LEFTBRACKET;}
")"  {return RIGHTBRACKET;}
"#". /comment/
[0-9]+ {yylval=atoi(yytext);return NUMBER;}
\n {return EOL;}
[ \t] /blank/
. /invalid char/
%

接着,改bison文件,加入对文件的读写

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
%{
#include <stdio.h>
%}
 
%token NUMBER
%token ADD SUB MUL DIV ABS LEFTBRACKET RIGHTBRACKET
%token EOL
 
%%
 
calclist:/**/
 |calclist exp EOL{printf ("=%d\n",$2);}
 ;
 
exp:factor {$$ = $1;}
 |exp ADD factor{$$=$1+$3;}
 |exp SUB factor{$$=$1-$3;}
 ;
 
 
factor:term {$$=$1;}
 |factor MUL term{$$=$1*$3;}
 |factor DIV term{$$=$1/$3;}
 ;
term:NUMBER {$$=$1;}
 |ABS term {$$=$2>=0?$2:-$2;}
 |LEFTBRACKET exp RIGHTBRACKET {$$=$2;}
 ;
%%
main(int argc,char **argv){
int i;
if (argc<2){
  yyparse();
}
else{
  for(i=1;i<argc;i++)
    {
    FILE *f=fopen(argv[i],"r");
    if (!f){
     perror(argv[i]);
     return (1);
    }
   yyrestart(f);
   yyparse();
   fclose(f);
  }
}
}
 
yyerror(char *s)
{
 fprintf(stderr,"error:%s\n",s);
}

最后 测试一下

?
1
2
3
4
5
6
7
8
root@myhaspl:~/test/3# make
bison -d calculator.y
flex calculator.l
gcc calculator.tab.c lex.yy.c -lfl
root@myhaspl:~/test/3# ./a.out mycpt1.cpt mycpt2.cpt
=158
=-8
root@myhaspl:~/test/3#

其中两个CPT文件内容类似 为:

12*66/(10-5)

我们接着完善这个计算器程序,让算式能显示出来,修改calculator.l

通过加入printf语句,打印词法分析器解析到的字符。比如 :

..................

[0-9]+ {yylval=atoi(yytext);printf("%d",yylval);return NUMBER;}

\n  {return EOL;}

[ \t] /blank/

. /invalid char/

%%

然后编译执行。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root@myhaspl:~/test/4# make
 
bison -d calculator.y
 
flex calculator.l
 
gcc calculator.tab.c lex.yy.c -lfl
 
root@myhaspl:~/test/4# ./a.out
 
12+66
 
12+66=78
 
^C
 
root@myhaspl:~/test/4# ./a.out mycpt1.cpt mycpt2.cpt 
 
12*66/(10-5)=158
 
77/(10+1)-15=-8

接下来加上读取的行号,将结果的显示更加人性化

flex文件要改:

\n  {printf("<line:%4d>",yylineno);yylineno++;return EOL;}

然后,bison文件也改:

calclist:/**/
  |calclist exp EOL{printf ("the result is:%d\n",$2);}
  ;

最后 ,编译运行测试一下。

?
1
2
3
4
5
6
7
8
9
10
root@myhaspl:~/test/4# make
bison -d calculator.y
flex calculator.l
gcc calculator.tab.c lex.yy.c -lfl
root@myhaspl:~/test/4# ./a.out mycpt1.cpt mycpt2.cpt
1266/(10-5)<line:  1>the result is:158
12/22-8<line:  2>the result is:-8
77(6-2)<line:  3>the result is:308
77/(10+1)-15<line:  4>the result is:-8
root@myhaspl:~/test/4#

 

延伸 · 阅读

精彩推荐
  • C/C++关于C语言中E-R图的详解

    关于C语言中E-R图的详解

    今天小编就为大家分享一篇关于关于C语言中E-R图的详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看...

    Struggler095962021-07-12
  • C/C++C语言实现双人五子棋游戏

    C语言实现双人五子棋游戏

    这篇文章主要为大家详细介绍了C语言实现双人五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    两片空白7312021-11-12
  • C/C++深入C++拷贝构造函数的总结详解

    深入C++拷贝构造函数的总结详解

    本篇文章是对C++中拷贝构造函数进行了总结与介绍。需要的朋友参考下...

    C++教程网5182020-11-30
  • C/C++c/c++实现获取域名的IP地址

    c/c++实现获取域名的IP地址

    本文给大家汇总介绍了使用c/c++实现获取域名的IP地址的几种方法以及这些方法的核心函数gethostbyname的详细用法,非常的实用,有需要的小伙伴可以参考下...

    C++教程网10262021-03-16
  • C/C++OpenCV实现拼接图像的简单方法

    OpenCV实现拼接图像的简单方法

    这篇文章主要为大家详细介绍了OpenCV实现拼接图像的简单方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    iteye_183805102021-07-29
  • C/C++C语言main函数的三种形式实例详解

    C语言main函数的三种形式实例详解

    这篇文章主要介绍了 C语言main函数的三种形式实例详解的相关资料,需要的朋友可以参考下...

    ieearth6912021-05-16
  • C/C++c/c++内存分配大小实例讲解

    c/c++内存分配大小实例讲解

    在本篇文章里小编给大家整理了一篇关于c/c++内存分配大小实例讲解内容,有需要的朋友们可以跟着学习参考下。...

    jihite5172022-02-22
  • C/C++使用C++制作简单的web服务器(续)

    使用C++制作简单的web服务器(续)

    本文承接上文《使用C++制作简单的web服务器》,把web服务器做的功能稍微强大些,主要增加的功能是从文件中读取网页并返回给客户端,而不是把网页代码...

    C++教程网5492021-02-22