提振精神:: ::齐期望解析器和解析器分组意外的行为(boost::spirit::qi Expec

2019-06-24 01:17发布

我希望有人可以通过我使用的无知闪耀光>>>在精神分析的运营商。

我有一个工作的语法,其中顶层规则如下

test = identifier >> operationRule >> repeat(1,3)[any_string] >> arrow >> any_string >> conditionRule;

它依赖于属性自动分配解析值的融合适应结构(即,元组升压)。

但是,我知道,一旦我们的operationRule匹配,我们必须继续或失败(即我们不希望允许回溯尝试与其他开头的规则identifier )。

test = identifier >> 
           operationRule > repeat(1,3)[any_string] > arrow > any_string > conditionRule;

这将导致一个神秘的编译器错误( 'boost::Container' : use of class template requires template argument list )。 Futz了一下周围和下面的编译:

test = identifier >> 
           (operationRule > repeat(1,3)[any_string] > arrow > any_string > conditionRule);

但属性设置不再工作 - 我的数据结构包含解析后的垃圾。 这可以通过添加动作等进行固定[at_c<0>(_val) = _1]但似乎有点笨重-以及根据升压文档使事情慢。

所以,我的问题是

  1. 是否值得防止反向追踪?
  2. 为什么我需要的分组运算符()
  3. 请问我的最后一个例子上面真的停下来后回溯operationRule匹配(我不怀疑,看来,如果里面的整个分析器(...)失败回溯将被允许)?
  4. 如果回答前一个问题是/否/,我该如何构建允许回溯的规则,如果operation是/不是/匹配,但不允许的,一旦操作/是/匹配回溯?
  5. 为什么分组操作破坏属性语法 - 需要采取行动?

我知道这是一个相当宽泛的问题 - 在正确的方向指向任何暗示将不胜感激!

Answer 1:

  1. 是否值得防止反向追踪?

    绝对。 预防早在一般跟踪是提高性能分析器一个行之有效的方法。

    • 减少使用(负)前瞻的(运营商,运营商 - 和一些运营商)
    • 为了分支(运营商|,操作||,运营商^和一些运营商* / - / +),使得最频繁的/有可能分支排在第一位,或者说,最昂贵的分支试图最后

    使用期望点( > )基本上不减少回溯: 它只是禁止它 。 这将使针对性的错误信息,避免无用的“解析成 - 的未知”。

  2. 为什么我需要的分组operator ()

    我不确定。 我有一个支票使用我what_is_the_attr助手从这里

    • ident >> op >> repeat(1,3)[any] >> "->" >> any
      综合属性:

       fusion::vector4<string, string, vector<string>, string> 
    • ident >> op > repeat(1,3)[any] > "->" > any
      综合属性:

       fusion::vector3<fusion::vector2<string, string>, vector<string>, string> 

    我还没有发现使用圆括号(东西编译) 需要组子表达式,但显然DataT需要进行修改,以符合变化的布局。

     typedef boost::tuple< boost::tuple<std::string, std::string>, std::vector<std::string>, std::string > DataT; 

下面的完整代码显示了如何宁愿做,使用适合结构。

  1. 请问我上面的例子中真止operationRule匹配后回溯(我不怀疑,看来,如果里面的(...)失败回溯将被允许在整个分析器)?

    绝对。 如果期望(或多个)不被满足,一个qi::expectation_failure<>则抛出异常。 这在默认情况下将中止解析。 你可以使用气:: ON_ERROR到retryfailacceptrethrow 。 该MiniXML例如对使用的期望点具有非常好的例子qi::on_error

  2. 如果回答前一个问题是/否/, 我该如何构建允许回溯的规则,如果操作是/不是/匹配,但不允许的,一旦操作/是/匹配回溯?

  3. 为什么分组操作破坏属性语法-需要采取行动?

    它不破坏属性语法,它只是改变了暴露的类型。 所以,如果你绑定一个适当的属性根据规则/语法,你不需要语义动作。 现在,我觉得应该有办法去没有分组,所以让我试试吧(最好是在你的短期自备的样品)。 事实上我发现没有这样的必要 。 我添加了一个完整的例子来帮助你看到正在发生的事情在我的测试,并没有使用语义动作。

完整的代码

完整的代码显示5种情景:

  • 选项1:原始无预期

    (没有相关的变化)

  • 选项2:符合预期

    使用修改的typedef DataT(如上所示)

  • OPTION 3:调整结构,没有预期

    使用具有BOOST_FUSION_ADAPT_STRUCT一个用户定义的结构

  • 方案4:调整结构,符合预期

    从OPTION 3修改适于结构

  • 方案5:先行破解

    这其中利用了“聪明”(?)破解 ,通过使所有>>成期望,以及检测存在operationRule预先-match。 这当然是最理想的,但可以让你保持DataT修改,并且不使用语义动作。

显然,定义OPTION编译之前所需的值。

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted.hpp>
#include <iostream>

namespace qi    = boost::spirit::qi; 
namespace karma = boost::spirit::karma; 

#ifndef OPTION
#define OPTION 5
#endif

#if OPTION == 1 || OPTION == 5 // original without expectations (OR lookahead hack)
    typedef boost::tuple<std::string, std::string, std::vector<std::string>, std::string> DataT;
#elif OPTION == 2 // with expectations
    typedef boost::tuple<boost::tuple<std::string, std::string>, std::vector<std::string>, std::string> DataT;
#elif OPTION == 3 // adapted struct, without expectations
    struct DataT
    {
        std::string identifier, operation;
        std::vector<std::string> values;
        std::string destination;
    };

    BOOST_FUSION_ADAPT_STRUCT(DataT, (std::string, identifier)(std::string, operation)(std::vector<std::string>, values)(std::string, destination));
#elif OPTION == 4 // adapted struct, with expectations
    struct IdOpT
    {
        std::string identifier, operation;
    };
    struct DataT
    {
        IdOpT idop;
        std::vector<std::string> values;
        std::string destination;
    };

    BOOST_FUSION_ADAPT_STRUCT(IdOpT, (std::string, identifier)(std::string, operation));
    BOOST_FUSION_ADAPT_STRUCT(DataT, (IdOpT, idop)(std::vector<std::string>, values)(std::string, destination));
#endif

template <typename Iterator>
struct test_parser : qi::grammar<Iterator, DataT(), qi::space_type, qi::locals<char> >
{
    test_parser() : test_parser::base_type(test, "test")
    {
        using namespace qi;

        quoted_string = 
               omit    [ char_("'\"") [_a =_1] ]             
            >> no_skip [ *(char_ - char_(_a))  ]
             > lit(_a); 

        any_string = quoted_string | +qi::alnum;

        identifier = lexeme [ alnum >> *graph ];

        operationRule = string("add") | "sub";
        arrow = "->";

#if OPTION == 1 || OPTION == 3   // without expectations
        test = identifier >> operationRule >> repeat(1,3)[any_string] >> arrow >> any_string;
#elif OPTION == 2 || OPTION == 4 // with expectations
        test = identifier >> operationRule  > repeat(1,3)[any_string]  > arrow  > any_string;
#elif OPTION == 5                // lookahead hack
        test = &(identifier >> operationRule) > identifier > operationRule > repeat(1,3)[any_string] > arrow > any_string;
#endif
    }

    qi::rule<Iterator, qi::space_type/*, qi::locals<char> */> arrow;
    qi::rule<Iterator, std::string(), qi::space_type/*, qi::locals<char> */> operationRule;
    qi::rule<Iterator, std::string(), qi::space_type/*, qi::locals<char> */> identifier;
    qi::rule<Iterator, std::string(), qi::space_type, qi::locals<char> > quoted_string, any_string;
    qi::rule<Iterator, DataT(),       qi::space_type, qi::locals<char> > test;
};

int main()
{
    std::string str("addx001 add 'str1'   \"str2\"       ->  \"str3\"");
    test_parser<std::string::const_iterator> grammar;
    std::string::const_iterator iter = str.begin();
    std::string::const_iterator end  = str.end();

    DataT data;
    bool r = phrase_parse(iter, end, grammar, qi::space, data);

    if (r)
    {
        using namespace karma;
        std::cout << "OPTION " << OPTION << ": " << str << " --> ";
#if OPTION == 1 || OPTION == 3 || OPTION == 5 // without expectations (OR lookahead hack)
        std::cout << format(delimit[auto_ << auto_ << '[' << auto_ << ']' << " --> " << auto_], data) << "\n";
#elif OPTION == 2 || OPTION == 4 // with expectations
        std::cout << format(delimit[auto_ << '[' << auto_ << ']' << " --> " << auto_], data) << "\n";
#endif
    }
    if (iter!=end)
        std::cout << "Remaining: " << std::string(iter,end) << "\n";
}

所有选项输出:

for a in 1 2 3 4 5; do g++ -DOPTION=$a -I ~/custom/boost/ test.cpp -o test$a && ./test$a; done
OPTION 1: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 2: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 3: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 4: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 5: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 


文章来源: boost::spirit::qi Expectation Parser and parser grouping unexpected behaviour