parsingr dans std :: vector avec Spirit Qi, obtenir des segfaults ou affirmer des échecs

J’utilise Spirit Qi comme parsingur syntaxique pour parsingr les expressions mathématiques dans un arbre d’expression. Je garde trace de choses telles que les types de symboles rencontrés lors de l’parsing, et qui doivent être déclarés dans le texte que j’parsing. En particulier, je suis en train d’parsingr les fichiers d’entrée Bertini , un exemple assez simple est ici , un exemple compliqué est ici , et par souci d’exhaustivité, comme ci-dessous:

%input: our first input file variable_group x,y; function f,g; f = x^2 - 1; g = y^2 - 4; END; 

La grammaire sur laquelle j’ai travaillé sera idéalement

  • recherchez les instructions de déclaration, puis parsingz la liste suivante de symboles du type en cours, séparés par des virgules, et stockez le vecteur de symboles résultant dans l’object de classe en cours d’parsing; par exemple, variable_group x, y;
  • rechercher un symbole précédemment déclaré, suivi d’un signe égal et définissant ce symbole en tant qu’object mathématique évaluable; par exemple, f = x^2 - 1; Cette partie que j’ai surtout sous contrôle.
  • trouvez un symbole non précédemment déclaré suivi de = et parsingz-le comme une sous-fonction. Je pense que je peux gérer cela aussi.

Le problème que je me suis efforcé de résoudre semble être si sortingvial. Pourtant, après des heures de recherche, je n’y suis toujours pas arrivé. J’ai lu des douzaines de publications sur la liste de diffusion de Boost Spirit, des publications de SO, le manuel et les en-têtes de Spirit eux-mêmes, mais je ne comprends toujours pas certaines choses critiques à propos de l’parsing syntaxique de Spirit Qi.

Voici la définition problématique de la grammaire de base, qui irait dans system_parser.hpp :

 #define BOOST_SPIRIT_USE_PHOENIX_V3 1 #include  #include  #include  #include  #include  #include  namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; template struct SystemParser : qi::grammar<Iterator, std::vector(), boost::spirit::ascii::space_type> { SystemParser() : SystemParser::base_type(variable_group_) { namespace phx = boost::phoenix; using qi::_1; using qi::_val; using qi::eps; using qi::lit; qi::symbols encountered_variables; qi::symbols declarative_symbols; declarative_symbols.add("variable_group",0); // wraps the vector between its appropriate declaration and line termination. BOOST_SPIRIT_DEBUG_NODE(variable_group_); debug(variable_group_); variable_group_.name("variable_group_"); variable_group_ %= lit("variable_group") >> genericvargp_ >> lit(';'); // creates a vector of ssortingngs BOOST_SPIRIT_DEBUG_NODE(genericvargp_); debug(genericvargp_); genericvargp_.name("genericvargp_"); genericvargp_ %= new_variable_ % ','; // will in the future make a shared pointer to an object using the ssortingng BOOST_SPIRIT_DEBUG_NODE(new_variable_); debug(new_variable_); new_variable_.name("new_variable_"); new_variable_ %= unencountered_symbol_; // this rule gets a ssortingng. BOOST_SPIRIT_DEBUG_NODE(unencountered_symbol_); debug(unencountered_symbol_); unencountered_symbol_.name("unencountered_symbol"); unencountered_symbol_ %= valid_variable_name_ - ( encountered_variables | declarative_symbols); // get a ssortingng which fits the naming rules. BOOST_SPIRIT_DEBUG_NODE(valid_variable_name_); valid_variable_name_.name("valid_variable_name_"); valid_variable_name_ %= +qi::alpha >> *(qi::alnum | qi::char_('_') | qi::char_('[') | qi::char_(']') ); } // rule declarations. these are member variables for the parser. qi::rule<Iterator, std::vector(), ascii::space_type > variable_group_; qi::rule<Iterator, std::vector(), ascii::space_type > genericvargp_; qi::rule new_variable_; qi::rule unencountered_symbol_;// , ascii::space_type // the rule which determines valid variable names qi::rule valid_variable_name_; }; 

et du code qui l’utilise:

 #include "system_parsing.hpp" int main(int argc, char** argv) { std::vector V; std::ssortingng str = "variable_group x, y, z;"; std::ssortingng::const_iterator iter = str.begin(); std::ssortingng::const_iterator end = str.end(); SystemParser S; bool s = phrase_parse(iter, end, S, boost::spirit::ascii::space, V); std::cout << "the unparsed string:\n" << std::string(iter,end); return 0; } 

Il comstack sous Clang 4.9.x sur OSX parfaitement. Quand je le lance, je reçois:

Assertion failed: (px != 0), function operator->, file /usr/local/include/boost/smart_ptr/shared_ptr.hpp, line 648.

Alternativement, si j’utilise l’opérateur d’attente > plutôt que >> dans la définition de la règle variable_group_ , j’obtiens notre cher vieil ami Segmentation fault: 11 .

Dans mon processus d’apprentissage, j’ai rencontré d’excellents articles sur la façon de dire que le type essaie de générer , sur la propagation d’atsortingbuts , sur la façon d’interagir avec des symboles , un exemple de récursion infinie à gauche qui conduit à une erreur de segmentation, des informations sur l’parsing dans classes, pas des structures qui ont un lien vers l’utilisation de points de personnalisation (mais les liens ne contiennent aucun exemple), l’astuce de Nabialek qui associe des mots-clés à des actions, et peut-être la plus pertinente pour ce que j’essaie de faire, l’parsing dynamic des différences est certainement quelque chose dont j’ai besoin étant donné que le nombre de symboles augmente et que, par la suite, leur utilisation en est un autre, les jeux de symboles déjà vus commencent à être vides et s’agrandissent. Les règles d’parsing sont dynamics.

Alors, voici où je suis. Mon problème actuel est l’assert / segfault généré par cet exemple particulier. Cependant, je ne suis pas sûr de certaines choses et j’ai besoin de conseils, que je n’ai tout simplement pas compilés parmi les sources que j’ai consultées, et la demande qui, espérons-le, rend cette question SO disjointe de celle posée précédemment par d’autres personnes:

  • Quand est-il approprié d’utiliser le lexeme ? Je ne sais pas quand utiliser le lexème, et non.
  • Quelles sont les directives pour savoir quand utiliser > plutôt que >> ?
  • J’ai vu de nombreux exemples de Fusion adapt où il y a une structure à parsingr et un ensemble de règles pour le faire. Mes fichiers d’entrée auront éventuellement plusieurs occurrences de déclarations de fonction, variables, etc., qui doivent toutes être placées au même endroit. Je dois donc pouvoir append des champs à l’object de la classe de terminal dans lequel je suis en train d’parsingr, dans n’importe quel ordre. , plusieurs fois. Je pense que je voudrais utiliser getter / setters pour l’object de classe, de sorte que l’parsing ne soit pas le seul chemin pour la construction d’object. Est-ce un problème?

Tous les conseils utiles pour ce débutant sont les bienvenus.

Vous référencez les variables de symbols . Mais ce sont des locaux, ils n’existent donc pas une fois que le constructeur est revenu. Cela appelle un comportement indéfini . Tout peut arriver.

Faites les tables de symmbol membres de la classe.

Aussi simplifier la danse autour

  • les skippers (voir les numéros de Boost Spirit pour les skippers ). Ce lien répond également à votre _ “Quand est-il approprié d’utiliser lexeme[] ? Dans votre exemple, il vous manquait le lexeme[] autour des encountered_variables|declarative_symbols , par exemple.
  • les macros de débogage
  • l’ operator%= , et quelques trucs généralement inutilisés
  • en devinant que vous n’aviez pas besoin du type mappé des symbols<> (car l’ int n’a pas été consommé), l’initialisation y a été simplifiée

Démo

Live On Coliru

 #define BOOST_SPIRIT_USE_PHOENIX_V3 1 #define BOOST_SPIRIT_DEBUG 1 #include  #include  #include  #include  #include  #include  namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; template  struct SystemParser : qi::grammar(), Skipper> { SystemParser() : SystemParser::base_type(variable_group_) { declarative_symbols += "variable_group"; variable_group_ = "variable_group" >> genericvargp_ >> ';'; genericvargp_ = new_variable_ % ','; valid_variable_name_ = qi::alpha >> *(qi::alnum | qi::char_("_[]")); unencountered_symbol_ = valid_variable_name_ - (encountered_variables|declarative_symbols); new_variable_ = unencountered_symbol_; BOOST_SPIRIT_DEBUG_NODES((variable_group_) (valid_variable_name_) (unencountered_symbol_) (new_variable_) (genericvargp_)) } private: qi::symbols encountered_variables, declarative_symbols; // rule declarations. these are member variables for the parser. qi::rule(), Skipper> variable_group_; qi::rule(), Skipper> genericvargp_; qi::rule new_variable_; qi::rule unencountered_symbol_; // , Skipper // the rule which determines valid variable names qi::rule valid_variable_name_; }; //#include "system_parsing.hpp" int main() { using It = std::ssortingng::const_iterator; std::ssortingng const str = "variable_group x, y, z;"; SystemParser S; It iter = str.begin(), end = str.end(); std::vector V; bool s = phrase_parse(iter, end, S, boost::spirit::ascii::space, V); if (s) { std::cout << "Parse succeeded: " << V.size() << "\n"; for (auto& s : V) std::cout << " - '" << s << "'\n"; } else std::cout << "Parse failed\n"; if (iter!=end) std::cout << "Remaining unparsed: '" << std::string(iter, end) << "'\n"; } 

Impressions

 Parse succeeded: 3 - 'x' - 'y' - 'z'