2011-10-14 9 views
10

Cerco di implementare un Lexer per un piccolo linguaggio di programmazione con Boost Spirit.Non riesco a ottenere il valore di stringa di un token

devo ottenere il valore di un gettone e ottengo un'eccezione bad_get:

Termina chiamata dopo aver lanciato un caso di 'boost :: bad_get'
cosa(): boost :: bad_get: valore fallito ottenere usando boost :: ottenere abortito

ottengo questa eccezione quando si fa:

std::string contents = "void"; 

base_iterator_type first = contents.begin(); 
base_iterator_type last = contents.end(); 

SimpleLexer<lexer_type> lexer; 

iter = lexer.begin(first, last); 
end = lexer.end(); 

std::cout << "Value = " << boost::get<std::string>(iter->value()) << std::endl; 

mio l exer è definito in questo modo:

typedef std::string::iterator base_iterator_type; 
typedef boost::spirit::lex::lexertl::token<base_iterator_type, boost::mpl::vector<unsigned int, std::string>> Tok; 
typedef lex::lexertl::actor_lexer<Tok> lexer_type; 

template<typename L> 
class SimpleLexer : public lex::lexer<L> { 
    private: 

    public: 
     SimpleLexer() { 
      keyword_for = "for"; 
      keyword_while = "while"; 
      keyword_if = "if"; 
      keyword_else = "else"; 
      keyword_false = "false"; 
      keyword_true = "true"; 
      keyword_from = "from"; 
      keyword_to = "to"; 
      keyword_foreach = "foreach"; 

      word = "[a-zA-Z]+"; 
      integer = "[0-9]+"; 
      litteral = "..."; 

      left_parenth = '('; 
      right_parenth = ')'; 
      left_brace = '{'; 
      right_brace = '}'; 

      stop = ';'; 
      comma = ','; 

      swap = "<>"; 
      assign = '='; 
      addition = '+'; 
      subtraction = '-'; 
      multiplication = '*'; 
      division = '/'; 
      modulo = '%'; 

      equals = "=="; 
      not_equals = "!="; 
      greater = '>'; 
      less = '<'; 
      greater_equals = ">="; 
      less_equals = "<="; 

      whitespaces = "[ \\t\\n]+"; 
      comments = "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/"; 

      //Add keywords 
      this->self += keyword_for | keyword_while | keyword_true | keyword_false | keyword_if | keyword_else | keyword_from | keyword_to | keyword_foreach; 
      this->self += integer | litteral | word; 

      this->self += equals | not_equals | greater_equals | less_equals | greater | less ; 
      this->self += left_parenth | right_parenth | left_brace | right_brace; 
      this->self += comma | stop; 
      this->self += assign | swap | addition | subtraction | multiplication | division | modulo; 

      //Ignore whitespaces and comments 
      this->self += whitespaces [lex::_pass = lex::pass_flags::pass_ignore]; 
      this->self += comments [lex::_pass = lex::pass_flags::pass_ignore]; 
     } 

     lex::token_def<std::string> word, litteral, integer; 

     lex::token_def<lex::omit> left_parenth, right_parenth, left_brace, right_brace; 

     lex::token_def<lex::omit> stop, comma; 

     lex::token_def<lex::omit> assign, swap, addition, subtraction, multiplication, division, modulo; 
     lex::token_def<lex::omit> equals, not_equals, greater, less, greater_equals, less_equals; 

     //Keywords 
     lex::token_def<lex::omit> keyword_if, keyword_else, keyword_for, keyword_while, keyword_from, keyword_to, keyword_foreach; 
     lex::token_def<lex::omit> keyword_true, keyword_false; 

     //Ignored tokens 
     lex::token_def<lex::omit> whitespaces; 
     lex::token_def<lex::omit> comments; 
}; 

C'è un altro modo per ottenere il valore di un token?

+3

a leggere di nuovo, ho notato che si specifica 'lex :: omit' come il tipo di attributo token. Questi token non espongono i dati di qualsiasi valore (nemmeno coppie di iteratori). Questo potrebbe essere il tuo problema. Altrimenti, raccomando vivamente l'analisi usando il Qi sopra gli iteratori di token: ottieni il meglio da entrambi i mondi. – sehe

+0

Ho verificato e purtroppo questo non è il problema. Io uso solo boost :: ottenere su un token quello del tipo buono e che dovrebbe avere il valore. –

risposta

9

È sempre possibile utilizzare i dati del token 'predefinito' (che è iteratore_intervallo del tipo iteratore di origine).

std::string tokenvalue(iter->value().begin(), iter->value().end()); 

Dopo aver studiato i casi di test nel repository spinta, ho scoperto una serie di cose:

  • questo è di progettazione
  • c'è un modo più semplice
  • il più facile il modo viene automatizzato nelle azioni semantiche di Lex (ad es. usando _1) e quando si usa il token lexer in Qi; l'assegnazione convertirà automaticamente al Qi tipo
  • attribuire questo ha (anzi) ottenuto i 'e di una volta, valutazione pigri' semantica menzionati nella documentazione

Il Cinch è che i dati token è variante, che inizia come intervallo di iteratore di input non elaborato. Solo dopo l'assegnazione forzata 'a', l'attributo convertito viene memorizzato nella cache nella variante. Si può assistere la transizione:

lexer_type::iterator_type iter = lexer.begin(first, last); 
lexer_type::iterator_type end = lexer.end(); 

assert(0 == iter->value().which()); 
std::cout << "Value = " << boost::get<boost::iterator_range<base_iterator_type> >(iter->value()) << std::endl; 

std::string s; 
boost::spirit::traits::assign_to(*iter, s); 
assert(1 == iter->value().which()); 
std::cout << "Value = " << s << std::endl; 

Come si può vedere, l'assegnazione di attributo è costretto qui, direttamente tramite l'implementazione assign_to trait.

completa dimostrazione di lavoro:

#include <boost/spirit/include/lex_lexertl.hpp> 

#include <iostream> 
#include <string> 

namespace lex = boost::spirit::lex; 

typedef std::string::iterator base_iterator_type; 
typedef boost::spirit::lex::lexertl::token<base_iterator_type, boost::mpl::vector<int, std::string>> Tok; 
typedef lex::lexertl::actor_lexer<Tok> lexer_type; 

template<typename L> 
class SimpleLexer : public lex::lexer<L> { 
    private: 

    public: 
     SimpleLexer() { 
      word = "[a-zA-Z]+"; 
      integer = "[0-9]+"; 
      literal = "..."; 

      this->self += integer | literal | word; 
     } 

     lex::token_def<std::string> word, literal; 
     lex::token_def<int> integer; 
}; 

int main(int argc, const char* argv[]) { 
    SimpleLexer<lexer_type> lexer; 

    std::string contents = "void"; 

    base_iterator_type first = contents.begin(); 
    base_iterator_type last = contents.end(); 

    lexer_type::iterator_type iter = lexer.begin(first, last); 
    lexer_type::iterator_type end = lexer.end(); 

    assert(0 == iter->value().which()); 
    std::cout << "Value = " << boost::get<boost::iterator_range<base_iterator_type> >(iter->value()) << std::endl; 

    std::string s; 
    boost::spirit::traits::assign_to(*iter, s); 
    assert(2 == iter->value().which()); 
    std::cout << "Value = " << s << std::endl; 

    return 0; 
} 
+0

Sembra un po 'complicato per qualcosa che dovrebbe essere fatto da Spirit. Nel mio caso il token viene digitato per ottenere il loro valore così ottengo una variante da value() non direttamente un iteratore. Ho anche un token int. Con la tua tecnica, non sfrutti la variante fornita da value(), no? –

+1

Quale parte è complicata? La parte in cui ha detto 'std :: string (iter-> value(). Begin(), iter-> value(). End())'?Non l'ho spiegato (ci si aspetta che leggiamo nel tuo esempio Looooung per "_get_" cosa intendi, e non vuoi leggere 7 righe di "showtoken" per vedere come è fatto? Mmmm.) Il mio esempio probabilmente sembra complicato perché è un esempio completo di come usarlo in un parser di vita reale per raggiungere, ad es segnalazione degli errori. Ci scusiamo per aver mostrato cose che non ti interessano :) – sehe

+0

La cosa che trovo complicata è che analizziamo a mano qualcosa che Boost Spirit ci dà. Se ho i token float, int, string e bool e voglio ottenere i loro valori primitivi, devo creare 4 funzioni di parsing, no? E normalmente questi valori sono memorizzati in boost :: variant. O forse non capisco il valore restituito della funzione value(). –