【问题标题】:Simple expression with boost::spirit使用 boost::spirit 的简单表达式
【发布时间】:2014-07-24 12:16:58
【问题描述】:

我需要解析simple_expression ::= limit int_number (days | hours | minutes)。我写了语法代码

struct Parser: grammar<std::string::const_iterator, boost::spirit::ascii::space_type>
{
public:
  Parser(ConditionTree& a_lTree):
    Parser::base_type(limit_expression),
    m_lTree(a_lTree)
  {
    using boost::spirit::qi::uint_;

    using boost::spirit::qi::_1;
    using boost::spirit::qi::_2;

    limit_expression = limit_days_operator | limit_hours_operator | limit_minutes_operator ;

    limit_days_operator = ( string("limit") > uint_ > string("days") )[ phoenix::bind( &ConditionTree::AddDaysLimitOperator, m_lTree, _2) ]  ;
    limit_hours_operator = ( string("limit") > uint_ > string("hours") )[ phoenix::bind( &ConditionTree::AddHoursLimitOperator, m_lTree, _2) ]  ;
    limit_minutes_operator = ( string("limit") > uint_ > string("minutes") )[ phoenix::bind( &ConditionTree::AddMinutesLimitOperator, m_lTree, _2) ] ;

    BOOST_SPIRIT_DEBUG_NODE(limit_expression);

    BOOST_SPIRIT_DEBUG_NODE(limit_days_operator);
    BOOST_SPIRIT_DEBUG_NODE(limit_hours_operator);
    BOOST_SPIRIT_DEBUG_NODE(limit_minutes_operator);
  }

  rule<std::string::const_iterator, boost::spirit::ascii::space_type> limit_expression;
  rule<std::string::const_iterator, boost::spirit::ascii::space_type> limit_days_operator;
  rule<std::string::const_iterator, boost::spirit::ascii::space_type> limit_hours_operator;
  rule<std::string::const_iterator, boost::spirit::ascii::space_type> limit_minutes_operator;

  ConditionTree& m_lTree;
}


void main()
{

  ConditionTree oTree;

  Parser parser(oTree);

  std::string strTest("limit5minutes");

  std::string::const_iterator it_begin(strTest.begin());
  std::string::const_iterator it_end(strTest.end());

  bool result = phrase_parse(it_begin, it_end, parser, space);
}

但它无法编译并出现以下 2 个错误: /usr/include/boost/spirit/home/support/argument.hpp:103: ошибка: no matching function for call to 'assertion_failed(mpl_::failed************ (boost::spirit::result_of::get_arg&lt;boost::fusion::vector1&lt;unsigned int&amp;&gt;, 1&gt;::index_is_out_of_bounds::************)())'

/usr/include/boost/spirit/home/phoenix/bind/detail/member_function_ptr.hpp:103: ошибка: invalid initialization of reference of type 'const unsigned int&amp;' from expression of type 'mpl_::void_'

在线 limit_days_operator = ( string("limit") &gt; uint_ &gt; string("days") )[ phoenix::bind( &amp;ConditionTree::AddDaysLimitOperator, m_lTree, _2) ] ;

我试图将语义动作移动到 uint_ :

limit_days_operator = string("limit") > uint_ [ phoenix::bind( &ConditionTree::AddDaysLimitOperator, m_lTree, _1) ] > string("days")  ;
limit_hours_operator = string("limit") > uint_ [ phoenix::bind( &ConditionTree::AddHoursLimitOperator, m_lTree, _1) ] > string("hours")  ;

然后解析器正确读取limit5days,但不正确读取limit5minutes,因为正如我所见,limit5dayslimit5hours 没有区别。

【问题讨论】:

  • void main(),认真的吗? (你必须使用 1990 年代的编译器来编译它)

标签: c++ parsing boost-spirit boost-phoenix


【解决方案1】:

这里发生了很多事情。然而,当我修复一些小东西试图阅读它并使其完整(SSCCE)时,它也编译了:Live On Coliru

我在这里简要说明一些要点:

  • 语义操作中的绑定表达式按值复制m_lTree,这样你就不能改变成员。此处需要phx::ref
  • 期望点导致无法成功解析 "days" 限制以外的任何内容
  • 使用lit,除非您真的想将字符串的值作为属性使用。更好的是,只需写 "limit" &gt;&gt; uint_ &gt;&gt; "days",因为重载会完成剩下的工作

您已经注意到,该语法中有许多反模式,使其变得复杂:

但是:

话虽如此,我将通过 avoiding semantic actions 简化事情,并将解析和评估分开。 只需解析成Limit 结构:

qi::rule<Iterator, ConditionTree::Limit(), Skipper> limit_expression;

limit_expression = "limit" >> qi::uint_ >> unit_;

处理没有单独表达式分支的单元:

unit_.add("days",    ConditionTree::Limit::days)
         ("hours",   ConditionTree::Limit::hours)
         ("minutes", ConditionTree::Limit::minutes);

因为,现在,语法只解析 Limit 对象,您可以在解析后原子地进行评估:

ConditionTree::Limit limit;
if (phrase_parse(iter, end, parser, ascii::space, limit))
{
    AddLimit(oTree, limit);
}

将解析与评估分开使您可以添加诸如

之类的内容
  • 重复计算同一表达式(不重复解析)
  • 在求值前简化表达式树
  • 评估前验证
  • 调试中
  • 更有趣的是,允许您以函数式风格编写ApplyLimit,而不是改变对象:

    ConditionTree ApplyLimit(ConditionTree const& ct, Limit limit) { 
        return ct + limit; // do something here
    }
    
  • 但最重要的是:它极大地简化了语法并为您节省了数小时的时间,而这些时间本来应该花在其他方面更好

看到这个Live On Coliru,输出:

void AddLimit(ConditionTree&, ConditionTree::Limit): 5 minutes

上市

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

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

struct ConditionTree {
    struct Limit {
        unsigned value;
        enum unit_t { days, hours, minutes } unit;
    };

    friend void AddLimit(ConditionTree& ct, Limit limit) {
        std::cout << "AddLimit: " << limit.value;
        switch (limit.unit) {
            case Limit::days:    std::cout << " days\n"; break;
            case Limit::hours:   std::cout << " hours\n"; break;
            case Limit::minutes: std::cout << " minutes\n"; break;
        }
    }
};

BOOST_FUSION_ADAPT_STRUCT(ConditionTree::Limit, (unsigned,value)(ConditionTree::Limit::unit_t,unit))

template <typename Iterator = std::string::const_iterator, typename Skipper = ascii::space_type>
struct Parser: qi::grammar<Iterator, ConditionTree::Limit(), ascii::space_type>
{
    public:
        Parser() : Parser::base_type(limit_expression)
    {
        unit_.add("days", ConditionTree::Limit::days)
                 ("hours", ConditionTree::Limit::hours)
                 ("minutes", ConditionTree::Limit::minutes);

        limit_expression = "limit" >> qi::uint_ >> unit_;
    }

    qi::symbols<char, ConditionTree::Limit::unit_t> unit_;
    qi::rule<Iterator, ConditionTree::Limit(), Skipper> limit_expression;
};

int main()
{
    ConditionTree oTree;

    Parser<> parser;

    std::string strTest("limit5minutes");

    std::string::const_iterator iter(strTest.begin()), end(strTest.end());

    ConditionTree::Limit limit;
    if (phrase_parse(iter, end, parser, ascii::space, limit))
    {
        AddLimit(oTree, limit);
    }
}

【讨论】:

  • @cv_and_he 感谢您注意到编辑,这已经值得了 :)
猜你喜欢
  • 2011-01-21
  • 2014-09-13
  • 2013-05-22
  • 2012-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-28
  • 1970-01-01
相关资源
最近更新 更多