2010-06-20 20 views
9

Estoy tratando de analizar un C-función como expresiones de árboles como el siguiente (utilizando el Spirit Parser Framework):Analizar una gramática con el Espíritu Boost

F(A() , B(GREAT(SOME , NOT)) , C(YES)) 

Por esto estoy tratando de utilizar las tres reglas en el siguiente gramática:

template< typename Iterator , typename ExpressionAST > 
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), space_type> { 

    InputGrammar() : InputGrammar::base_type() { 
     tag = (qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9"))[ push_back(at_c<0>(qi::_val) , qi::_1) ]; 
     command = tag [ at_c<0>(qi::_val) = at_c<0>(qi::_1) ] >> "(" >> (*instruction >> ",") 
             [ push_back(at_c<1>(qi::_val) , qi::_1) ] >> ")"; 
     instruction = (command | tag) [qi::_val = qi::_1]; 
    } 
    qi::rule< Iterator , ExpressionAST() , space_type > tag; 
    qi::rule< Iterator , ExpressionAST() , space_type > command; 
    qi::rule< Iterator , ExpressionAST() , space_type > instruction; 
}; 

en cuenta que mi regla de etiqueta simplemente trata de captar los identificadores utilizados en las expresiones (los nombres de función ''). También observe que la firma de la regla de etiqueta devuelve un ExpressionAST en lugar de un std::string, como en la mayoría de los ejemplos. La razón por la que quiero hacerlo así es bastante simple: odio usar variantes y las evitaré si es posible. Sería genial conservar el pastel y comérselo también, supongo.

Un comando debe comenzar con una etiqueta (el nombre del nodo actual, primer campo de cadena del nodo AST) y un número variable de argumentos entre paréntesis, y cada uno de los argumentos puede ser una etiqueta u otro comando .

Sin embargo, este ejemplo no funciona en absoluto. Compila y todo, pero en tiempo de ejecución no puede analizar todas mis cadenas de prueba. Y lo que realmente me molesta es que no sé cómo solucionarlo, ya que no puedo depurar el código anterior, al menos en el sentido tradicional de la palabra. Básicamente, la única forma en que veo que puedo corregir el código anterior es saber qué estoy haciendo mal.

Entonces, la pregunta es que no sé cuál es el problema con el código anterior. ¿Cómo definirías la gramática anterior?

El tipo ExpressionAST que estoy usando es:

struct MockExpressionNode { 
    std::string name; 
    std::vector<MockExpressionNode> operands; 

    typedef std::vector<MockExpressionNode>::iterator iterator; 
    typedef std::vector<MockExpressionNode>::const_iterator const_iterator; 

    iterator begin() { return operands.begin(); } 
    const_iterator begin() const { return operands.begin(); } 
    iterator end() { return operands.end(); } 
    const_iterator end() const { return operands.end(); } 

    bool is_leaf() const { 
     return (operands.begin() == operands.end()); 
    } 
}; 

BOOST_FUSION_ADAPT_STRUCT(
    MockExpressionNode, 
    (std::string, name) 
    (std::vector<MockExpressionNode>, operands) 
) 
+0

Algo que descubrí recientemente es que los identificadores C y C++ pueden tener caracteres '$' en sus nombres. De modo que a-z, A-Z, 0-9 (excepto el primer carácter), _ y $ son válidos en un identificador C/C++. – Cthutu

+2

@Cthutu MSVC permite caracteres acentuados en los identificadores. No significa que cumple con los estándares. –

+0

Más importante aún, ¿cuál es el punto que estás tratando de hacer @Cthutu? ¿Hay escasez de identificadores? ¿Su compilador no admite espacios de nombres correctamente? – sehe

Respuesta

11

En cuanto a la depuración, es posible utilizar un descanso normal y ver enfoque. Sin embargo, esto dificulta la forma en que ha formateado las reglas. Si formatea según los ejemplos de espíritu (~ un analizador por línea, una declaración de fénix por línea), los puntos de inflexión serán mucho más informativos.

Su estructura de datos no tiene una forma de distinguir A() de SOME en que ambas son hojas (avíseme si me falta algo). De su comentario de variante, no creo que esta fuera su intención, así que para distinguir estos dos casos, agregué una variable de miembro bool commandFlag a MockExpressionNode (verdadero para A() y falso para SOME), con una línea de adaptador de fusión correspondiente.

Para el código en concreto, que necesita para aprobar la regla de inicio al constructor de base, es decir .:

InputGrammar() : InputGrammar::base_type(instruction) {...} 

Este es el punto de entrada en la gramática, y es por eso que no estaba recibiendo ningún dato analizados. Me sorprende que compilara sin él, pensé que se requería el tipo de gramática para que coincidiera con el tipo de la primera regla. Aun así, esta es una conveniente convención a seguir.

Para la regla tag, en realidad hay dos analizadores qi::char_("a-zA-Z_"), que se _1 con el tipo char y *qi::char_("a-zA-Z_0-9") que se _2 con el tipo (básicamente) vector<char>.No es posible coaccionar a éstos en una cadena sin autorules, pero se puede hacer colocando una regla para cada carbón analizada:

tag = qi::char_("a-zA-Z_") 
     [ at_c<0>(qi::_val) = qi::_1 ]; 
    >> *qi::char_("a-zA-Z_0-9")   //[] has precedence over *, so _1 is 
     [ at_c<0>(qi::_val) += qi::_1 ]; // a char rather than a vector<char> 

Sin embargo, es mucho más limpio para dejar que el espíritu hacer esta conversión. Así que defina una nueva regla:

qi::rule< Iterator , std::string(void) , ascii::space_type > identifier; 
identifier %= qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9"); 

Y no se preocupe;). A continuación, la etiqueta se convierte en

tag = identifier 
     [ 
      at_c<0>(qi::_val) = qi::_1, 
      ph::at_c<2>(qi::_val) = false //commandFlag 
     ] 

para el mando, la primera parte está bien, pero theres un par de problemas con (*instruction >> ",")[ push_back(at_c<1>(qi::_val) , qi::_1) ]. Esto analizará reglas de instrucción cero o múltiples seguidas de un ",". También intenta push_back vector<MockExpressionNode> (¿no está seguro de por qué esto compilado tampoco, tal vez no instanciado debido a la regla de inicio que falta?). Creo que quieres lo siguiente (con la modificación identificador):

command = 
     identifier 
     [ 
      ph::at_c<0>(qi::_val) = qi::_1, 
      ph::at_c<2>(qi::_val) = true //commandFlag 
     ] 
    >> "(" 
    >> -(instruction % ",") 
     [ 
      ph::at_c<1>(qi::_val) = qi::_1 
     ] 
    >> ")"; 

Este utiliza el operador opcional - y el operador de la lista %, este último es equivalente a instruction >> *("," >> instruction). La expresión de fénix simplemente asigna el vector directamente al miembro de la estructura, pero también puede adjuntar la acción directamente a la coincidencia de instrucción y usar push_back.

La regla de instrucción está bien, solo mencionaré que es equivalente a instruction %= (command|tag).

Una última cosa, si en realidad no hay distinción entre A() y SOME (es decir, su estructura original sin commandFlag), se puede escribir este analizador utilizando sólo autorules:

template< typename Iterator , typename ExpressionAST > 
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), ascii::space_type> { 
    InputGrammar() : InputGrammar::base_type(command) { 
     identifier %= 
      qi::char_("a-zA-Z_") 
     >> *qi::char_("a-zA-Z_0-9"); 
     command %= 
      identifier 
     >> -(
      "(" 
     >> -(command % ",") 
     >> ")"); 
    } 
    qi::rule< Iterator , std::string(void) , ascii::space_type > identifier; 
    qi::rule< Iterator , ExpressionAST(void) , ascii::space_type > command; 
}; 

Esta es la gran ventaja de usando una estructura envolvente de fusión que modela la entrada de cerca.

+0

Hola AcademicRobot, excelente publicación. Me tomó unos días responder simplemente porque había mucho para digerir sobre los operadores que realmente no leí en los documentos. También estaba tratando de reemplazar el comando setgregadores con qi :: _ val.setAsFlag(); pero al parecer, el tipo de _val no es lo mismo que ExpressionAST, pero un actor envoltorio fénix de algún tipo – lurscher

+1

@lurscher - Me alegro de que lo haya encontrado útil. Sí, qi :: _ val evaluará a ExpressionAST, pero en realidad no es de ese tipo. Para llamar a las funciones de miembro, tendrá que usar el enlace phoenix (para memfun 'void setAsFlag (bool flag)'): 'phoenix :: bind (& ExpressionAST :: setAsFlag, qi :: _ val, true)'. – academicRobot

+0

extraño, que tiene un cierto sabor a vacío *. – lurscher

Cuestiones relacionadas