2011-05-06 10 views
13

Me preguntaba si hay una forma en Boost.Spirit.Qi de combinar dinámicamente un número arbitrario de reglas en tiempo de ejecución. El funcionamiento interno de Boost.Spirit sigue siendo un misterio para mí, pero dado que las reglas se implementan como objetos, parece factible. Mi motivación es hacer que ciertas partes de mi gramática se extiendan fácilmente.Combina dinámicamente reglas de Boost.Spirit.Qi en tiempo de ejecución (número arbitrario de alternativas)

consideremos el siguiente ejemplo artificial:

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

typedef std::string::const_iterator iterator_t; 

template<typename Expr> 
inline bool parse_full(const std::string& input, const Expr& expr) 
{ 
    iterator_t first(input.begin()), last(input.end()); 

    bool result = qi::phrase_parse(first, last, expr, boost::spirit::ascii::space); 

    return first == input.end() && result; 
} 

void no_op() {} 

int main(int argc, char *argv[]) 
{ 
    int attr = -1; 

    // "Static" version - Works fine! 
    /* 
    qi::rule<iterator_t, void(int&)> grammar; 

    qi::rule<iterator_t, void(int&)> ruleA = qi::char_('a')[qi::_r1 = px::val(0)]; 
    qi::rule<iterator_t, void(int&)> ruleB = qi::char_('b')[qi::_r1 = px::val(1)]; 
    qi::rule<iterator_t, void(int&)> ruleC = qi::char_('c')[qi::_r1 = px::val(2)]; 

    grammar = 
     ruleA(qi::_r1) | //[no_op] 
     ruleB(qi::_r1) | //[no_op] 
     ruleC(qi::_r1); //[no_op] 
    */ 

    // "Dynamic" version - Does not compile! :(

    std::vector<qi::rule<iterator_t, void(int&)>> rules; 

    rules.push_back(qi::char_('a')[qi::_r1 = px::val(0)]); 
    rules.push_back(qi::char_('b')[qi::_r1 = px::val(1)]); 
    rules.push_back(qi::char_('c')[qi::_r1 = px::val(2)]); 

    std::vector<qi::rule<iterator_t, void(int&)>>::iterator i(rules.begin()), last(rules.end()); 

    qi::rule<iterator_t, void(int&)> grammar; 

    grammar = (*i)(qi::_r1); 

    for(++i; i!=last; ++i) 
    { 
     grammar = grammar.copy() | (*i)(qi::_r1); 
    } 

    // Tests 

    if(parse_full("a", grammar(px::ref(attr)))) std::cout << attr << std::endl; 
    if(parse_full("b", grammar(px::ref(attr)))) std::cout << attr << std::endl; 
    if(parse_full("c", grammar(px::ref(attr)))) std::cout << attr << std::endl; 

    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 

    return 0; 
} 

El error dado por Visual Studio 2010 es:

error C2440: 'initializing' : cannot convert from 'boost::fusion::void_' to 'int &' 

Mi sospecha es que esto es causado por no aprobar el atributo heredado a grammar.copy(). Desafortunadamente, no pude encontrar una manera fácil de hacerlo, así que opté por una solución alternativa. Como resultado, tengo una última versión (¡y me gustaría agradecer a todos los que se quedaron hasta ahora!). Este hecho parece funcionar:

// "Dynamic" version - Kind of works! :-/ 

    std::vector<qi::rule<iterator_t, void(int&)>> rules; 

    rules.push_back(qi::char_('a')[qi::_r1 = px::val(0)]); 
    rules.push_back(qi::char_('b')[qi::_r1 = px::val(1)]); 
    rules.push_back(qi::char_('c')[qi::_r1 = px::val(2)]); 

    std::vector<qi::rule<iterator_t, void(int&)>>::iterator i(rules.begin()), last(rules.end()); 

    qi::rule<iterator_t, int()> temp; 

    temp = (*i)(qi::_val); //[no_op] 

    for(++i; i!=last; ++i) 
    { 
     temp = temp.copy() | (*i)(qi::_val); //[no_op] 
    } 

    qi::rule<iterator_t, void(int&)> grammar; 

    grammar = temp[qi::_r1 = qi::_1]; 

Sin embargo, una vez que adjuntar una acción semántica sencilla (por ejemplo, "[NO_OP]"), el comportamiento se vuelve muy raro. En lugar de imprimir 0,1,2 como antes, imprime 0,0,2. Entonces me pregunto, ¿es lo que estoy tratando de lograr lo que resulta en un comportamiento indefinido? ¿Es esto un error? O muy posiblemente, ¿estoy usando algo (por ejemplo, acciones semánticas?) De la manera incorrecta?

Respuesta

7

Sí No estoy seguro de entender realmente cómo funciona internamente, pero no copia todas las reglas en su bucle (sólo el de la izquierda) así que esto parece funcionar:

std::vector<qi::rule<iterator_t, void(int&)>> rules; 

rules.push_back(qi::char_('a')[qi::_r1 = px::val(0)]); 
rules.push_back(qi::char_('b')[qi::_r1 = px::val(1)]); 
rules.push_back(qi::char_('c')[qi::_r1 = px::val(2)]); 

std::vector<qi::rule<iterator_t, void(int&)>>::iterator 
i(rules.begin()), last(rules.end()); 

qi::rule<iterator_t, int()> temp; 

for(; i!=last; ++i) 
{ 
qi::rule<iterator_t, int()> tmp = (*i)(qi::_val)[no_op]; 

    temp = temp.copy() | tmp.copy(); 
} 

qi::rule<iterator_t, void(int&)> grammar; 

grammar = temp[qi::_r1 = qi::_1]; 

// Tests 

int intres1; 
int intres2; 
int intres3; 

bool res1 = parse_full("a", grammar(px::ref(intres1))); 
bool res2 = parse_full("b", grammar(px::ref(intres2))); 
bool res3 = parse_full("c", grammar(px::ref(intres3))); 
+0

Gracias! Tu respuesta parece muy prometedora. Desafortunadamente, no tengo ningún medio para probarlo ahora mismo. ¡Lo aceptaré tan pronto como tenga la oportunidad de probarlo! – kloffy

+0

¿Funcionó este arreglo? –

+1

sinceras disculpas por llegar casi 2 años tarde. ¡He venido a probar esta solución y funciona de maravilla! – kloffy

Cuestiones relacionadas