2011-10-14 12 views
10

Intento implementar un Lexer para un pequeño lenguaje de programación con Boost Spirit.No puedo obtener el valor de cadena de un token

tengo para obtener el valor de una ficha y me da una excepción bad_get:

por terminado llama después de lanzar una instancia de 'boost :: bad_get'
lo que(): impulso :: bad_get: valor no consigue utilizando impulso :: get Aborted

obtengo esta excepción cuando se hace:

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; 

Mi l exer se define así:

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

¿Hay alguna otra manera de obtener el valor de un Token?

+3

en la lectura de nuevo, noto que se especifica 'lex :: omit' como el tipo de atributo token. Estos tokens no expondrán ningún valor de datos (ni siquiera pares de iteradores). Este podría ser tu problema. De lo contrario, recomiendo analizar el uso de Qi en la parte superior de los iteradores de tokens: obtener lo mejor de ambos mundos. – sehe

+0

He verificado y lamentablemente este no es el problema. Solo uso boost :: get en un token del tipo bueno y eso debería tener el valor. –

Respuesta

9

Siempre puede usar los datos del token 'predeterminado' (que es iterator_range del tipo de iterador de origen).

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

Después de estudiar los casos de prueba en el repositorio de impulso, descubrí una serie de cosas:

  • esto es por diseño
  • hay una manera más fácil
  • la más fácil el camino viene automatizado en las acciones semánticas de Lex (por ejemplo, usando _1) y al usar el token lexer en Qi; la asignación convertirá automáticamente a la Qi tipo de atributo
  • esto tiene (de hecho) se llevó la 'de una sola vez, la evaluación perezosa,' semántica mencionados en los documentos

la cincha es que los datos de token es variante, que comienza como el rango del iterador de entrada sin procesar. Solo después de la asignación forzada 'a', el atributo convertido se almacena en caché en la variante. Usted puede ser testigo de la transición:

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; 

Como se puede ver, la asignación de atributo se ve obligado aquí, directamente a través de la implementación assign_to rasgo.

demostración de trabajo completo:

#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

Parece un poco demasiado complicado para algo que debería hacer Spirit. En mi caso, el token se escribe para obtener su valor, así que obtengo una variante de value() que no es directamente un iterador. También tengo un token int. Con su técnica, no aprovecha la variante proporcionada por value(), ¿no? –

+1

¿Qué parte es demasiado complicada? La parte donde dice 'std :: string (iter-> value(). Begin(), iter-> value(). End())'?No lo deletreé (¿esperas que leamos en tu muestra de Looooong '_get_' lo que quieres decir, y no quieres leer 7 líneas de 'showtoken' para ver cómo se hace? Mmmm.) Mi muestra probablemente parece demasiado complicado porque es un ejemplo completo de cómo usar esto en un analizador de la vida real para lograr, por ejemplo error al reportar. Perdón por mostrar cosas que no te interesan :) – sehe

+0

Lo que encuentro demasiado complicado es que analizamos a mano algo que Boost Spirit nos da. Si tengo tokens float, int, string y bool y quiero obtener sus valores primitivos, tengo que crear 4 funciones de análisis, ¿no? Y normalmente estos valores se almacenan en la variante boost ::. O quizás no entiendo el valor de retorno de la función de valor(). –

Cuestiones relacionadas