2014-05-17 22 views
5

Desidero utilizzare Boost.Spirit.Lex per eseguire il lex di un file binario; A tal fine ho scritto il seguente programma (qui è un estratto):
Utilizzo di Boost.Spirit.Lex e iteratori di flusso

#include <boost/spirit/include/lex_lexertl.hpp> 
#include <boost/spirit/include/support_multi_pass.hpp> 
#include <boost/bind.hpp> 
#include <boost/ref.hpp> 
#include <fstream> 
#include <iterator> 
#include <string> 

namespace spirit = boost::spirit; 
namespace lex = spirit::lex; 

#define X 1 
#define Y 2 
#define Z 3 

template<typename L> 
class word_count_tokens : public lex::lexer<L> 
{ 
    public: 
     word_count_tokens() { 
      this->self.add 
       ("[^ \t\n]+", X) 
       ("\n", Y) 
       (".", Z); 
     } 
}; 

class counter 
{ 
    public: 
     typedef bool result_type; 

     template<typename T> 
     bool operator() (const T &t, size_t &c, size_t &w, size_t &l) const { 
      switch (t.id()) { 
       case X: 
        ++w; c += t.value().size(); 
        break; 
       case Y: 
        ++l; ++c; 
        break; 
       case Z: 
        ++c; 
        break; 
      } 

      return true; 
     } 
}; 

int main (int argc, char **argv) 
{ 
    std::ifstream ifs (argv[1], std::ios::in | std::ios::binary); 
    auto first = spirit::make_default_multi_pass (std::istream_iterator<char> (ifs)); 
    auto last = spirit::make_default_multi_pass (std::istream_iterator<char>()); 
    size_t w, c, l; 
    word_count_tokens<lex::lexertl::lexer<>> word_count_functor; 

    w = c = l = 0; 

    bool r = lex::tokenize (first, last, word_count_functor, boost::bind (counter(), _1, boost::ref (c), boost::ref (w), boost::ref (l))); 

    ifs.close(); 

    if (r) { 
     std::cout << l << ", " << w << ", " << c << std::endl; 
    } 

    return 0; 
} 

La build restituisce il seguente errore:

lexer.hpp:390:46: error: non-const lvalue reference to type 'const char *' cannot bind to a value of unrelated type 

Ora, l'errore è dovuto alla definizione di concreta lexer, lex::lexer<>; infatti il ​​suo primo parametro è impostato di default su const char *. Ottengo lo stesso errore anche se uso spirit::istream_iterator o spirit::make_default_multi_pass (.....).
Ma se si specificano i parametri di modello corretti di lex::lexer<> ottengo una pletora di errori!

Soluzioni?

Aggiornamento

ho putted tutti i file sorgente; è l'esempio del sito word_counter.

+0

Naturalmente, il i tipi esatti di 'first' e' last' sono importanti. Un sacco. – sehe

+0

Ho corretto il codice della tua domanda modificata. Per pubblicare una nuova domanda, spiegherò le correzioni. – sehe

+0

Il mio problema era molto semplice, scusami se il mio inglese non era molto chiaro! :-(Ora, se voglio usare qualcosa come l'esempio in [Boost.Spirit.Lex] (http://www.boost.org/doc/libs/1_55_0/libs/spirit/example/lex/word_count_functor.cpp) ma con 'istream_iterator' invece' const char * ', l'esempio non viene compilato riportando l'errore specificato.Nel mio aggiornamento ho inserito il mio codice reale –

risposta

2

Va bene, dal momento che la domanda è stata cambiata, ecco una nuova risposta, affrontando alcuni punti con l'esempio di codice completo.

  1. In primo luogo, è necessario utilizzare un tipo di token personalizzato. Cioè

    word_count_tokens<lex::lexertl::lexer<lex::lexertl::token<boost::spirit::istream_iterator>>> word_count_functor; 
    // instead of: 
    // word_count_tokens<lex::lexertl::lexer<>> word_count_functor; 
    

    Ovviamente, è consuetudine typedef lex::lexertl::token<boost::spirit::istream_iterator>

  2. È necessario utilizzare min_token_id anziché di token ID 1,2,3. Inoltre, ne fanno un enum per facilità di manutenzione:

    enum token_ids { 
        X = lex::min_token_id + 1, 
        Y, 
        Z, 
    }; 
    
  3. Non è più possibile utilizzare solo .size() sul default del token value() dal momento che la gamma iteratore non è RandomAccessRange più.Invece, impiegare boost::distance() che è specializzato per iterator_range:

     ++w; c += boost::distance(t.value()); // t.value().size(); 
    

La combinazione di queste correzioni: Live On Coliru

#include <boost/spirit/include/lex_lexertl.hpp> 
#include <boost/spirit/include/support_istream_iterator.hpp> 
#include <boost/bind.hpp> 
#include <fstream> 

namespace spirit = boost::spirit; 
namespace lex = spirit::lex; 

enum token_ids { 
    X = lex::min_token_id + 1, 
    Y, 
    Z, 
}; 

template<typename L> 
class word_count_tokens : public lex::lexer<L> 
{ 
    public: 
     word_count_tokens() { 
      this->self.add 
       ("[^ \t\n]+", X) 
       ("\n"  , Y) 
       ("."  , Z); 
     } 
}; 

struct counter 
{ 
    typedef bool result_type; 

    template<typename T> 
    bool operator() (const T &t, size_t &c, size_t &w, size_t &l) const { 
     switch (t.id()) { 
      case X: 
       ++w; c += boost::distance(t.value()); // t.value().size(); 
       break; 
      case Y: 
       ++l; ++c; 
       break; 
      case Z: 
       ++c; 
       break; 
     } 

     return true; 
    } 
}; 

int main (int argc, char **argv) 
{ 
    std::ifstream ifs (argv[1], std::ios::in | std::ios::binary); 
    ifs >> std::noskipws; 
    boost::spirit::istream_iterator first(ifs), last; 
    word_count_tokens<lex::lexertl::lexer<lex::lexertl::token<boost::spirit::istream_iterator>>> word_count_functor; 

    size_t w = 0, c = 0, l = 0; 
    bool r = lex::tokenize (first, last, word_count_functor, 
      boost::bind (counter(), _1, boost::ref (c), boost::ref (w), boost::ref (l))); 

    ifs.close(); 

    if (r) { 
     std::cout << l << ", " << w << ", " << c << std::endl; 
    } 
} 

Quando eseguito su se stesso, stampe

65, 183, 1665 
+0

Ok! Grazie! Questa era la mia risposta attesa! :-) Ma ... i documenti Boost non sono molto esplicativi ... Cioè, sulla definizione del tipo di token era chiaro, ma su min_token_id no! Grazie! –

2

Penso che il vero problema non venga mostrato. Non visualizzi first o last e ho la sensazione che potresti avere dei provvisori lì.

Ecco un esempio che mi è venuta a verificare, forse si può vedere che cosa è che stai facendo male --- --- diverso :)

#include <boost/spirit/include/lex_lexertl.hpp> 
#include <boost/spirit/include/qi.hpp> 
#include <fstream> 
#ifdef MEMORY_MAPPED 
# include <boost/iostreams/device/mapped_file.hpp> 
#endif 

namespace /*anon*/ 
{ 
    namespace qi =boost::spirit::qi; 
    namespace lex=boost::spirit::lex; 

    template <typename Lexer> 
     struct mylexer_t : lex::lexer<Lexer> 
    { 
     mylexer_t() 
     { 
      fileheader = "hello"; 

      this->self = fileheader 
       | space [ lex::_pass = lex::pass_flags::pass_ignore ]; 
     } 

     lex::token_def<lex::omit> 
      fileheader, space; 
    }; 

    template <typename Iterator> struct my_grammar_t 
     : public qi::grammar<Iterator> 
    { 
     template <typename TokenDef> 
      my_grammar_t(TokenDef const& tok) 
       : my_grammar_t::base_type(header) 
     { 
      header = tok.fileheader; 
      BOOST_SPIRIT_DEBUG_NODE(header); 
     } 

     private: 
     qi::rule<Iterator> header; 
    }; 
} 

namespace /* */ { 

    std::string safechar(char ch) { 
     switch (ch) { 
      case '\t': return "\\t"; break; 
      case '\0': return "\\0"; break; 
      case '\r': return "\\r"; break; 
      case '\n': return "\\n"; break; 
     } 
     return std::string(1, ch); 
    } 

    template <typename It> 
     std::string showtoken(const boost::iterator_range<It>& range) 
     { 
      std::ostringstream oss; 
      oss << '['; 
      std::transform(range.begin(), range.end(), std::ostream_iterator<std::string>(oss), safechar); 
      oss << ']'; 
      return oss.str(); 
     } 
} 

bool parsefile(const std::string& spec) 
{ 
#ifdef MEMORY_MAPPED 
    typedef char const* It; 
    boost::iostreams::mapped_file mmap(spec.c_str(), boost::iostreams::mapped_file::readonly); 
    char const *first = mmap.const_data(); 
    char const *last = first + mmap.size(); 
#else 
    typedef char const* It; 
    std::ifstream in(spec.c_str()); 
    in.unsetf(std::ios::skipws); 

    std::string v(std::istreambuf_iterator<char>(in.rdbuf()), std::istreambuf_iterator<char>()); 
    It first = &v[0]; 
    It last = first+v.size(); 
#endif 

    typedef lex::lexertl::token<It /*, boost::mpl::vector<char, unsigned int, std::string> */> token_type; 
    typedef lex::lexertl::actor_lexer<token_type> lexer_type; 

    typedef mylexer_t<lexer_type>::iterator_type iterator_type; 
    try 
    { 
     static mylexer_t<lexer_type> mylexer; 
     static my_grammar_t<iterator_type> parser(mylexer); 

     auto iter = mylexer.begin(first, last); 
     auto end = mylexer.end(); 

     bool r = qi::parse(iter, end, parser); 

     r = r && (iter == end); 

     if (!r) 
      std::cerr << spec << ": parsing failed at: \"" << std::string(first, last) << "\"\n"; 
     return r; 
    } 
    catch (const qi::expectation_failure<iterator_type>& e) 
    { 
     std::cerr << "FIXME: expected " << e.what_ << ", got '"; 
     for (auto it=e.first; it!=e.last; it++) 
      std::cerr << showtoken(it->value()); 
     std::cerr << "'" << std::endl; 
     return false; 
    } 
} 

int main() 
{ 
    if (parsefile("input.bin")) 
     return 0; 
    return 1; 
} 

Per la variante:

typedef boost::spirit::istream_iterator It; 
std::ifstream in(spec.c_str()); 
in.unsetf(std::ios::skipws); 

It first(in), last; 
+0

La tua risposta è stata utile per capire che tokenize (...) non può essere usato con altri iteratori invece di 'const char *', è corretto? Devo usare actor_lexer per usare iteratori personalizzati? –

+0

'actor_lexer' ha permesso l'uso di azioni semantiche (e stato, IIRC) nel lexer. Ho mostrato l'uso di ** ['boost :: istream_iterator' in alternativa] (http: //coliru.stacked-crooked.c om/a/595c391789fc1db5) **, quindi sì è possibile. Se hai una versione modificata di questo SSCCE che mostra un problema con cui sei bloccato, potresti postare _that_ come una domanda. ** Modifica ** Vedo che hai aggiornato la domanda (...) 7 ore dopo la mia risposta. Daremo un'occhiata alla tua domanda modificata più tardi – sehe

Problemi correlati