2013-07-18 10 views
10

Po kilku samouczkach (np. http://boost-spirit.com/home/articles/qi-example/nabialek-trick/) chcę użyć sztuczki Nabialka do utworzenia parsera dynamicznego. Przetwarzanie już działa dobrze, ale nie dostaję atrybutów przetransportowanych. Objaśnienia takie jak https://stackoverflow.com/a/9109972/2524462 sugerują, że atrybuty powinny być możliwe, ale nie argumentami.Boost.Spirit.Qi: Jak zwracać atrybuty za pomocą podstępu Nabialka

To tylko mały przykład parsowania ciągu i liczby w strukturze. To tylko po to, by pokazać mój problem; ta metoda powinna być później zastosowana w większym systemie, gdzie parser dynamiczny jest naprawdę potrzebny.

Pytanie: Jak przetransportować atrybuty sztuczką Nabialka?

Nie jestem ekspertem od ducha, więc proszę o zachowanie mnie. Używam gcc 4.8.1 i zwiększam 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; 
    } 
    } 
} 

Przetwarzanie wynik:

<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> 

Odpowiedz

14

Państwo płacą dużo uwagi w klasie Spirit :)

Było kilka kwestii:

  1. atrybut deklaracja reguły line była błędna:

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

    musi być

    qi::rule<It, myline(), Skipper, qi::locals<Rule<myline>*> > line; 
    
  2. automatyczna propagacja atrybut jest hamowana w obecności działań semantycznych. Zobacz ostatnią odpowiedź, aby uzyskać więcej informacji: Boost.spirit: parsing number char and string. Więc trzeba jawnie angażować zachowanie auto-rule Ducha użyciu %=:

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

    Nees być

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

    Uwagi:

    • %= może iść na zasadzie string (nie semantyczny działania wymagają automatycznej propagacji atrybutów)
    • potrzebujemy do jawnieomit[] wynikiem meczu słów kluczowych, ponieważ tak naprawdę nie możemy przypisać Rule<>* do naszego myline atrybutu

oto stała wersja:

#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; 
    } 
    } 
} 

Wydruki:

<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

Zobacz próbkę próbną ** [na żywo na Coliru] (http://coliru.stacked-crooked.com/view?i d = 339ded0e400164a92e13f5cb65e2e54e-414c51b0c3fcfe921b347f2307ac5c70) ** – sehe

+1

Dziękuję bardzo, działa bardzo dobrze.Wydaje mi się, że potrzebuję jeszcze kilku lekcji ;-) –

+0

Dobrze ci idzie, nie martw się. – sehe

Powiązane problemy