如何使用 boost::spirit 解析数学表达式并将其绑定到函数
How to parse a mathematical expression with boost::spirit and bind it to a function
我想定义一个有两个参数的函数
double func(double t, double x);
实际实现是从外部文本文件中读取的。
例如,在文本文件中指定
function = x*t;
该函数应该实现x
和t
之间的乘法,以便稍后调用它。
我正在尝试使用 boost::spirit 解析该函数。但我不知道如何真正实现它。
下面,我创建了一个实现乘法的简单函数。我将它绑定到一个提升功能,我可以使用它。我还创建了一个简单的语法,它解析两个双打之间的乘法。
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include "boost/function.hpp"
#include "boost/bind.hpp"
#include <boost/spirit/include/qi_symbols.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
namespace ascii=boost::spirit::ascii;
using boost::spirit::ascii::space;
using boost::spirit::qi::symbols;
template< typename Iterator >
struct MyGrammar : public virtual qi::grammar< Iterator, ascii::space_type >
{
MyGrammar() : MyGrammar::base_type(expression)
{
using qi::double_;
//The expression should take x and t as symbolic expressions
expression = (double_ >> '*' >> double_)[std::cout << "Parse multiplication: " << (qi::_1 * qi::_2)];
}
qi::rule<Iterator, ascii::space_type> expression;
};
double func(const double a, const double b)
{
return a*b; //This is the operation to perform
}
int main()
{
typedef std::string::const_iterator iterator_Type;
typedef MyGrammar<iterator_Type> grammar_Type;
grammar_Type calc;
std::string str = "1.*2."; // This should be changed to x*t
iterator_Type iter = str.begin();
iterator_Type end = str.end();
bool r = phrase_parse(iter, end, calc, space);
typedef boost::function < double ( const double t,
const double x) > function_Type;
function_Type multiplication = boost::bind(&func, _1, _2);
std::cout << "\nResult: " << multiplication( 2.0, 3.0) << std::endl;
return 0;
}
如果我修改上面的代码设置
std::string str = "x*t";
我如何解析这样的表达式并将其绑定到函数 multiplication
,这样,如果我调用 multiplication(1.0, 2.0)
,它将 t 关联到 1.0,x 关联到 2.0,并且它 returns 运算结果?
你将学习精神。太棒了!
不过,看来你在这里贪得无厌。
首先,您的语法实际上还没有解析表达式。而且它肯定不会导致您可以绑定的功能。
事实上,您正在使用不产生任何结果的语法来解析输入。它只会产生一个 side-effect(这是将带有简单操作数的简单二进制表达式的结果打印到控制台)。这种 / 类似于 / 解释性语言,尽管它很快就会在
时分崩离析
- 您尝试解析像
2*8 + 9
这样的表达式
- 您的输入会回溯(糟糕,副作用已经触发)
接下来你要绑定 func
(顺便说一句,这是多余的;你没有绑定任何参数,所以你可以在这里说 function_Type multiplication(func);
),并且调用它。虽然很酷,但这实际上与解析输入无关。
最后,你的问题是关于第三个的问题,上面的任何地方都没有涉及。这个问题是关于 symbol tables 和标识符查找的。
- 这意味着您应该解析实际标识符的来源(
x
或 t
,例如)
- 你需要将它们存储到一个符号中 table 以便它们可以映射到一个值(也许是 scope/lifetime)
问题中存在逻辑漏洞,您没有定义 "formal parameter list" 的来源(您在此处的文本中提到了它:function = x*t;
但解析器不处理它,您也没有硬编码任何此类元数据);所以我们甚至无法开始将 x
和 t
映射到正式参数列表(因为它不存在)。
Let's assume for the moment that in fact arguments are positional (as they are, and you seem to want this as you call the bound function with positional arguments anyways. (So we don't have to worry about a name because no one will ever see a name.)
调用者应将上下文传递给函数,以便在评估期间可以通过标识符名称查找值。
所以,虽然我可以尝试让您坐下来,并在您甚至梦想以您所要求的奇妙方式将其粘合在一起之前先了解所有需要创建的细节和螺栓,但让我们不要.
这会占用我太多时间,你可能会不知所措。
建议
我只能建议查看更简单的资源。从教程开始
- 计算器系列教程不错。在这个答案中,我列出了计算器示例,并简要描述了它们展示的技术:What is the most efficient way to recalculate attributes of a Boost Spirit parse with a different symbol table?
- 编译器教程实际上可以满足您的所有需求,但它们有点高级
Ask freely if you have any questions along the way and you're at risk of getting stuck. But at least then we have a question that is answerable and answers that genuinely help you.
现在,看看我的其他一些答案,我在其中实际实现了这样的语法(按照复杂性的增加排序):
很适合比较:这个对 Boost::spirit how to parse and call c++ function-like expressions 的回答即时解释了解析的表达式(这模仿了您自己的解析器中 [std::cout << "Parse multiplication: " << (qi::_1 * qi::_2)]
的方法)
那里的另一个答案 (Boost::spirit how to parse and call c++ function-like expressions) 实现了目标,但使用专用的 AST 表示和单独的解释阶段。
这些答案中描述了每种方法的好处。这些解析器 没有 符号 table 也没有求值上下文。
更多示例:
- 一个简单的布尔表达式语法评估器(仅支持文字,不支持变量)
- Building a Custom Expression Tree in Spirit:Qi (Without Utree or Boost::Variant)
我想定义一个有两个参数的函数
double func(double t, double x);
实际实现是从外部文本文件中读取的。
例如,在文本文件中指定
function = x*t;
该函数应该实现x
和t
之间的乘法,以便稍后调用它。
我正在尝试使用 boost::spirit 解析该函数。但我不知道如何真正实现它。
下面,我创建了一个实现乘法的简单函数。我将它绑定到一个提升功能,我可以使用它。我还创建了一个简单的语法,它解析两个双打之间的乘法。
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include "boost/function.hpp"
#include "boost/bind.hpp"
#include <boost/spirit/include/qi_symbols.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
namespace ascii=boost::spirit::ascii;
using boost::spirit::ascii::space;
using boost::spirit::qi::symbols;
template< typename Iterator >
struct MyGrammar : public virtual qi::grammar< Iterator, ascii::space_type >
{
MyGrammar() : MyGrammar::base_type(expression)
{
using qi::double_;
//The expression should take x and t as symbolic expressions
expression = (double_ >> '*' >> double_)[std::cout << "Parse multiplication: " << (qi::_1 * qi::_2)];
}
qi::rule<Iterator, ascii::space_type> expression;
};
double func(const double a, const double b)
{
return a*b; //This is the operation to perform
}
int main()
{
typedef std::string::const_iterator iterator_Type;
typedef MyGrammar<iterator_Type> grammar_Type;
grammar_Type calc;
std::string str = "1.*2."; // This should be changed to x*t
iterator_Type iter = str.begin();
iterator_Type end = str.end();
bool r = phrase_parse(iter, end, calc, space);
typedef boost::function < double ( const double t,
const double x) > function_Type;
function_Type multiplication = boost::bind(&func, _1, _2);
std::cout << "\nResult: " << multiplication( 2.0, 3.0) << std::endl;
return 0;
}
如果我修改上面的代码设置
std::string str = "x*t";
我如何解析这样的表达式并将其绑定到函数 multiplication
,这样,如果我调用 multiplication(1.0, 2.0)
,它将 t 关联到 1.0,x 关联到 2.0,并且它 returns 运算结果?
你将学习精神。太棒了!
不过,看来你在这里贪得无厌。
首先,您的语法实际上还没有解析表达式。而且它肯定不会导致您可以绑定的功能。
事实上,您正在使用不产生任何结果的语法来解析输入。它只会产生一个 side-effect(这是将带有简单操作数的简单二进制表达式的结果打印到控制台)。这种 / 类似于 / 解释性语言,尽管它很快就会在
时分崩离析- 您尝试解析像
2*8 + 9
这样的表达式
- 您的输入会回溯(糟糕,副作用已经触发)
- 您尝试解析像
接下来你要绑定
func
(顺便说一句,这是多余的;你没有绑定任何参数,所以你可以在这里说function_Type multiplication(func);
),并且调用它。虽然很酷,但这实际上与解析输入无关。最后,你的问题是关于第三个的问题,上面的任何地方都没有涉及。这个问题是关于 symbol tables 和标识符查找的。
- 这意味着您应该解析实际标识符的来源(
x
或t
,例如) - 你需要将它们存储到一个符号中 table 以便它们可以映射到一个值(也许是 scope/lifetime)
问题中存在逻辑漏洞,您没有定义 "formal parameter list" 的来源(您在此处的文本中提到了它:
function = x*t;
但解析器不处理它,您也没有硬编码任何此类元数据);所以我们甚至无法开始将x
和t
映射到正式参数列表(因为它不存在)。Let's assume for the moment that in fact arguments are positional (as they are, and you seem to want this as you call the bound function with positional arguments anyways. (So we don't have to worry about a name because no one will ever see a name.)
调用者应将上下文传递给函数,以便在评估期间可以通过标识符名称查找值。
- 这意味着您应该解析实际标识符的来源(
所以,虽然我可以尝试让您坐下来,并在您甚至梦想以您所要求的奇妙方式将其粘合在一起之前先了解所有需要创建的细节和螺栓,但让我们不要.
这会占用我太多时间,你可能会不知所措。
建议
我只能建议查看更简单的资源。从教程开始
- 计算器系列教程不错。在这个答案中,我列出了计算器示例,并简要描述了它们展示的技术:What is the most efficient way to recalculate attributes of a Boost Spirit parse with a different symbol table?
- 编译器教程实际上可以满足您的所有需求,但它们有点高级
Ask freely if you have any questions along the way and you're at risk of getting stuck. But at least then we have a question that is answerable and answers that genuinely help you.
现在,看看我的其他一些答案,我在其中实际实现了这样的语法(按照复杂性的增加排序):
很适合比较:这个对 Boost::spirit how to parse and call c++ function-like expressions 的回答即时解释了解析的表达式(这模仿了您自己的解析器中
[std::cout << "Parse multiplication: " << (qi::_1 * qi::_2)]
的方法)那里的另一个答案 (Boost::spirit how to parse and call c++ function-like expressions) 实现了目标,但使用专用的 AST 表示和单独的解释阶段。
这些答案中描述了每种方法的好处。这些解析器 没有 符号 table 也没有求值上下文。
更多示例:
- 一个简单的布尔表达式语法评估器(仅支持文字,不支持变量)
- Building a Custom Expression Tree in Spirit:Qi (Without Utree or Boost::Variant)