Writing a simple equation parser

前端 未结 10 1165
-上瘾入骨i
-上瘾入骨i 2020-12-01 03:56

What sorts of algorithms would be used to do this (as in, this is a string, and I want to find the answer):

((5 + (3 + (7 * 2))) - (8 * 9)) / 72
10条回答
  •  天命终不由人
    2020-12-01 04:29

    I would use the tools that are available nearly everywhere.
    I like lex/yacc because I know them but there are equivalents everywhere. So before you write complex code see if there are tools that can help you to make it simple (problems like this have been solved before so don;t re-invent the wheel).

    So, using lex(flex)/yacc(bison) I would do:

    e.l

    %option noyywrap
    
    Number      [0-9]+
    WhiteSpace  [ \t\v\r]+
    NewLine     \n
    %{
    #include 
    %}
    
    %%
    
    \(              return '(';
    \)              return ')';
    \+              return '+';
    \-              return '-';
    \*              return '*';
    \/              return '/';
    
    {Number}        return 'N';
    {NewLine}       return '\n';
    {WhiteSpace}    /* Ignore */
    
    .               fprintf(stdout,"Error\n");exit(1);
    
    
    %%
    

    e.y

    %{
    #include 
        typedef double (*Operator)(double,double);
        double mulOp(double l,double r)  {return l*r;}
        double divOp(double l,double r)  {return l/r;}
        double addOp(double l,double r)  {return l+r;}
        double subOp(double l,double r)  {return l-r;}
    extern char* yytext;
    extern void yyerror(char const * msg);
    %}
    
    %union          
    {
        Operator        op;
        double          value;
    }
    
    %type           MultOp AddOp
    %type        Expression MultExpr AddExpr BraceExpr
    
    %%
    
    Value:          Expression '\n'   { fprintf(stdout, "Result: %le\n", $1);return 0; }
    
    Expression:     AddExpr                          { $$ = $1;}
    
    AddExpr:        MultExpr                         { $$ = $1;}
                |   AddExpr   AddOp  MultExpr        { $$ = ($2)($1, $3);}
    
    MultExpr:       BraceExpr                        { $$ = $1;}
                |   MultExpr  MultOp BraceExpr       { $$ = ($2)($1, $3);}
    
    BraceExpr:      '(' Expression ')'               { $$ = $2;}
                |   'N'                              { sscanf(yytext,"%le", &$$);}
    
    MultOp:         '*'                              { $$ = &mulOp;}
                |   '/'                              { $$ = &divOp;}
    AddOp:          '+'                              { $$ = &addOp;}
                |   '-'                              { $$ = &subOp;}
    %%
    
    void yyerror(char const * msg)
    {
        fprintf(stdout,"Error: %s", msg);
    }
     
    int main()
    {
        yyparse();
    }
    

    Build

    > flex e.l
    > bison e.y
    > gcc *.c
    > ./a.out
    ((5 + (3 + (7 * 2))) - (8 * 9)) / 72
    Result: -6.944444e-01
    >
    

    The above also handles normal operator precedence rules:
    Not because of anything I did,but somebody smart worked this out ages ago and now you can get the grammar rules for expression parsing easily (Just google C Grammer and rip the bit you need out).

    > ./a.out
    2 + 3 * 4
    Result: 1.400000e+01
    

提交回复
热议问题