lex yacc 创建一个桌面计算器

这篇博客总结了使用lex(flex)2.5.37和yacc(bison)2.7创建一个支持加减乘除及括号的桌面计算器的过程。通过逐步构建能识别浮点数的词法分析器和处理单个数字的语法分析器,最终实现可以连续输入并计算表达式的功能,例如3.2+5.3*(6.2+3.3)。作者提到,这种方式比手工编写更容易,但理解生成的代码颇具挑战性。推荐了Flex和Bison生成文件的分析资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作为学习龙书前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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值