2013-10-05 22 views
5

Edit: Ho strappato il lexer in quanto non in modo pulito integra con Qi e solo offusca grammatiche (vedi here).spinta spirito parser lex-> qi: Ottenere il meccanismo on_success "senza documenti" per lavorare


on_success non è ben documentata e sto provando a filo fino alla mia parser. Gli esempi che si occupano di on_success riguardano i parser appena creati su qi --i.e., No lex.

questo è come mi sto cercando di introdurre il costrutto:

using namespace qi::labels; 
qi::on_success(event_entry_,std::cout << _val << _1); 

Ma non si compila. Sto temendo che il problema sia lex. Qualcuno potrebbe dirmi cosa sto facendo male e in secondo luogo dirmi cosa sono tutti i segnaposto disponibili, lì tipo e cosa rappresentano (dal momento che non sono documentati).

Il file completo è il seguente:

#include <boost/spirit/include/phoenix_core.hpp> 
#include <boost/spirit/home/phoenix/bind/bind_member_variable.hpp> 
#include <boost/spirit/include/lex_lexertl.hpp> 
#include <boost/spirit/include/qi.hpp> 
#include <boost/none.hpp> 
#include <boost/cstdint.hpp> 
#include <boost/fusion/include/adapt_struct.hpp> 
#include <string> 
#include <exception> 
#include <vector> 

namespace lex = boost::spirit::lex; 
namespace px = boost::phoenix; 
namespace qi = boost::spirit::qi; 
namespace ascii = boost::spirit::ascii; 


template <typename Lexer> 
struct tokens : lex::lexer<Lexer> 
{ 
    tokens() 
     : left_curly("\"{\""), 
     right_curly("\"}\""), 
     left_paren("\"(\""), 
     right_paren("\")\""), 
     colon(":"), 
     scolon(";"), 
     namespace_("(?i:namespace)"), 
     event("(?i:event)"), 
     optional("(?i:optional)"), 
     required("(?i:required)"), 
     repeated("(?i:repeated)"), 
     t_int_4("(?i:int4)"), 
     t_int_8("(?i:int8)"), 
     t_string("(?i:string)"), 
     ordinal("\\d+"), 
     identifier("\\w+") 

    { 
     using boost::spirit::lex::_val; 

     this->self 
      = 
       left_curly [ std::cout << px::val("lpar") << std::endl] 
      | right_curly [ std::cout << px::val("rpar") << std::endl] 
      | left_paren 
      | right_paren 
      | colon    [ std::cout << px::val("colon") << std::endl] 
      | scolon 
      | namespace_   [ std::cout << px::val("kw namesapce") << std::endl] 
      | event    [ std::cout << px::val("kw event") << std::endl] 
      | optional   [ std::cout << px::val("optional ") << "-->" << _val << "<--" << std::endl] 
      | required   [ std::cout << px::val("required") << std::endl] 
      | repeated 
      | t_int_4 
      | t_int_8 
      | t_string 
      | ordinal    [ std::cout << px::val("val ordinal (") << _val << ")" << std::endl] 
      | identifier   [std::cout << px::val("val identifier(") << _val << ")" << std::endl]; 


     this->self("WS") = lex::token_def<>("[ \\t\\n]+"); 
    } 


    lex::token_def<lex::omit> left_curly, right_curly, colon, scolon,repeated, left_paren, right_paren; 
    lex::token_def<lex::omit> namespace_, event, optional, required,t_int_4, t_int_8, t_string; 
    lex::token_def<boost::uint32_t> ordinal; 
    lex::token_def<> identifier; 
}; 

enum event_entry_qualifier 
{ 
    ENTRY_OPTIONAL, 
    ENTRY_REQUIRED, 
    ENTRY_REPEATED 
}; 

enum entry_type 
{ 
    RBL_INT4, 
    RBL_INT8, 
    RBL_STRING, 
    RBL_EVENT 
}; 

struct oid 
{ 
    boost::uint32_t ordinal; 
    std::string  name; 
}; 

BOOST_FUSION_ADAPT_STRUCT 
(
    oid, 
    (boost::uint32_t, ordinal) 
    (std::string, name) 
) 

struct type_descriptor 
{ 
    entry_type type_id; 
    std::string referenced_event; 
}; 

BOOST_FUSION_ADAPT_STRUCT 
(
    type_descriptor, 
    (entry_type, type_id) 
    (std::string, referenced_event) 
) 

struct event_entry 
{ 
    event_entry_qualifier qualifier; 
    oid     identifier; 
    type_descriptor  descriptor; 
}; 

BOOST_FUSION_ADAPT_STRUCT 
(
    event_entry, 
    (event_entry_qualifier, qualifier) 
    (oid, identifier) 
    (type_descriptor, descriptor) 
) 

struct event_descriptor 
{ 
    oid      identifier; 
    std::vector<event_entry> event_entries; 
}; 

BOOST_FUSION_ADAPT_STRUCT 
(
    event_descriptor, 
    (oid, identifier) 
    (std::vector<event_entry>, event_entries) 
) 

template <typename Iterator, typename Lexer> 
struct grammar : qi::grammar<Iterator,event_descriptor(), qi::in_state_skipper<Lexer> > 
{ 
    template <typename TokenDef> 
    grammar(TokenDef const& tok) 
     : grammar::base_type(event_descriptor_) 
    { 
     using qi::_val; 
     //start = event; 
     event_descriptor_ = tok.event >> oid_ >> tok.left_curly >> *(event_entry_) >> tok.right_curly; 

     event_entry_ = event_qualifier >> oid_ >> type_descriptor_ >> tok.scolon; 

     event_qualifier = tok.optional [ _val = ENTRY_OPTIONAL] 
         | tok.required [ _val = ENTRY_REQUIRED] 
         | tok.repeated [ _val = ENTRY_REPEATED]; 

     oid_ = tok.ordinal 
      >> tok.colon 
      >> tok.identifier; 

     type_descriptor_ 
      = ((atomic_type >> qi::attr("")) 
      | (event_type >> tok.left_paren >> tok.identifier >> tok.right_paren)); 

     atomic_type = tok.t_int_4   [ _val = RBL_INT4] 
       | tok.t_int_8    [ _val = RBL_INT8] 
       | tok.t_string   [ _val = RBL_STRING]; 

     event_type = tok.event   [_val = RBL_EVENT]; 

     using namespace qi::labels; 
     qi::on_success(event_entry_,std::cout << _val << _1); 
    } 

    qi::rule<Iterator> start; 
    qi::rule<Iterator, event_descriptor(), qi::in_state_skipper<Lexer> > event_descriptor_; 
    qi::rule<Iterator, event_entry(), qi::in_state_skipper<Lexer> > event_entry_; 
    qi::rule<Iterator, event_entry_qualifier()> event_qualifier; 
    qi::rule<Iterator, entry_type()> atomic_type; 
    qi::rule<Iterator, entry_type()> event_type; 
    qi::rule<Iterator, type_descriptor(),qi::in_state_skipper<Lexer> > type_descriptor_; 
    qi::rule<Iterator, oid()> oid_; 


}; 

std::string test = " EVENT 1:sihan { OPTIONAL 123:hassan int4; OPTIONAL 123:hassan int4; } "; 

int main() 
{ 
    typedef lex::lexertl::token<std::string::iterator, boost::mpl::vector<boost::uint32_t, std::string> > token_type; 
    typedef lex::lexertl::actor_lexer<token_type> lexer_type; 
    typedef tokens<lexer_type>::iterator_type iterator_type; 

    tokens<lexer_type> token_lexer; 
    grammar<iterator_type,tokens<lexer_type>::lexer_def> grammar(token_lexer); 

    std::string::iterator it = test.begin(); 
    iterator_type first = token_lexer.begin(it, test.end()); 
    iterator_type last = token_lexer.end(); 

    bool r; 

    r = qi::phrase_parse(first, last, grammar, qi::in_state("WS")[token_lexer.self]); 

    if(r) 
     ; 
    else 
    { 
     std::cout << "parsing failed" << std::endl; 
    } 
} 
+1

posso fornire una risposta parziale. '_1',' _2', e '_3' sono iteratori (' _1' è la posizione iniziale della regola e '_3' è la fine, non ho mai inchiodato cosa fosse' _2'.) Per me, non avevo lexer e a stava iterando su un vecchio 'std :: string', quindi gli iteratori erano facili da ispezionare e usare. Non so come funzionano gli iteratori lex. Ovviamente, '_val' è il valore appena analizzato, che puoi anche modificare. (Tutti i miei nodi AST derivavano da una classe base contenente informazioni sorgente, quindi la mia funzione 'on_success' ha appena copiato le posizioni di '_1' e' _3' in '_val' per la segnalazione degli errori.) – GManNickG

+2

Il tuo errore è probabilmente dovuto al fatto che sei cercando di inserire un iteratore su un ostream, che probabilmente non funzionerà. Posso spostare questi commenti in una risposta se lo si desidera, insieme a una funzione generale 'successful_handler' che verrà chiamata, ma come accennato si è da soli per capire gli iteratori da un lexer. :) – GManNickG

+0

Per quanto riguarda i miei test, on_error/on_success non vuole lavorare con il tuo campione ... Mmm. – sehe

risposta

4

Guardando i file di intestazione Credo che il significato dei segnaposto è:

_1 = Iterator position when the rule was tried. 
_2 = Iterator to the end of the input. 
_3 = Iterator position right after the rule has been successfully matched. 

(Dal momento che non sono sicuro che le righe sopra sono comprensibili, ecco un piccolo esempio con il tuo input)

        rule being tried 
         _________________________________ 
         ´         ` 
[EVENT][1][:][sihan][{][OPTIONAL][123][:][hassan][int4][;][OPTIONAL][321][:][hassan2][int4][;][}] 
          _1         _3         _2 

Come GManNickG menziona nei commenti questi sono gli iteratori lesseri e non è possibile accedere facilmente alla stringa originale con essi. Il conjure2 example combina l'uso di un lexer e on_error/on_success. Per fare ciò usa un tipo speciale di token, position_token. Questo token ha sempre accesso alla coppia di iteratori della stringa originale associata a se stessa (il token normale perde queste informazioni quando si utilizza lex::omit). position_token ha diversi metodi interessanti. matched() restituisce un iterator_range<OriginalIterator> e begin() e end() restituisce gli iteratori corrispondenti.

Nel codice seguente ho scelto di creare un phoenix::function che accetta due iteratori di lexer (chiamati con _1 e _3) e restituisce una stringa che copre la distanza tra di essi (utilizzando std::string(begin_iter->begin(), end_iter->begin())).

Un problema che ho riscontrato è che il fatto che lo spazio bianco fosse in uno stato diverso causava che gli iteratori restituiti dallo position_token non erano validi. Quello che ho fatto per risolvere questo problema è stato mettere tutto nello stesso stato e quindi usare semplicemente lo lex::_pass = lex::pass_flags::pass_ignore con lo spazio bianco.

L'ultima (minore) problema è che se si desidera utilizzare std::cout << _val è necessario definire operator<< per i tipi a cui sei interessato

PS:. Io uso sempre BOOST_SPIRIT_USE_PHOENIX_V3 e questo richiede che ogni spirito/Phoenix includono viene da boost/spirit/include/.... Se, per qualsiasi motivo, hai bisogno/vuoi usare V2, dovrai cambiare la funzione phoenix ::. Inoltre, non sono in grado di utilizzare uno stile vecchio per il ciclo, quindi se non puoi usare C++ 11 dovrai modificare la definizione di operatore < < per event_descriptor.


#define BOOST_SPIRIT_USE_PHOENIX_V3 
// #define BOOST_SPIRIT_DEBUG 
#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix_core.hpp> 
#include <boost/spirit/include/phoenix_bind.hpp> //CHANGED 
#include <boost/spirit/include/lex_lexertl.hpp> 
#include <boost/spirit/include/lex_lexertl_position_token.hpp> //ADDED 
#include <boost/none.hpp> 
#include <boost/cstdint.hpp> 
#include <boost/fusion/include/adapt_struct.hpp> 
#include <string> 
#include <exception> 
#include <vector> 

namespace lex = boost::spirit::lex; 
namespace px = boost::phoenix; 
namespace qi = boost::spirit::qi; 
namespace ascii = boost::spirit::ascii; 


template <typename Lexer> 
struct tokens : lex::lexer<Lexer> 
{ 
    tokens() 
     : left_curly("\"{\""), 
     right_curly("\"}\""), 
     left_paren("\"(\""), 
     right_paren("\")\""), 
     colon(":"), 
     scolon(";"), 
     namespace_("(?i:namespace)"), 
     event("(?i:event)"), 
     optional("(?i:optional)"), 
     required("(?i:required)"), 
     repeated("(?i:repeated)"), 
     t_int_4("(?i:int4)"), 
     t_int_8("(?i:int8)"), 
     t_string("(?i:string)"), 
     ordinal("\\d+"), 
     identifier("\\w+") 

    { 
     using boost::spirit::lex::_val; 

     this->self 
      = 
       left_curly //[ std::cout << px::val("lpar") << std::endl] 
      | right_curly //[ std::cout << px::val("rpar") << std::endl] 
      | left_paren 
      | right_paren 
      | colon    //[ std::cout << px::val("colon") << std::endl] 
      | scolon 
      | namespace_   // [ std::cout << px::val("kw namesapce") << std::endl] 
      | event    // [ std::cout << px::val("kw event") << std::endl] 
      | optional   //[ std::cout << px::val("optional ") << "-->" << _val << "<--" << std::endl] 
      | required   //[ std::cout << px::val("required") << std::endl] 
      | repeated 
      | t_int_4 
      | t_int_8 
      | t_string 
      | ordinal    //[ std::cout << px::val("val ordinal (") << _val << ")" << std::endl] 
      | identifier   //[std::cout << px::val("val identifier(") << _val << ")" << std::endl] 
      | lex::token_def<>("[ \\t\\n]+") [lex::_pass = lex::pass_flags::pass_ignore] //CHANGED 
      ; 
    } 


    lex::token_def<lex::omit> left_curly, right_curly, left_paren, right_paren, colon, scolon; 
    lex::token_def<lex::omit> namespace_, event, optional, required, repeated, t_int_4, t_int_8, t_string; 
    lex::token_def<boost::uint32_t> ordinal; 
    lex::token_def<> identifier; 
}; 

enum event_entry_qualifier 
{ 
    ENTRY_OPTIONAL, 
    ENTRY_REQUIRED, 
    ENTRY_REPEATED 
}; 

enum entry_type 
{ 
    RBL_INT4, 
    RBL_INT8, 
    RBL_STRING, 
    RBL_EVENT 
}; 

struct oid 
{ 
    boost::uint32_t ordinal; 
    std::string  name; 
}; 

BOOST_FUSION_ADAPT_STRUCT 
(
    oid, 
    (boost::uint32_t, ordinal) 
    (std::string, name) 
) 

std::ostream& operator<<(std::ostream& os, const oid& val) //ADDED 
{ 
    return os << val.ordinal << "-" << val.name; 
} 

struct type_descriptor 
{ 
    entry_type type_id; 
    std::string referenced_event; 
}; 

BOOST_FUSION_ADAPT_STRUCT 
(
    type_descriptor, 
    (entry_type, type_id) 
    (std::string, referenced_event) 
) 

std::ostream& operator<<(std::ostream& os, const type_descriptor& val) //ADDED 
{ 
    return os << val.type_id << "-" << val.referenced_event; 
} 

struct event_entry 
{ 
    event_entry_qualifier qualifier; 
    oid     identifier; 
    type_descriptor  descriptor; 
}; 


BOOST_FUSION_ADAPT_STRUCT 
(
    event_entry, 
    (event_entry_qualifier, qualifier) 
    (oid, identifier) 
    (type_descriptor, descriptor) 
) 

std::ostream& operator<<(std::ostream& os, const event_entry& val) //ADDED 
{ 
    return os << val.qualifier << "-" << val.identifier << "-" << val.descriptor; 
} 

struct event_descriptor 
{ 
    oid      identifier; 
    std::vector<event_entry> event_entries; 
}; 



BOOST_FUSION_ADAPT_STRUCT 
(
    event_descriptor, 
    (oid, identifier) 
    (std::vector<event_entry>, event_entries) 
) 

std::ostream& operator<<(std::ostream& os, const event_descriptor& val) //ADDED 
{ 
    os << val.identifier << "["; 
    for(const auto& entry: val.event_entries) //C++11 
     os << entry; 
    os << "]"; 
    return os; 
} 

struct build_string_impl  //ADDED 
{ 
    template <typename Sig> 
    struct result; 
    template <typename This, typename Iter1, typename Iter2> 
    struct result<This(Iter1,Iter2)> 
    { 
     typedef std::string type; 
    }; 

    template <typename Iter1, typename Iter2> 
    std::string operator()(Iter1 begin, Iter2 end) const 
    { 
     return std::string(begin->begin(),end->begin()); 
    } 
}; 

px::function<build_string_impl> build_string; 

template <typename Iterator, typename Lexer> 
struct grammar : qi::grammar<Iterator,event_descriptor() > 
{ 
    template <typename TokenDef> 
    grammar(TokenDef const& tok) 
     : grammar::base_type(event_descriptor_) 
    { 
     using qi::_val; 
     //start = event; 
     event_descriptor_ = tok.event >> oid_ >> tok.left_curly >> *(event_entry_) >> tok.right_curly; 

     event_entry_ = event_qualifier >> oid_ >> type_descriptor_ >> tok.scolon; 

     event_qualifier = tok.optional [ _val = ENTRY_OPTIONAL] 
         | tok.required [ _val = ENTRY_REQUIRED] 
         | tok.repeated [ _val = ENTRY_REPEATED]; 

     oid_ = tok.ordinal 
      >> tok.colon 
      >> tok.identifier; 

     type_descriptor_ 
      = ((atomic_type >> qi::attr("")) 
      | (event_type >> tok.left_paren >> tok.identifier >> tok.right_paren)); 

     atomic_type = tok.t_int_4   [ _val = RBL_INT4] 
       | tok.t_int_8    [ _val = RBL_INT8] 
       | tok.t_string   [ _val = RBL_STRING]; 

     event_type = tok.event   [_val = RBL_EVENT]; 

     using namespace qi::labels; 
     qi::on_success(event_entry_,std::cout << _val << " " << build_string(_1,_3) << std::endl); //CHANGED 
     // BOOST_SPIRIT_DEBUG_NODES((event_descriptor_)(event_entry_)(event_qualifier)(oid_)(type_descriptor_)(atomic_type)(event_type)); 

    } 

    qi::rule<Iterator> start; 
    qi::rule<Iterator, event_descriptor()> event_descriptor_; 
    qi::rule<Iterator, event_entry()> event_entry_; 
    qi::rule<Iterator, event_entry_qualifier()> event_qualifier; 
    qi::rule<Iterator, entry_type()> atomic_type; 
    qi::rule<Iterator, entry_type()> event_type; 
    qi::rule<Iterator, type_descriptor()> type_descriptor_; 
    qi::rule<Iterator, oid()> oid_; 


}; 

std::string test = " EVENT 1:sihan { OPTIONAL 123:hassan int4; OPTIONAL 321:hassan2 int4; } "; 

int main() 
{ 
    typedef lex::lexertl::position_token<std::string::iterator, boost::mpl::vector<boost::uint32_t, std::string> > token_type; //CHANGED 
    typedef lex::lexertl::actor_lexer<token_type> lexer_type; 
    typedef tokens<lexer_type>::iterator_type iterator_type; 

    tokens<lexer_type> token_lexer; 
    grammar<iterator_type,tokens<lexer_type>::lexer_def> grammar(token_lexer); 

    std::string::iterator it = test.begin(); 
    iterator_type first = token_lexer.begin(it, test.end()); 
    iterator_type last = token_lexer.end(); 

    bool r; 

    r = qi::parse(first, last, grammar); //CHANGED 

    if(r) 
     ; 
    else 
    { 
     std::cout << "parsing failed" << std::endl; 
    } 
} 
+0

Grazie per la risposta così ben ponderata e spiegata. Si occupa anche di uno dei miei precedenti problemi con il dover usare la roba 'qi :: in_state' come skipper. Volevo passare allo stile sopra per saltare lo spazio bianco. Penso di essere finalmente in un posto in cui iniziare a scrivere il mio DSL con rabbia ora. L'unica cosa che manca è spostare il token-> numero intero_id mapping che faccio nella mia 'qi :: grammar' sopra nelle regole 'event_qualifier', 'atomic_type' e 'event_type' di cui sopra in lex. –

+0

Le cose più eccellenti. Ho avuto "tutti" lo stesso lì, tranne che mi sono perso per il link mancante 'position_token'. +1 (@HassanSyed Ho riordinato le dichiarazioni per far corrispondere l'ordine di inizializzazione. '-Wextra' è il tuo amico) – sehe

+0

Un bug strano si presenta quando commento l'azione semantica sul token' option' nel lexer. Qualcosa a che fare con allocare memoria. Mostra sia in clang ++ che in gcc. Questo non appare se rimoziono sia facoltativo che richiesto e cambia l'input per avere entrambi RICHIESTO: D –