lex yacc 创建一个桌面计算器

作为学习龙书前4章一个总结,做一个计算器,支持+-*/(),*/优先,lex采用flex2.5.37,yacc采用bison2.7

最后的效果是可以连续输入表达式进行计算,比如3.2+5.3*(6.2+3.3)

首先构建可以单独运行的可以识别浮点数的词法分析器和可以处理单个数字的语法分析器,然后进行修改并将两者结合起来

首先构建独立词法分析器,参考龙书中图4-60

%{
#include "stdio.h"
float yylval;
float NUMBER;
%}

number  [0-9]+\.?|[0-9]*\.[0-9]+
%%
[ ]     { /* skip blanks */ }
{number}        { sscanf(yytext, "%f", &yylval);
                  return NUMBER; }
\n|.    { return yytext[0]; }
%%

int yywrap()
{
    return 1;
}

void main()
{
    yylex();
    printf("output:%f\n", yylval);
}

然后构建词法分析器

%{
#include <ctype.h>
%}

%token NUMBER

%%
line    : expr '\n'     { printf("output:%f\n", $1); }
        ;
expr    : expr '+' term { $$ = $1 + $3; printf("expr%f=%f+%f\n", $$,$1,$3); }
        | expr '-' term { $$ = $1 - $3; printf("expr%f=%f-%f\n", $$,$1,$3); }
        | term { $$ = $1; printf("expr=%f\n", $1); }
        ;
term    : term '*' factor { $$ = $1 * $3; printf("term%f=%f*%f\n", $$,$1,$3); }
        | term '/' factor { $$ = $1 / $3;  printf("term%f=%f/%f\n", $$,$1,$3); }
        | factor        { $$ = $1; printf("factor:%f\n", $1); }
        ;
factor  : '(' expr ')'  { $$ = $2;  printf("factor(%f)\n", $2);}
        | NUMBER        { $$ = $1; printf("number:%f\n", $1); }
        ;
%%
yylex() {
    int c;
    c = getchar();
    if (isdigit(c))
    {
        yylval = c-'0';
        return NUMBER;
    }
    return c;
}
void yyerror(char *s)
{
    printf("%s\n", s);
}

将二者结合起来发现词法分析器不能正确识别浮点数,原因是YACC默认为int,修改即可:#define YYSTYPE float

first.l

%{
%}
number  [0-9]+
%%
[ ]     { /* skip blanks */ }
{number}        { yylval = atoi(yytext);
                  return NUMBER; }
\n|.    { return yytext[0]; }
%%

int yywrap()
{
    return 1;
}

second.y
%{
#define YYSTYPE float
#include <ctype.h>
%}

%token NUMBER

%%
line    : expr '\n'     { printf("output:%d\n", $1); }
        ;
expr    : expr '+' term { $$ = $1 + $3; printf("expr%d=%d+%d\n", $$,$1,$3); }
        | expr '-' term { $$ = $1 - $3; printf("expr%d=%d-%d\n", $$,$1,$3); }
        | term { $$ = $1; printf("expr=%d\n", $1); }
        ;
term    : term '*' factor { $$ = $1 * $3; printf("term%d=%d*%d\n", $$,$1,$3); }
        | term '/' factor { $$ = $1 / $3;  printf("term%d=%d/%d\n", $$,$1,$3); }
        | factor        { $$ = $1; printf("factor:%d\n", $1); }
        ;
factor  : '(' expr ')'  { $$ = $2;  printf("factor(%d)\n", $2);}
        | NUMBER        { $$ = $1; printf("number:%d\n", $1); }
        ;
%%
#include "lex.yy.c"
void yyerror(char *s)
{
    printf("%s\n", s);
}


第一行 line : expr '\n'是为了让\n作为一个匹配的结束,否则只能计算一次

这样一个计算器就生成了,比自己手动写一个容易多了,当然要理解生成的代码还是很不容易。

关于lex代码阅读可以参考:Flex生成文件分析

bison代码阅读可以参考:Bison生成文件分析

#define YYSTYPE float

你可能感兴趣的:(lex yacc 创建一个桌面计算器)