2017-08-14 132 views
2

從升壓精神X3教程:升壓精神X3:解析成結構

首先,讓我們創建一個代表僱員一個結構:

namespace client { namespace ast 
{ 
    struct employee 
    { 
     int age; 
     std::string surname; 
     std::string forename; 
     double salary; 
    }; 
}} 

然後,我們需要告訴Boost.Fusion約我們的員工結構使其成爲語法可以利用的一流融合公民。

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::employee, 
    (int, age) 
    (std::string, surname) 
    (std::string, forename) 
    (double, salary) 
)` 

[...] 在融合的觀點,一個結構僅僅是一個元組的形式。你可以使任何 結構適應完全符合的融合元組。 應用我們上面的摺疊規則,RHS具有以下屬性: fusion::vector<int, std::string, std::string, double> struct employee IS兼容 fusion :: vector。 因此,開始的RHS在其工作時使用開始的屬性(結構員工)原位 。

如果我很好理解,這個邏輯很大程度上依賴於屬性的順序。

現在,我在一個情況下,我需要解析像

Layer "L1" { 
    number = 23 
    color = green 
    visible = true 
} 

成一個結構

struct LayerInfo 
{ 
    std::string layerName; 
    int layerNumber; 
    std::string color; 
    bool visible; 
} 

的問題是,層屬性的順序可以改變,這與上面看到的邏輯是相反的。

哪一種解析這種結構的正確方法是? 我是否需要一定需要使用語義操作?

+1

[我試圖](https://wandbox.org/permlink/xfNV2Gk7ZPeLppK7)使用['融合:: map'方法]解決這個(HTTPS:// github上.com/boostorg/spirit/blob/develop/test/x3/fusion_map.cpp),這似乎是Qi的置換解析器的替代品。不過爲了使它工作,我必須對您的示例進行一些更改:I已經對可以在嵌套結構中重新排序的屬性進行了分組(我認爲這是這種方法所必需的),並且在每個屬性之後都添加了一個';'(這絕對不是必需的,但是簡化了哪個隊長的使用)。如果你有興趣,我可以把它作爲一個答案。 – llonesmiz

+0

@llonesmiz肯定!謝謝 – Filippo

+1

@llonesmiz非常順利。我試圖用更多動力來對抗事物。現在這兩種方法都有弱點,國際海事組織。 – sehe

回答

1

我愛@llonesmiz在評論中的做法。儘管如此,我仍然不得不用X3嘗試使用我最喜歡的方法來使用功能組合。下面是解析和傳播值的方法的草圖。

缺少對財產存在/唯一性的檢查。 (我認爲使用基本上包含std::set<V T::*>x3::with<>上下文添加可以實現這樣的事情。當然,這樣的事情需要(依賴於實現嗎?)強制轉換或擦除包裝。

現在,沒有註釋:

Live On Coliru

#include <iostream> 
//#define BOOST_SPIRIT_X3_DEBUG 
#include <boost/spirit/home/x3.hpp> 
#include <boost/fusion/include/adapt_struct.hpp> 
#include <boost/fusion/include/io.hpp> 

struct LayerInfo 
{ 
    std::string layerName; 
    int layerNumber = 0; 
    std::string color; 
    bool visible = false; 
}; 

namespace Parser { 
    namespace x3 = boost::spirit::x3; 

    // custom type parsers 
    auto quoted = rule<std::string>("quoted", x3::lexeme [ '"' >> *('\\' >> x3::char_ | ~x3::char_('"')) >> '"' ]); 
    struct colors_type : x3::symbols<char> { 
     colors_type() { 
      this->add("red")("blue")("green")("black"); 
     } 
    } static const colors; 

    namespace detail { 
     template <typename T> auto propagate(T member) { 
      return [=](auto& ctx){ x3::traits::move_to(x3::_attr(ctx), x3::_val(ctx).*member); }; 
     } 

     template <typename T> auto make_member_parser(int T::* const member) { return x3::int_ [propagate(member)]; } 
     template <typename T> auto make_member_parser(bool T::* const member) { return x3::bool_ [propagate(member)]; } 
     template <typename T> auto make_member_parser(std::string T::* const member) { return x3::raw[colors] [propagate(member)]; } 

     template <typename T = LayerInfo, typename P> 
      auto rule(const char* debug, P p) { return x3::rule<struct _, T> {debug} = x3::skip(x3::space)[p]; }; 

     auto property = [](auto label, auto member) { 
      return rule(label, x3::as_parser(label) >> '=' >> make_member_parser(member)); 
     }; 
    } 

    using detail::rule; 
    using detail::propagate; 
    using detail::property; 

    auto name  = rule("name", "Layer" >> quoted [propagate(&LayerInfo::layerName)]); 

    auto number  = property("number", &LayerInfo::layerNumber); 
    auto color  = property("color", &LayerInfo::color); 
    auto visible = property("visible", &LayerInfo::visible); 

    auto layer_info = name >> '{' >> +(number | color | visible) >> '}'; 

    auto grammar = rule("layer_info", layer_info); 
} 

std::ostream& operator<<(std::ostream& os, LayerInfo const& li) { 
    return os << "LayerInfo \"" << li.layerName << "\"{" 
     << "number=" << li.layerNumber << " " 
     << "color=" << li.color   << " " 
     << "visible=" << std::boolalpha << li.visible 
     << "}\n"; 
} 

int main() { 
    std::string const sample = R"(Layer "L1" { 
    number = 23 
    color = green 
    visible = true 
})"; 

    LayerInfo v; 
    auto f = sample.begin(), l = sample.end(); 
    bool ok = parse(f, l, Parser::grammar, v); 


    if (ok) 
     std::cout << "Parsed: " << v << "\n"; 
    else 
     std::cout << "Parse failed\n"; 

    if (f!=l) 
     std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n"; 
} 

打印

Parsed: LayerInfo "L1"{number=23 color=green visible=true} 

機智調試信息:Live On Coliru

<layer_info> 
    <try>Layer "L1" {\n num</try> 
    <name> 
    <try>Layer "L1" {\n num</try> 
    <quoted> 
     <try> "L1" {\n number =</try> 
     <success> {\n number = 23\n </success> 
     <attributes>[L, 1]</attributes> 
    </quoted> 
    <success> {\n number = 23\n </success> 
    <attributes>LayerInfo "L1"{number=0 color= visible=false} 
</attributes> 
    </name> 
    <number> 
    <try>\n number = 23\n </try> 
    <success>\n color = green\n </success> 
    <attributes>LayerInfo "L1"{number=23 color= visible=false} 
</attributes> 
    </number> 
    <number> 
    <try>\n color = green\n </try> 
    <fail/> 
    </number> 
    <color> 
    <try>\n color = green\n </try> 
    <success>\n visible = true\n</success> 
    <attributes>LayerInfo "L1"{number=23 color=green visible=false} 
</attributes> 
    </color> 
    <number> 
    <try>\n visible = true\n</try> 
    <fail/> 
    </number> 
    <color> 
    <try>\n visible = true\n</try> 
    <fail/> 
    </color> 
    <visible> 
    <try>\n visible = true\n</try> 
    <success>\n}</success> 
    <attributes>LayerInfo "L1"{number=23 color=green visible=true} 
</attributes> 
    </visible> 
    <number> 
    <try>\n}</try> 
    <fail/> 
    </number> 
    <color> 
    <try>\n}</try> 
    <fail/> 
    </color> 
    <visible> 
    <try>\n}</try> 
    <fail/> 
    </visible> 
    <success></success> 
    <attributes>LayerInfo "L1"{number=23 color=green visible=true} 
</attributes> 
</layer_info> 
+0

我想編譯你在VS2017上提供的確切代碼,我得到一個「屬性沒有預期的大小」的錯誤。在Coliru上,一切都很好,代碼編譯完成。可能是這個錯誤的原因是什麼? – Filippo

+0

編譯器 - 它不在Spirit X3,AFAIK支持的集合中。 X3仍然是實驗性的 – sehe