3

我正在使用 Boost Spirit 在某些软件中实现功能,允许用户输入将重复应用于输入流的数学方程。输入流值表示为符号boost::spirit::qi::symbols,用户可以在其等式中引用这些符号。(例如out1 = 3 * in1 + in2

解析和编译用户方程对性能不敏感,但计算其输出值是因为它构成时间关键管道的一部分。

在文档中使用 Spirit 的标准方式是在解析输入时计算输入的输出(属性)。但是,在每次计算之间,只有符号( 、 等)的属性值out1in1发生变化,感觉可能有一种更有效的方法来实现这一点,也许是通过缓存表达式的抽象语法树并重复它。

给定一组新的符号值,重新计算这个(固定)方程的值的最有效方法是什么?

4

2 回答 2

4

使用 Spirit 的标准方式并不像您想象的那样有限。

虽然您可以使用它即时计算即时值,但更常见的是在输出属性中构建AST 树,可以转换(简化、优化)和解释(例如发出虚拟机甚至汇编指令) .

编译器教程完整显示了这一点,但计算器示例非常接近您似乎正在寻找的内容:http: //www.boost.org/doc/libs/1_55_0/libs/spirit/example/qi/compiler_tutorial /

  • calc1在 example/qi/compiler_tutorial/calc1.cpp

    演示语法的普通计算器示例。解析器只是一个语法检查器,不做任何语义评估。

  • calc2在 example/qi/compiler_tutorial/calc2.cpp

    一个计算器示例,演示了使用普通函数的语法和语义操作。解析器打印适合基于堆栈的虚拟机的代码。

  • calc3在 example/qi/compiler_tutorial/calc3.cpp

    一个计算器示例,演示了使用 phoenix 进行实际表达式评估的语法和语义动作。解析器本质上是一个动态评估表达式的“解释器”。

这对您来说很有趣,因为它在解析期间停止进行计算:

  • calc4在 example/qi/compiler_tutorial/calc4.cpp

    演示 AST 生成的计算器示例。AST,一旦创建,就会被遍历,

    1. 打印其内容和
    2. 来评估结果。

  • calc5在 example/qi/compiler_tutorial/calc5.cpp

    与 Calc4 相同,这一次,我们将加入调试支持,以及错误处理和报告。

  • calc6在 example/qi/compiler_tutorial/calc6.cpp

    又一个计算器示例!这一次,我们将编译成一个简单的虚拟机。这实际上是大约 2000 年的第一个 Spirit 示例。现在,它已移植到 Spirit2。

  • calc7在 example/qi/compiler_tutorial/calc7/main.cpp

    现在我们将介绍变量和赋值。这一次,我们也将重命名一些规则——一个更大计划的策略;-)

    这个版本还展示了语法模块化。在这里,您将看到表达式和语句是如何构建为模块化语法的。

  • calc8在 example/qi/compiler_tutorial/calc8/main.cpp

    现在我们将介绍布尔表达式和控制结构。现在我们在做什么很明显吗?;-)

我相信你会在教程结束时找到很多灵感!

于 2014-05-28T18:15:46.483 回答
2

您可以构建自己的反映 AST 的计算器对象树。因此,对于您的示例out1 = 3 * in1 + in2,AST 是:

*
  3
  +
    in1
    in2

因此,您将构建一个像这样的对象层次结构:

Multiplier(
  Constant(3),
  Adder(
    Variable(&in1),
    Variable(&in2)
  )
)

与类类似:

class Result {
  virtual double value() = 0;
};

class Multiplier : public Result {
  Multiplier(Result* lhs, Result* rhs);
  double value() { return _lhs->value() * _rhs->value(); }
}
于 2014-05-28T14:20:28.230 回答