gpt4 book ai didi

c++ - 从语义 Action 更新综合属性值

转载 作者:太空狗 更新时间:2023-10-29 20:42:25 25 4
gpt4 key购买 nike

我想了解 boost::spirit::qi 的幕后究竟发生了什么。假设我们有一个简单的解析器来解析和计算由数字和加/减操作组成的表达式:

int main()
{
std::string INPUT_DATA = "12e-1 + 3.4 - .67";
typedef std::string::iterator iterator_type;
iterator_type begin = std::begin(INPUT_DATA);
iterator_type end = std::end(INPUT_DATA);

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::qi::ascii;

auto parser = qi::double_[qi::_val = qi::_1] // (1)
>> *(
(qi::lit('+') >> qi::double_[qi::_val += qi::_1]) // (2)
|
(qi::lit('-') >> qi::double_[qi::_val -= qi::_1]) // (3)
);

double result;
bool ok = qi::phrase_parse(begin, end, parser, ascii::space, result);

if ( ok && begin == end)
{
std::cout << "parsed, result = " << result << std::endl;
}
else
{
std::cout << "not parsed" << std::endl;
}

return 0;
}

qi::_val 为什么会出现在 (1)(2)(3) 引用相同的值?如何在不使用 boost::phoenix 的情况下获得相同的结果?

我想我必须编写一堆仿函数来接收来自 qi::double_ 的解析值,但我应该用它做什么?如何访问解析器的合成值?

最佳答案

除了评论中提供的精美低级信息外,让我向您展示Spirit的方式。

当然,我将以不使用语义操作的演示结束。是的,它涉及更多代码,但它也将解析评估 分离。这在更复杂的情况下很有用(想想回溯的解析器)。

1.

从稍微简化您的代码开始:step 1

auto parser = 
double_ [_val = _1] // (1)
>> *( (lit('+') >> double_[_val += _1]) // (2)
| (lit('-') >> double_[_val -= _1]) // (3)
);

2.

当然,您可以使用常规绑定(bind)函数:step 2

void add_operand(double& lhs, double rhs) { lhs += rhs; }
void sub_operand(double& lhs, double rhs) { lhs -= rhs; }

auto parser =
double_ [_val = _1]
>> *( (lit('+') >> double_[bind(add_operand, _val, _1)])
| (lit('-') >> double_[bind(sub_operand, _val, _1)])
);

3.

现在,使用 BOOST_PHOENIX_ADAPT_FUNCTION 使它稍微漂亮一些:step 3

BOOST_PHOENIX_ADAPT_FUNCTION(void, add_, add_operand, 2)
BOOST_PHOENIX_ADAPT_FUNCTION(void, sub_, sub_operand, 2)

double_ [_val = _1]
>> *( (lit('+') >> double_[add_(_val, _1)])
| (lit('-') >> double_[sub_(_val, _1)])
);

4.

或者您可以使用仿函数:step 4

struct add_operand { 
template<typename...> struct result { typedef void type; };
template<typename L, typename R>
void operator()(L& lhs, R rhs) const { lhs += rhs; }
};

struct sub_operand {
template<typename...> struct result { typedef void type; };
template<typename L, typename R>
void operator()(L& lhs, R rhs) const { lhs -= rhs; }
};

auto parser =
double_ [_val = _1]
>> *( (lit('+') >> double_[bind(add_operand(), _val, _1)])
| (lit('-') >> double_[bind(sub_operand(), _val, _1)])
);

哎呀,漂亮到此为止。


5.

但是,不用担心,您也可以调整这些:step 5

phx::function<add_operand> add_;
phx::function<sub_operand> sub_;

auto parser =
double_ [_val = _1]
>> *( (lit('+') >> double_[add_(_val, _1)])
| (lit('-') >> double_[sub_(_val, _1)])
);

最后:去Pro

最后,您可以通过使用简单的 AST 完全不需要任何语义操作来完成此操作:

rule<iterator_type, term<add>()     , ascii::space_type> add_term;
rule<iterator_type, term<subtract>(), ascii::space_type> sub_term;
rule<iterator_type, expression() , ascii::space_type> parser;

add_term = '+' >> double_;
sub_term = '-' >> double_;
parser = double_ >> *(add_term|sub_term);

现在我们解析成一个表达式 AST:

expression result;
ok = phrase_parse(begin, end, parser, ascii::space, result);

然后我们使用 eval 函数打印结果:

std::cout << "parsed, result = " << eval(result) << std::endl;

它是如何工作的?自己看看:

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>

/////////////////
// AST
template <typename> struct term {
term(double value=0) : value(value) {}
double value;
};

using operation = boost::variant<term<struct add>, term<struct subtract> >;

struct expression
{
double initial;
std::vector<operation> operations;
};

BOOST_FUSION_ADAPT_STRUCT(expression, (double, initial)(std::vector<operation>,operations))
// End of AST
/////////////////

double eval(expression const& e)
{
double result = e.initial;

struct visitor : boost::static_visitor<> {
double& _v; visitor(double& ref) : _v(ref) {}
void operator()(term<add> const& rhs) const { _v += rhs.value; }
void operator()(term<subtract> const& rhs) const { _v -= rhs.value; }
};

for(auto& o : e.operations)
boost::apply_visitor(visitor(result), o);
return result;
}

int main()
{
const std::string INPUT_DATA = "12e-1 + 3.4 - .67";
typedef std::string::const_iterator iterator_type;
iterator_type begin = std::begin(INPUT_DATA);
iterator_type end = std::end(INPUT_DATA);

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::qi::ascii;

bool ok;
expression result;
{
using namespace qi;

rule<iterator_type, term<add>() , ascii::space_type> add_term;
rule<iterator_type, term<subtract>(), ascii::space_type> sub_term;
rule<iterator_type, expression() , ascii::space_type> parser;

add_term = '+' >> double_;
sub_term = '-' >> double_;
parser = double_ >> *(add_term|sub_term);

ok = phrase_parse(begin, end, parser, ascii::space, result);
}

if (ok && begin == end)
std::cout << "parsed, result = " << eval(result) << std::endl;
else
std::cout << "not parsed" << std::endl;
}

关于c++ - 从语义 Action 更新综合属性值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18585255/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com