我会使用几乎随处可用的工具。
我喜欢 lex/yacc,因为我认识它们,但到处都有等价物。因此,在您编写复杂代码之前,请先看看是否有工具可以帮助您简化代码(此类问题之前已经解决,所以不要重新发明轮子)。
所以,我会使用 lex(flex)/yacc(bison):
e.l
%option noyywrap
Number [0-9]+
WhiteSpace [ \t\v\r]+
NewLine \n
%{
#include <stdio.h>
%}
%%
\( return '(';
\) return ')';
\+ return '+';
\- return '-';
\* return '*';
\/ return '/';
{Number} return 'N';
{NewLine} return '\n';
{WhiteSpace} /* Ignore */
. fprintf(stdout,"Error\n");exit(1);
%%
e.y
%{
#include <stdio.h>
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 <op> MultOp AddOp
%type <value> 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();
}
构建
> flex e.l
> bison e.y
> gcc *.c
> ./a.out
((5 + (3 + (7 * 2))) - (8 * 9)) / 72
Result: -6.944444e-01
>
上面也处理正常的运算符优先规则:
不是因为我做了什么,而是很久以前有人聪明地解决了这个问题,现在您可以轻松获得表达式解析的语法规则(只需 google C Grammer 并撕掉您需要的部分)。
> ./a.out
2 + 3 * 4
Result: 1.400000e+01