2013-07-18 6 views
10

zurückgegeben werden Nach mehreren Tutorials (z. B. http://boost-spirit.com/home/articles/qi-example/nabialek-trick/) Ich möchte den Nabialek-Trick verwenden, um einen dynamischen Parser zu haben. Parsing funktioniert schon, aber die Attribute werden nicht transportiert. Erklärungen wie https://stackoverflow.com/a/9109972/2524462 legen nahe, dass Attribute möglich sein sollten, aber keine Argumente.Boost.Spirit.Qi: Wie Attribute mit Nabialek Trick

Dies ist nur ein kleines Beispiel zum Analysieren einer Zeichenfolge und einer Zahl in eine Struktur. Es dient nur dazu, mein Problem zu zeigen; Diese Methode sollte später in einem größeren System verwendet werden, in dem der dynamische Parser wirklich benötigt wird.

Frage: Wie transportiere ich Attribute mit dem Nabialek-Trick?

Ich bin kein Geist Experte, also bitte bitte mit mir. Ich benutze gcc 4.8.1 und boosten 1.54.

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

namespace qi = boost::spirit::qi; 
namespace phx = boost::phoenix; 

//------------------------------------------------------------------------------ 
// Data structure 
struct myline { 
    myline() 
     : _n(0), _s("") { 
    } 

    myline(int n, std::string s) 
     : _n(n), _s(s) { 
    } 

    void set(int n, std::string s) { 
    _n = n; 
    _s = s; 
    } 

    int _n; 
    std::string _s; 
}; 

BOOST_FUSION_ADAPT_STRUCT(::myline, (int, _n) (std::string, _s)) 

//------------------------------------------------------------------------------ 
// Parser grammar 
template<typename It, typename Skipper = qi::space_type> 
struct parser: qi::grammar<It, myline(), Skipper> { 
    parser() 
     : parser::base_type(start) { 
    using namespace qi; 

    start = line; 

    string %= qi::lexeme["'" >> *~qi::char_("'") >> "'"]; 

    one = (string >> "@" >> qi::int_)[_val = phx::construct<myline>(_2, _1)]; 
    two = (qi::int_ >> "@" >> string); 

    keyword.add("one", &one)("two", &two); 

    line = keyword[_a = _1] >> qi::lazy(*_a); 

    on_error<fail>(
     start, 
     std::cout << phx::val("Error! Expecting ") << _4 
     << phx::val(" here: \"") << phx::construct<std::string>(_3, _2) 
     << phx::val("\"\n")); 

    BOOST_SPIRIT_DEBUG_NODES((start)(line)(one)(two)) 
    } 

private: 
    template<typename Attr> using Rule = qi::rule<It, Attr(), Skipper>; 

    Rule<myline> start, one, two; 
    qi::rule<It, myline, Skipper, qi::locals<Rule<myline>*> > line; 

    Rule<std::string> string; 

    qi::symbols<char, Rule<myline>*> keyword; 
}; 

//------------------------------------------------------------------------------ 
int main() { 
    for (const std::string input : std::vector<std::string> { "one 'test'@1", 
                  "two [email protected]'test'" }) { 
    auto f(std::begin(input)), l(std::end(input)); 
    const static parser<decltype(f)> p; 

    myline parsed_script; 
    bool ok = qi::phrase_parse(f, l, p, qi::space, parsed_script); 

    if (!ok) { 
     std::cout << "invalid input\n"; 
    } 

    std::cout << parsed_script._n << ": " << parsed_script._s << std::endl; 

    if (f != l) { 
     std::cout << "unparsed: '" << std::string(f, l) << "'" << std::endl; 
    } 
    } 
} 

Parsing Ergebnis:

<start> 
    <try>one 'test'@1</try> 
    <line> 
    <try>one 'test'@1</try> 
    <one> 
     <try> 'test'@1</try> 
     <success></success> 
     <attributes>[[1, [t, e, s, t]]]</attributes> 
    </one> 
    <success></success> 
    <attributes>[]</attributes><locals>(0x43b0e0)</locals> 
    </line> 
    <success></success> 
    <attributes>[[0, []]]</attributes> 
</start> 
<start> 
    <try>two [email protected]'test'</try> 
    <line> 
    <try>two [email protected]'test'</try> 
    <two> 
     <try> [email protected]'test'</try> 
     <success></success> 
     <attributes>[[2, [t, e, s, t]]]</attributes> 
    </two> 
    <success></success> 
    <attributes>[]</attributes><locals>(0x43b110)</locals> 
    </line> 
    <success></success> 
    <attributes>[[0, []]]</attributes> 
</start> 

Antwort

14

Sie haben viel Aufmerksamkeit in Spirit Klasse zahlen :)

Es gibt eine Reihe von Fragen waren:

  1. das Attribut Erklärung der line Regel war falsch:

    qi::rule<It, myline, Skipper, qi::locals<Rule<myline>*> > line; 
    

    benötigt

    qi::rule<It, myline(), Skipper, qi::locals<Rule<myline>*> > line; 
    
  2. Automatische Attribut Ausbreitung in Gegenwart von semantischen Aktionen gehemmt sein. Eine aktuelle Antwort finden Sie unter: Boost.spirit: parsing number char and string. So müssen Sie explizit Geistes Auto-Regelverhalten engagieren %= mit:

    line = keyword[_a = _1] >> qi::lazy(*_a); 
    

    nees

    // NOTE the %= 
    line %= omit [ keyword[_a = _1] ] >> qi::lazy(*_a); 
    

    Hinweise sein:

    • die %= auf der string Regel gehen kann (keine semantische Aktionen implizieren automatische Attributausbreitung)
    • müssen wir ausdrücklichomit[] das Ergebnis des Keyword-Spiel, weil wir nicht wirklich die Rule<>* zu unserem myline Attribut zuweisen

Hier ist die korrigierte Version:

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

namespace qi = boost::spirit::qi; 
namespace phx = boost::phoenix; 

//------------------------------------------------------------------------------ 
// Data structure 
struct myline { 
    myline() 
     : _n(0), _s("") { 
    } 

    myline(int n, std::string s) 
     : _n(n), _s(s) { 
    } 

    void set(int n, std::string s) { 
    _n = n; 
    _s = s; 
    } 

    int _n; 
    std::string _s; 
}; 

BOOST_FUSION_ADAPT_STRUCT(::myline, (int, _n) (std::string, _s)) 

//------------------------------------------------------------------------------ 
// Parser grammar 
template<typename It, typename Skipper = qi::space_type> 
struct parser: qi::grammar<It, myline(), Skipper> { 
    parser() 
     : parser::base_type(start) { 
    using namespace qi; 

    start = line; 

    string = qi::lexeme["'" >> *~qi::char_("'") >> "'"]; 

    one = (string >> "@" >> qi::int_) [_val   = phx::construct<myline>(_2, _1)]; 
    two = (qi::int_ >> "@" >> string); 

    keyword.add("one", &one)("two", &two); 

    // NOTE the %= 
    line %= omit [ keyword[_a = _1] ] >> qi::lazy(*_a); 

    on_error<fail>(
     start, 
     std::cout << phx::val("Error! Expecting ") << _4 
     << phx::val(" here: \"") << phx::construct<std::string>(_3, _2) 
     << phx::val("\"\n")); 

    BOOST_SPIRIT_DEBUG_NODES((start)(line)(one)(two)) 
    } 

private: 
    template<typename Attr> using Rule = qi::rule<It, Attr(), Skipper>; 

    Rule<myline> start, one, two; 
    qi::rule<It, myline(), Skipper, qi::locals<Rule<myline>* > > line; 

    Rule<std::string> string; 

    qi::symbols<char, Rule<myline>* > keyword; 
}; 

//------------------------------------------------------------------------------ 
int main() { 
    for (const std::string input : std::vector<std::string> { "one 'test1'@1", 
                  "two [email protected]'test2'" }) { 
    auto f(std::begin(input)), l(std::end(input)); 
    const static parser<decltype(f)> p; 

    myline parsed_script; 
    bool ok = qi::phrase_parse(f, l, p, qi::space, parsed_script); 

    if (!ok) { 
     std::cout << "invalid input\n"; 
    } 

    std::cout << parsed_script._n << ": " << parsed_script._s << std::endl; 

    if (f != l) { 
     std::cout << "unparsed: '" << std::string(f, l) << "'" << std::endl; 
    } 
    } 
} 

Drucke:

<start> 
    <try>one 'test1'@1</try> 
    <line> 
    <try>one 'test1'@1</try> 
    <one> 
     <try> 'test1'@1</try> 
     <success></success> 
     <attributes>[[1, [t, e, s, t, 1]]]</attributes> 
    </one> 
    <success></success> 
    <attributes>[[1, [t, e, s, t, 1]]]</attributes><locals>(0x6386c0)</locals> 
    </line> 
    <success></success> 
    <attributes>[[1, [t, e, s, t, 1]]]</attributes> 
</start> 
1: test1 
<start> 
    <try>two [email protected]'test2'</try> 
    <line> 
    <try>two [email protected]'test2'</try> 
    <two> 
     <try> [email protected]'test2'</try> 
     <success></success> 
     <attributes>[[2, [t, e, s, t, 2]]]</attributes> 
    </two> 
    <success></success> 
    <attributes>[[2, [t, e, s, t, 2]]]</attributes><locals>(0x6386f0)</locals> 
    </line> 
    <success></success> 
    <attributes>[[2, [t, e, s, t, 2]]]</attributes> 
</start> 
2: test2 
+1

Siehe das feste Beispiel ** [live auf Coliru] (http://coliru.stacked-crooked.com/view?i d = 339ded0e400164a92e13f5cb65e2e54e-414c51b0c3fcfe921b347f2307ac5c70) ** – sehe

+1

Vielen Dank, funktioniert sehr gut.Scheint, ich brauche noch mehrere Lektionen ;-) –

+0

Du machst es gut, keine Sorgen. – sehe