簡體   English   中英

boost :: spirit :: qi Expectation Parser和parser對意外行為進行分組

[英]boost::spirit::qi Expectation Parser and parser grouping unexpected behaviour

我希望有人能夠通過我在精神解析中使用>>>運算符的無知來發光。

我有一個工作語法,頂級規則看起來像

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

它依賴於屬性來自動將解析后的值分配給融合自適應結構(即boost元組)。

但是,我知道一旦我們匹配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]這樣的動作來修復,但這看起來有點笨拙 - 以及根據boost文檔使事情變慢。

所以,我的問題是

  1. 是否值得阻止反向追蹤?
  2. 為什么我需要分組運算符()
  3. 我的上一個例子在operationRule匹配后是否真的停止了回溯(我懷疑不是,似乎如果(...)內的整個解析器失敗回溯將被允許)?
  4. 如果上一個問題的答案是/ no /,如何構建一個規則,如果operation是/不匹配,則允許回溯,但是一旦操作/匹配,就不允許回溯?
  5. 為什么分組操作符會破壞屬性語法 - 需要操作?

我意識到這是一個相當廣泛的問題 - 任何指向正確方向的提示都將受到高度贊賞!

  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表達式進行分組,但顯然需要修改DataT以匹配更改的布局。

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

下面的完整代碼顯示了我喜歡如何使用改編的結構。

  1. 我的上述示例是否真的在operationRule匹配后停止回溯(我懷疑沒有,似乎如果(...)內的整個解析器失敗回溯將被允許)?

    絕對。 如果未滿足qi::expectation_failure<>則拋出qi::expectation_failure<>異常。 這默認情況下會中止解析。 您可以使用qi :: on_error retryfailacceptrethrow MiniXML示例在使用qi::on_error期望點方面有很好的例子

  2. 如果上一個問題的答案是/ no /, 如何構建一個規則,如果操作是/不匹配,則允許回溯,但是一旦操作/匹配,就不允許回溯?

  3. 為什么分組操作符會破壞屬性語法 - 需要操作?

    它不會破壞屬性語法,只是更改了暴露的類型。 因此,如果將適當的屬性引用綁定到規則/語法,則不需要語義操作。 現在,我覺得應該有沒有分組的方法 ,所以讓我嘗試一下(最好是你的短自我樣本)。 事實上,我沒有找到這樣的需要 我添加了一個完整的示例來幫助您了解我的測試中發生了什么,而不是使用語義操作。

完整代碼

完整代碼顯示了5個場景:

  • 選項1:原始沒有期望

    (無相關變化)

  • 方案2:滿懷期望

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

  • 選項3:適應結構,沒有期望

    將用戶定義的結構與BOOST_FUSION_ADAPT_STRUCT一起使用

  • 選項4:適應結構,有期望

    從OPTION 3修改改編的結構

  • 選項5:前瞻黑客

    這個利用了一個'聰明'(?)hack ,將所有>>變為期望,並事先檢測出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";
}

所有OPTIONS的輸出:

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 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM