用lex和yacc制作简单计算器

简介

最近想玩下编译,那么在这之前先了解了下脚手架,其中包括了lex和yacc,下面简单介绍下lex和yacc。

lex

Lex是LEXical compiler的缩写,是Unix环境下非常著名的工具,主要功能是生成一个词法分析器(scanner)的C源码,描述规则采用正则表达式(regular expression)。

yacc

yacc(Yet Another Compiler Compiler),是一个经典的生成语法分析器的工具。是Unix/Linux上一个用来生成编译器的编译器(编译器代码生成器)。yacc生成的编译器主要是用C语言写成的语法解析器(Parser),需要与词法解析器Lex一起使用,再把两部份产生出来的C程序一并编译。yacc本来只在Unix系统上才有,但现时已普遍移植往Windows及其他平台。

好了,今天打算先用这两个简单的做个计算器,只是简单的实现一下四则运算而已,一则了解下工具lex和yacc,二则练练手。

准备工作

环境:Mac OS

  • 安装lex: brew install flex
  • 安装yacc: brew install byacc

PS:其中 flexlex 的改进版本, byacc 是伯克利分校的 yacc ,用法同 yacc

实现

首先建立个文件夹,名为 calcu 。接下来写 lex 文件,后缀名是 .l ,这里命名为 calc.l

 1 %{
 2 #include <stdio.h>
 3 #include "y.tab.h"
 4 
 5 int 
 6 yywrap(void)
 7 {
 8     return 1;
 9 }
10 %}
11 
12 %%
13 
14 "+"     return ADD;
15 "-"     return SUB;
16 "*"     return MUL;
17 "/"     return DIV;
18 "\n"    return CR;
19 ([1-9][0-9]*)|0|([0-9]+\.[0-9]*) {
20     double temp;
21     sscanf(yytext, "%lf", &temp);
22     yylval.double_value = temp;
23     return DOUBLE_LITERAL;
24 }
25 [ \t];
26 . {
27     fprintf(stderr, "lexical error.\n");
28     exit(1);
29 }
30 %%

接下来是 yacc 文件的编写,后缀名为 .y ,这里命名为 calc.y ,具体代码如下:

 1 %{
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #define YYDEBUG 1
 5 %}
 6 %union {
 7     int          int_value;
 8     double       double_value;
 9 }
10 %token <double_value>      DOUBLE_LITERAL
11 %token ADD SUB MUL DIV CR
12 %type <double_value> expression term primary_expression
13 %%
14 line_list
15     : line
16     | line_list line
17     ;
18 line
19     : expression CR
20     {
21         printf(">>%lf\n", $1);
22     }
23 expression
24     : term
25     | expression ADD term
26     {
27         $$ = $1 + $3;
28     }
29     | expression SUB term
30     {
31         $$ = $1 - $3;
32     }
33     ;
34 term
35     : primary_expression
36     | term MUL primary_expression 
37     {
38         $$ = $1 * $3;
39     }
40     | term DIV primary_expression
41     {
42         $$ = $1 / $3;
43     }
44     ;
45 primary_expression
46     : DOUBLE_LITERAL
47     ;                 
48 %%
49 int
50 yyerror(char const *str)
51 {
52     extern char *yytext;
53     fprintf(stderr, "parser error near %s\n", yytext);
54     return 0;
55 }
56 
57 int main(void)
58 {
59     extern int yyparse(void);
60     extern FILE *yyin;
61 
62     yyin = stdin;
63     if (yyparse()) {
64         fprintf(stderr, "Error ! Error ! Error !\n");
65         exit(1);
66     }
67 }

代码解析

忙(懒)而未写,有待补充

运行

yacc -dv calc.y

会生成 yacc 的两个文件,分别是 y.tab.cy.tab.h

lex calc.l

会生成 lex.yy.c 文件。其中 y.tab.c 包含 yacc 生成的语法分析器的代码, lex.yy.c 是词法分析器的代码,为了将 calc.y 中定义的记号以及联合体传给 lex.yy.c ,yacc会生成 y.tab.h 的头文件。

cc -o calc y.tab.c lex.yy.c

生成可执行文件calc。

然后运行

./calc

然后就可以使用计算器咯~

参考资料

Related Posts

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章