六. 基于Flex/Bison/符号表写一个仿bc计算器

[实现除函数定义及if while for语句的其它大部分功能]
参考bc命令手册(执行man bc便可看到),模仿bc的语法写一个高级计算器,能够实现整个bc所具有的大部分功能。


功能:

  1. 实现以下符号及其优先级
    +-*/^()
    &&||++--%=
    +=-=*=/=

  2. 实现给标识符赋值和调用

  3. 实现多行计算,使用\n进行隔开

  4. 实现内置变量scale来控制数字精度,obase来控制进制格式

  5. math.h中函数全部移植

已知bug:

  1. 数字过大会溢出
  2. 给变量赋值语句,两边不能有空格

效果演示:

sbc计算器演示效果


实现代码:

关于符号表设计请看我这篇《用c with class写一个符号表》

脚本:

#!/bin/bash
echo "flex执行开始!"
flex -o sbc.yy.c bc.l
echo "bison执行开始!"
bison -o sbc.tab.h bc.y
echo "gcc编译开始!"
gcc -o sbc -w *.c *.h  -lfl -lrt -lm -lpthread 
echo "删除中间文件..."
rm sbc.yy.c sbc.tab.*
echo "执行生成最终目标文件..."
echo "sbc计算器 v1.0.0" 
./sbc
rm sbc

bison代码:

/*
 *file: bc.y
 *auther: jin1ming
 *system: manjaro
 */
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "SymbolTable.h"
SymbolTable *st;
double getKey(SymbolTable* s,char* id);
void printBinary(int n);
int scale;//控制计算精度
int obase;//输出进制格式
char format[20];
%}
%union{ 
    double dbl;
    char* str;
};

%token <dbl> NUMBER IDs 
%token <str> ID
%type <dbl> expression arithmetic_expression_list
%type <dbl> arithmetic_expression conditional_expression_list
%type <dbl> conditional_expression math_func
/*以下操作符有优先级,顺序不可改变*/
%left LOR
%left LAND
%left OR
%left AND
%left NEQ EQL
%left LSS GTR LEQ GEQ
%left ADD SUB
%left MUL DIV REM
%left POWER 
%right ASSIGN ADD_ASSIGN SUB_ASSIGN MUL_ASSIGN DIV_ASSIGN REM_ASSIGN
%token EOL SCALE INC DEC NOT    OBASE
%token LPAREN RPAREN COMMA
%token ACOS ASIN ATAN ATAN2 COS COSH FMOD
%token SIN SINH TANH EXP FREXP LDEXP FLOOR
%token LOG LOG10 MODF POW SQRT CEIL FABS

%%
calclist
    :   /* 空规则 */
    | calclist statement EOL
    ;
/*
 *所识别语句分为2种:
 * 1.表达式,类似1+1,识别将输出结果
 * 2.赋值语句,类似i=1,识别将进行赋值或修改值
 */
statement
    :   expression  {   if(obase == 10){
                                    sprintf(format,"= %%.%dlf\n",scale);
                                    printf(format,$1);  
                                    }else if (obase == 8) {
                                    printf("= %o\n",(int)$1);   
                                    }   else if (obase == 16) {
                                    printf("= %x\n",(int)$1);
                                    } else if (obase == 2) {
                                    printf("= ");
                                    printBinary((int)$1);
                                    printf("\n");
                                    }
                                }
    |   assignment
    | /* 空规则,匹配空行 */
    |   system_statement
    ;
/*一些内置环境变量*/
system_statement
    :   SCALE ASSIGN NUMBER {scale = (int)$3;   }
    |   OBASE ASSIGN NUMBER {obase = (int)$3;   }
    ;
/*
 *表达式
 *分为条件表达式、算数表达式
 */
expression
    :   arithmetic_expression_list { $$ = $1; }
    |   conditional_expression_list { $$ = $1; }
    ;
/*赋值运算*/
assignment
    :   ID ASSIGN expression 
        {    st->modify(st,$1,$3);   }
    |   ID ADD_ASSIGN expression    
        {   double tem = st->find(st,$1);   
            st->modify(st,$1,tem+$3); }
    |   ID SUB_ASSIGN expression
        {   double tem = st->find(st,$1);   
            st->modify(st,$1,tem-$3); }
    |   ID MUL_ASSIGN expression
        {   double tem = st->find(st,$1);   
            st->modify(st,$1,tem*$3); }
    |   ID DIV_ASSIGN expression
        {   double tem = st->find(st,$1);   
            st->modify(st,$1,tem/$3); }
    |   ID REM_ASSIGN expression
        {   double tem = st->find(st,$1);   
            st->modify(st,$1,(int)tem%(int)$3); }
    ;
/*
 *算数表达式
 *  拆分成两个,以消除偏移/规约冲突
 */
arithmetic_expression_list
    :   arithmetic_expression_list ADD arithmetic_expression_list
        { $$ = $1 + $3; }
    |   arithmetic_expression_list SUB arithmetic_expression_list
        { $$ = $1 - $3; }
    |   arithmetic_expression_list MUL arithmetic_expression_list 
        { $$ = $1 * $3; }
    |   arithmetic_expression_list DIV arithmetic_expression_list 
        { if(0 == $3) {
                    yyerror("errno:6\tDevided by zero!\n");
                    exit(6);
            }
            $$ = $1 / $3; }
    |   arithmetic_expression_list POWER arithmetic_expression_list 
        { $$ = pow($1,$3); }
    |   arithmetic_expression_list REM arithmetic_expression_list 
        { $$ = (int)$1 % (int)$3; }
    |   arithmetic_expression 
        { $$ = $1; }
    |   LPAREN arithmetic_expression_list RPAREN 
        { $$ = $2; }
    |   math_func 
    ;   
math_func
    :   ACOS arithmetic_expression_list RPAREN 
        { $$ = acos($2); }
    |   ASIN arithmetic_expression_list RPAREN
        { $$ = asin($2); } 
    |   ATAN arithmetic_expression_list RPAREN 
        { $$ = atan($2); } 
    |   ATAN2 arithmetic_expression_list COMMA arithmetic_expression_list RPAREN 
        { $$ = atan2($2,$4); } 
    |   COS arithmetic_expression_list RPAREN  
        { $$ = cos($2); } 
    |   COSH arithmetic_expression_list RPAREN 
        { $$ = cosh($2); }  
    |   FMOD arithmetic_expression_list COMMA arithmetic_expression_list RPAREN 
        { $$ = fmod($2,$4); } 
    |   SIN arithmetic_expression_list RPAREN  
        { $$ = sin($2); } 
    |   SINH arithmetic_expression_list RPAREN  
        { $$ = sinh($2); } 
    |   TANH arithmetic_expression_list RPAREN  
        { $$ = tanh($2); } 
    |   EXP arithmetic_expression_list RPAREN  
        { $$ = exp($2); } 
    |   FREXP arithmetic_expression_list COMMA arithmetic_expression_list RPAREN  
        { $$ = frexp($2,(int)$4); } 
    |   LDEXP arithmetic_expression_list COMMA arithmetic_expression_list RPAREN  
        { $$ = ldexp($2,(int)$4); } 
    |   FLOOR arithmetic_expression_list RPAREN 
        { $$ = floor($2); } 
    |   LOG arithmetic_expression_list RPAREN  
        { $$ = log($2); } 
    |   LOG10 arithmetic_expression_list RPAREN  
        { $$ = log10($2); } 
    |   MODF arithmetic_expression_list COMMA arithmetic_expression_list RPAREN  
        { $$ = modf($2,&$4); } 
    |   POW arithmetic_expression_list COMMA arithmetic_expression_list RPAREN  
        { $$ = pow($2,$4); } 
    |   SQRT arithmetic_expression_list RPAREN  
        { $$ = sqrt($2); } 
    |   CEIL arithmetic_expression_list RPAREN 
        { $$ = ceil($2); }  
    |   FABS arithmetic_expression_list RPAREN 
        { $$ = fabs($2); } 
    ;
arithmetic_expression
    : NUMBER { $$ = $1; }
    |   ID {    $$ = getKey(st,$1); }
    | ID    INC { double v = st->find(st,$1)+1;
                            st->modify(st,$1,v); 
                            $$ = v-1; }
    | ID    DEC { double v = st->find(st,$1)-1;
                            st->modify(st,$1,v); 
                            $$ = v+1; }
/*  | INC ID { double v = st->find(st,$2)+1;
                            st->modify(st,$2,v); 
                            $$ = v; }
    | DEC ID    { double v = st->find(st,$2)-1;
                            st->modify(st,$2,v); 
                            $$ = v; }
                            */
    |   ADD ID  { $$ = st->find(st,$2); }
    |   SUB ID  { $$ = 0 - st->find(st,$2); }
    ;
/*
 *条件表达式
 *拆分成两个,以消除偏移/规约冲突
 */
conditional_expression_list
    :   conditional_expression { $$ = $1; }
    |   conditional_expression LAND conditional_expression
conditional_expression { $$ = $1 && $3; }
    |   conditional_expression LOR conditional_expression
conditional_expression { $$ = $1 || $3; }
    ;
conditional_expression
    :   arithmetic_expression EQL arithmetic_expression 
        {   $$ = ( $1 == $3 );  }
    | arithmetic_expression LSS arithmetic_expression
        {   $$ = ( $1 < $3 );   }
    | arithmetic_expression GTR arithmetic_expression
        {   $$ = ( $1 > $3 );   }
    | arithmetic_expression NEQ arithmetic_expression
        {   $$ = ( $1 != $3 );  }
    | arithmetic_expression LEQ arithmetic_expression
        {   $$ = ( $1 <= $3 );  }
    | arithmetic_expression GEQ arithmetic_expression
        {   $$ = ( $1 >= $3 );  }
    |   NOT conditional_expression { $$ = !$2; }
    ;

%%

int main(int argc,char **argv)
{
        st = newSymbolTable(); 
        scale = 0;//默认不保留小数
        obase = 10;//默认输出十进制
    yyparse();
    
    return 0;
}

int yyerror(char *s)
{
    printf("error: %s\n",s);
    return 0;
}

/*如果符号表不存在当前标识符,将返回0*/
double getKey(SymbolTable* s,char* id)
{
        double tmp = st->find(st,id);
        if(st->find(st,id) == __DBL_MAX__) {
                return (double)0;
        } else {
                return tmp;
        }
        return (double)0;
}
void printBinary(int n) 
{ 
        int a; 
        a=n%2; 
        n=n>>1; 
        if (n!=0) 
        printBinary(n); 
        printf("%d",a); 
} 

flex代码:

/*
 *file: bc.l
 *auther: jin1ming
 *system: manjaro
 */
%option yylineno
%{
#include "sbc.tab.h"
%}
 
/*数字定义*/
/*科学计数表示*/
science {decimal}(\.[0-9]+)?([Ee][-+]?[0-9]+)?
/*十进制*/
decimal 0|[1-9][0-9]*
/*十六进制*/
hexadecimal 0[xX][a-fA-F0-9]+
/*二进制*/
binary 0[bB][01]+
/*八进制*/
octal 0[0-7]+
/*总表示*/
number ({hexadecimal}|{binary}|{science}|{octal})(([uU]?[Ll]?)|([Ll]?[Uu]?)|([fF]?))
/*注意浮点数总是有符号,不需要Uu后缀,所以在接下来单做一个浮点数异常处理*/
/*数字异常处理*/
floatexcption {decimal}\.[0-9]+([Ee]?[-+]?[0-9]+)?[Uu]
excption [0-9][0-9a-zA-Z\.]+

/*小数点后的精度*/
SCALE scale
/*输出进制*/
OBASE obase
/*注释*/
COMMENT \/\*(.|\n)*\/

/*标识符定义*/
identifier [a-z_A-Z][a-z_A-Z0-9]*
 
/*其它字符*/
whitespace [ \s\t\r\f\v]+
COMMA   ,
EOL         ;
errno       .
%%

"="         { return ASSIGN;    }
"+="        {   return ADD_ASSIGN;  }
"-="        {   return SUB_ASSIGN;  }
"*="        { return MUL_ASSIGN;    }
"/="        { return DIV_ASSIGN;    }
"%="        {   return REM_ASSIGN;  }
 /*运算符*/
"+"         { return ADD; }
"-"         { return SUB; }
"*"         { return MUL; }
"/"         { return DIV; }
"^"         { return POWER; }
"%"         { return REM; }
"++"        { return INC; }
"--"        { return DEC; }
"!"         {   return NOT;  }
"&&"        {   return LAND;    }
"||"        {   return LOR; }
 /*函数库*/
"acos("     { return ACOS; }
"asin("     { return ASIN; }
"atan("     { return ATAN; }
"atan2("    { return ATAN2; }
"cos("      { return COS; }
"cosh("     { return COSH; }
"sin("      { return SIN; }
"sinh("     { return SINH; }
"tanh("     { return TANH; }
"exp("      { return EXP; }
"frexp("    { return FREXP; }
"ldexp("    { return LDEXP; }
"log("      { return LOG; }
"log10("    { return LOG10; }
"modf("     { return MODF; }
"pow("      { return POW; }
"sqrt("     { return SQRT; }
"ceil("     { return CEIL; }
"fabs("     { return FABS; }
"floor("    { return FLOOR; }
"fmod("     { return FMOD; }
 /*标点符号*/
"("         {   return LPAREN;  }
")"         { return RPAREN;    }
","         {   return COMMA; }
(;\n)|\n|;      {   return EOL; }
 /*系统内置变量*/
{SCALE}             { return SCALE; }
{OBASE}             { return OBASE; }
 /*数字及标识符*/
{number}            { yylval.dbl = atof(yytext); return NUMBER;}
{identifier} { yylval.str = strdup(yytext); return ID;}
 /*数字异常处理*/
{floatexcption} { printf("errno:2\tfloatexcption\n");exit(2);}
{excption}      { printf("errno:3\texcption\n");exit(3);}
 /*未识别字符*/
{errno}             {printf("errno:5\tOn line %d,mystery character:  %s\n",yylineno,yytext);exit(4);}
 /*跳过空白和注释*/
{whitespace} {}
{COMMENT}           {}
%%
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容

  • 1. 介绍 浏览器可能是最广泛使用的软件。本书将介绍浏览器的工作原理。我们将看到,当你在地址栏中输入google....
    康斌阅读 2,022评论 7 18
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,237评论 0 4
  • 前端必读:浏览器内部工作原理[https://kb.cnblogs.com/page/129756/] 作者: T...
    我是强强阅读 1,149评论 0 2
  • 转载说明 一、介绍 浏览器可以被认为是使用最广泛的软件,本文将介绍浏览器的工作原理,我们将看到,从你在地址栏输入g...
    17碎那年阅读 2,453评论 0 22
  • 7.9/88%,香港喜剧片,2.5上映。
    Bonnie_Ho阅读 151评论 0 0