Question

Je suis en train d'analyser une fonction C comme des expressions d'arbres comme les suivantes (en utilisant le Spirit Parser cadre ):

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

Pour cela, je suis en train d'utiliser les trois règles de la grammaire suivante:

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

Notez que ma règle d'étiquette essaie juste de capturer les identifiants utilisés dans les expressions (les noms de fonction « »). De même avis que la signature de la règle de la balise renvoie une ExpressionAST au lieu d'un std::string, comme dans la plupart des exemples. La raison pour laquelle je veux faire comme ceci est en fait assez simple: je déteste utiliser des variantes et je les éviter si possible. Il serait bon de garder le gâteau et le manger aussi je suppose.

commande A devrait commencer par une balise (le nom du nœud courant, le premier champ de chaîne du nœud AST) et un nombre variable d'arguments entourés par des parenthèses, et chacun des arguments peut être une étiquette elle-même ou une autre commande.

Toutefois, cet exemple ne fonctionne pas du tout. Il compile et tout, mais au moment de l'exécution, il ne parvient pas à analyser toutes mes chaînes de test. Et la chose qui me gêne vraiment est que je ne peux pas comprendre comment y remédier, puisque je ne peux pas vraiment déboguer le code ci-dessus, au moins au sens traditionnel du mot. Fondamentalement, la seule façon dont je vois que je peux corriger le code ci-dessus est en sachant ce que je fais mal.

Alors, la question est que je ne sais pas ce qui ne va pas avec le code ci-dessus. Comment définiriez-vous la grammaire ci-dessus?

Le type de ExpressionAST J'utilise est:

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)
)
Était-ce utile?

La solution

En ce qui concerne le débogage, il est possible d'utiliser une approche pause et montre normale. Ceci est rendu difficile par la façon dont vous avez formaté les règles cependant. Si vous formatez par les exemples d'esprit (~ un analyseur par ligne, une instruction de Phoenix par ligne), les points de rupture sera beaucoup plus informatif.

Votre structure de données n'a pas de moyen de distinguer A() de SOME en ce qu'ils sont les deux feuilles (laissez-moi savoir si je manque quelque chose). De votre commentaire variante, je ne pense pas que ce soit votre intention, donc de distinguer ces deux cas, j'ai ajouté une variable membre bool commandFlag à MockExpressionNode (vrai pour A() et faux pour SOME), avec une ligne d'adaptation de fusion correspondant.

Pour le code spécifique, vous devez passer la règle de départ au constructeur de base, i.e.:.

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

Ceci est le point d'entrée dans la grammaire, et est la raison pour laquelle vous ne recevez pas les données analysées. Je suis surpris qu'il a compilé sans elle, je pensais que le type de grammaire était nécessaire pour correspondre au type de la première règle. Cependant, cela est une convention pratique à suivre.

Pour la règle de tag, il y a en fait deux parseurs qi::char_("a-zA-Z_"), qui est _1 avec le type char et *qi::char_("a-zA-Z_0-9") qui est _2 avec vector<char> de type (essentiellement). Ce ne est pas possible de contraindre ceux-ci dans une chaîne sans autorules, mais il peut être fait en attachant une règle à chaque omble chevalier analysable:

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>

Cependant, son plus propre à laisser l'esprit faire cette conversion. définir Ainsi, une nouvelle règle:

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

Et ne vous inquiétez pas;). Étiquetterons devient

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

Pour la commande, la première partie est très bien, mais Theres problèmes de couple avec (*instruction >> ",")[ push_back( at_c<1>(qi::_val) , qi::_1 ) ]. Cela analysera les règles d'instruction zéro ou plusieurs suivis d'un « ». Il tente également de push_back un vector<MockExpressionNode> (ne sais pas pourquoi cette compilation soit, peut-être pas instancié à cause de la règle de départ manquante?). Je pense que vous voulez que le suivant (avec la modification de l'identifiant):

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
        ]
    >>  ")";

utilise l'opérateur en option - et l'opérateur de liste %, ce dernier est équivalent à instruction >> *("," >> instruction). L'expression de phénix affecte alors que le vecteur directement à l'organe de structure, mais vous pouvez également joindre l'action directement au match de l'instruction et de l'utilisation push_back.

La règle d'instruction est bien, je vais juste mentionner qu'il est équivalent à instruction %= (command|tag).

Une dernière chose, s'il n'y a en fait pas de distinction entre A() et SOME (à savoir votre structure originale sans commandFlag), vous pouvez écrire cet analyseur en utilisant seulement 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;
};

Ceci est le grand avantage d'utiliser une structure enveloppée de fusion que les modèles d'entrée de près.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top