运行时公式评估

时间:2015-02-17 23:08:08

标签: c parsing fortran bison yacc

我想评估用户可以为许多数据点输入的公式,因此效率是一个问题。这是针对Fortran项目的,但到目前为止我的解决方案都集中在使用yacc / bison语法,所以我可能会使用Fortran的iso_c_binding功能来连接yyparse()。

首选(目前为止)解决方案是Bison手册中经典mfcalc计算器示例的一个小扩展,其中bison语法也用于识别(单个)变量名称(这并不难) )。

问题是在可执行语句中该怎么做。我在那里看到两个选择。

首先,我可以简单地评估解析时的表达式,如mfcalc示例中那样。

其次,我可以调用一次bison解析器进行解析,并创建一个基于堆栈(反向抛光)的被解析公式表示,所以 2 + 3*x将被翻译为2 3 * +(当然,作为相关数据结构)。

语法的相关部分如下所示:

%union {
    double val;
    char *c;
    int fcn;
  }

%type <val> NUMBER
%type <c> VAR
%type <fcn> Function

/* Tokens and %left PLUS MINUS etc. left out for brevity */

%%
...

Function:
      SIN     { $$=SIN; }
      | COS     { $$=COS; }
      | TAN     { $$=TAN; }
      | SQRT    { $$=SQRT; }

Expression:
      NUMBER { push_number($1); }
      | VAR  { push_var($1); }
      | Expression PLUS Expression { push_operand(PLUS); }
      | Expression MINUS Expression { push_operand(MINUS); }
      | Expression DIVIDE Expression { push_operand(DIVIDE); }
      | MINUS Expression %prec NEG { push_operand(NEG); }
      | LEFT_PARENTHESIS Expression RIGHT_PARENTHESIS;
      | Function LEFT_PARENTHESIS Expression RIGHT_PARENTHESIS { push_function($1); }
      | Expression POWER Expression { push_operand(POWER); }

函数push _...会将公式放入一个结构数组中,这些结构包含一个包含令牌和yacc联合的结构。

然后使用非常简单(并且希望很快)的解释器来解释RPN。

所以,问题。

第二种方法有效吗?我认为它来自我对bison(或yacc)处理shift和reduce的方式的理解(基本上,这会改变数字并减少表达式,所以顺序应该保证为了正确的RPN),但我不太确定。

另外,使用$$构造(第一种方法)简单地评估函数是否值得付出额外的努力?

最后,还有其他更好的解决方案吗?我曾考虑使用语法树,但我认为额外的努力实际上是值得的。此外,我倾向于认为使用树是一种过度杀戮,阵列可以做得很好: - )

1 个答案:

答案 0 :(得分:1)

生成三地址虚拟操作比RPN稍微困难一点。实际上,RPN是一个虚拟堆栈机器。三个地址操作 - 也可以很容易地进入数组 - 可能更快解释,并且从长远来看可能更灵活。

将表达式解析为某种内部形式的主要优点是,评估内部表单可能比重新分析原始字符串更快。情况可能并非如此,但通常是因为将浮点文字转换为浮点数(相对而言)非常慢。

还有一种中间情况,即将表达式标记(放入数组),然后在解析标记流时直接进行评估。 (实际上,这会使野牛成为您的虚拟机。)

这些策略中哪一项最好取决于您的用例的详细信息,但这些策略都不困难,因此您可以尝试所有三种策略并进行比较。