2015-03-14 1 views
1

J'ai déclaré les règles de ma grammaire comme const static. Cela a fonctionné bien jusqu'à ce que j'aie essayé d'utiliser des règles croisées récursives (la règle1 est définie en utilisant rule2 qui est définie en utilisant rule1). Le code source peut encore être construit, mais segfaults sur la source d'analyse contenant un tel cas récursif. est ici un code simplifié de la grammaire:Déclaration des règles de recoupement

template < typename Iterator > 
class Skipper : public qi::grammar<Iterator> { 
public: 
    Skipper () : Skipper::base_type(_skip_rule) { } 
private: 
    static qi::rule<Iterator> const 
     _comment, 
     _skip_rule; 
}; 

template < typename Iterator > 
typename qi::rule<Iterator> const 
    Skipper<Iterator>::_comment(
     boost::spirit::repository::confix("/*", "*/")[*(qi::char_ - "*/")]   // Multi-line 
     | boost::spirit::repository::confix("//", qi::eol)[*(qi::char_ - qi::eol)] // Single-line 
    ); 

template < typename Iterator > 
typename qi::rule<Iterator> const 
    Skipper<Iterator>::_skip_rule(qi::ascii::space | _comment); 

template < typename Iterator, typename Skipper > 
class Grammar : public qi::grammar<Iterator, Skipper > { 
public: 
    Grammar () : Grammar::base_type(expression) { } 
private: 
    static qi::rule<Iterator, Skipper> const 
     // Tokens 
     scalar_literal, 
     identifier, 
     // Rules 
     operand, 
     expression; 
}; 

template < typename Iterator, typename Skipper > 
typename qi::rule<Iterator, Skipper> const 
    Grammar<Iterator, Skipper>::scalar_literal(qi::uint_ | qi::int_); 

template < typename Iterator, typename Skipper > 
typename qi::rule<Iterator, Skipper> const 
    Grammar<Iterator, Skipper>::identifier(qi::lexeme[(qi::alpha | '_') >> *(qi::alnum | '_')]); 

template < typename Iterator, typename Skipper > 
typename qi::rule<Iterator, Skipper> const 
    Grammar<Iterator, Skipper>::operand((scalar_literal | identifier | ('(' >> expression >> ')'))); 

template < typename Iterator, typename Skipper > 
typename qi::rule<Iterator, Skipper> const 
    Grammar<Iterator, Skipper>::expression(operand); 

(expression règle est identique à opérande pour rendre le code plus facile à comprendre, bien sûr, il devrait être plus compliqué encore basé sur opérande) . opérande déclaration utilise expression un et vice versa. Ce segfaults en essayant de parse_phrase par exemple (123). Je suppose que c'est à cause de "forward" en utilisant expression; même si je mets expression déclaration avant l'opérande un. Alors, de quelle manière ces règles devraient-elles être déclarées pour éviter les erreurs d'exécution?

Répondre

1
  1. Tout d'abord, la statique n'a rien à voir avec elle:

    Live On Coliru échoue tout aussi mal:

    #include <boost/spirit/include/qi.hpp> 
    #include <boost/spirit/repository/include/qi.hpp> 
    
    namespace qi = boost::spirit::qi; 
    
    template <typename Iterator> 
    struct Skipper : qi::grammar<Iterator> { 
        Skipper() : Skipper::base_type(_skip_rule) { } 
    private: 
        qi::rule<Iterator> const 
         _comment { 
          boost::spirit::repository::confix("/*", "*/") [*(qi::char_ - "*/")]  // Multi-line 
         | boost::spirit::repository::confix("//", qi::eol) [*(qi::char_ - qi::eol)] // Single-line 
         }, 
         _skip_rule { 
          qi::ascii::space | _comment 
         }; 
    }; 
    
    template <typename Iterator, typename Skipper> 
    struct Grammar : qi::grammar<Iterator, Skipper> { 
        Grammar() : Grammar::base_type(expression) { } 
    private: 
        qi::rule<Iterator, Skipper> const 
         // Tokens 
         scalar_literal { qi::uint_ | qi::int_ }, 
         identifier  { qi::lexeme[(qi::alpha | '_') >> *(qi::alnum | '_')] }, 
         // Rules 
         operand  { (scalar_literal | identifier | ('(' >> expression >> ')')) }, 
         expression  { operand }; 
    }; 
    
    int main() { 
        using It = std::string::const_iterator; 
        Skipper<It> s; 
        Grammar<It, Skipper<It> > p; 
        std::string const input = "(123)"; 
    
        It f = input.begin(), l = input.end(); 
    
        bool ok = qi::phrase_parse(f,l,p,s); 
    
        if (ok) std::cout << "Parse success\n"; 
        else  std::cout << "Parse failed\n"; 
        if (f!=l) std::cout << "Remaining input: '" << std::string(f,l) << "'\n"; 
    } 
    
  2. En second lieu, le skipper n'a rien à voir avec les choses :

    Live On Coliru échoue tout aussi mal:

    #include <boost/spirit/include/qi.hpp> 
    #include <boost/spirit/repository/include/qi.hpp> 
    
    namespace qi = boost::spirit::qi; 
    
    template <typename Iterator, typename Skipper = qi::ascii::space_type> 
    struct Grammar : qi::grammar<Iterator, Skipper> { 
        Grammar() : Grammar::base_type(expression) { } 
    private: 
        qi::rule<Iterator, Skipper> const 
         // Tokens 
         scalar_literal { qi::uint_ | qi::int_ }, 
         identifier  { qi::lexeme[(qi::alpha | '_') >> *(qi::alnum | '_')] }, 
         // Rules 
         operand  { (scalar_literal | identifier | ('(' >> expression >> ')')) }, 
         expression  { operand }; 
    }; 
    
    int main() { 
        using It = std::string::const_iterator; 
        Grammar<It> p; 
        std::string const input = "(123)"; 
    
        It f = input.begin(), l = input.end(); 
    
        bool ok = qi::phrase_parse(f,l,p,qi::ascii::space); 
    
        if (ok) std::cout << "Parse success\n"; 
        else  std::cout << "Parse failed\n"; 
        if (f!=l) std::cout << "Remaining input: '" << std::string(f,l) << "'\n"; 
    } 
    
  3. En troisième lieu, le moment de l'initialisation doit faire avec:

    Live On Coliru réussit:

    #include <boost/spirit/include/qi.hpp> 
    #include <boost/spirit/repository/include/qi.hpp> 
    
    namespace qi = boost::spirit::qi; 
    
    template <typename Iterator, typename Skipper = qi::ascii::space_type> 
    struct Grammar : qi::grammar<Iterator, Skipper> { 
        Grammar() : Grammar::base_type(expression) { 
         scalar_literal = qi::uint_ | qi::int_; 
         identifier  = (qi::alpha | '_') >> *(qi::alnum | '_'); 
         // Rules 
         operand  = (scalar_literal | identifier | ('(' >> expression >> ')')); 
         expression  = operand; 
        } 
    private: 
        qi::rule<Iterator>   scalar_literal, identifier; // Tokens 
        qi::rule<Iterator, Skipper> operand,  expression; // Rules 
    }; 
    
    int main() { 
        using It = std::string::const_iterator; 
        Grammar<It> p; 
        std::string const input = "(123)"; 
    
        It f = input.begin(), l = input.end(); 
    
        bool ok = qi::phrase_parse(f,l,p,qi::ascii::space); 
    
        if (ok) std::cout << "Parse success\n"; 
        else  std::cout << "Parse failed\n"; 
        if (f!=l) std::cout << "Remaining input: '" << std::string(f,l) << "'\n"; 
    } 
    

    Prints

    Parse success 
    
  4. Enfin, vous pouvez avoir tout le gâteau et le manger aussi:

    Live On Coliru

    #include <boost/spirit/include/qi.hpp> 
    #include <boost/spirit/repository/include/qi.hpp> 
    
    namespace qi = boost::spirit::qi; 
    
    namespace parsing { 
        namespace detail { 
         template <typename Iterator> 
         struct Skipper : qi::grammar<Iterator> { 
          Skipper() : Skipper::base_type(_skip_rule) { 
           _comment = boost::spirit::repository::confix("/*", "*/") [*(qi::char_ - "*/")]  // Multi-line 
             | boost::spirit::repository::confix("//", qi::eol) [*(qi::char_ - qi::eol)] // Single-line 
             ; 
    
           _skip_rule = qi::ascii::space | _comment; 
          } 
         private: 
          qi::rule<Iterator> _comment, _skip_rule; 
         }; 
    
         template <typename Iterator, typename Skipper = Skipper<Iterator> > 
         struct Grammar : qi::grammar<Iterator, Skipper> { 
          Grammar() : Grammar::base_type(expression) { 
           scalar_literal = qi::uint_ | qi::int_; 
           identifier  = (qi::alpha | '_') >> *(qi::alnum | '_'); 
           // Rules 
           operand  = (scalar_literal | identifier | ('(' >> expression >> ')')); 
           expression  = operand; 
          } 
         private: 
          qi::rule<Iterator>   scalar_literal, identifier; // Tokens 
          qi::rule<Iterator, Skipper> operand,  expression; // Rules 
         }; 
        } 
    
        template <typename Iterator, typename Skipper = detail::Skipper<Iterator> > 
        struct facade { 
         template <typename Range> static bool parse(Range const& input) { 
          Iterator f = boost::begin(input), l = boost::end(input); 
          bool ok = qi::phrase_parse(f, l, _parser, _skipper); 
    
          if (f!=l) 
           std::cout << "Remaining input: '" << std::string(f,l) << "'\n"; 
    
          return ok; 
         } 
    
        private: 
         static const detail::Skipper<Iterator>   _skipper; 
         static const detail::Grammar<Iterator, Skipper> _parser; 
        }; 
    
        template <class I, class S> const detail::Skipper<I> facade<I,S>::_skipper = {}; 
        template <class I, class S> const detail::Grammar<I, S> facade<I,S>::_parser = {}; 
    } 
    
    int main() { 
        using It = std::string::const_iterator; 
        std::string const input = "(123)"; 
    
        bool ok = parsing::facade<It>::parse(input); 
    
        if (ok) std::cout << "Parse success\n"; 
        else  std::cout << "Parse failed\n"; 
    } 
    

Notez que le résultat est le même, l'analyseur/skipper sont tout aussi statiques et const que dans le code original, le code est beaucoup plus facile à maintenir (et a un peu plus de structure en même temps ime).C'est fondamentalement là où le thème Singletons-are-bad rencontre le thème interne-const-is-problématique. Vous n'avez pas besoin de faire les champs const. Vous n'avez pas besoin de rendre les instances statiques. Créez simplement une seule instance si vous préférez. De plus, ce n'est pas un problème que l'analyseur soit maintenant copiable (vous n'avez pas à le copier, mais maintenant vous pouvez le faire).

+0

Merci beaucoup! Mais une question de plus: si j'essaie d'initialiser des règles en dehors du corps du constructeur: 'Grammaire(): scalaire_literal (qi :: uint_ | qi :: int_), identificateur ((qi :: alpha | '_') >> * (qi :: alnum | '_')), opérande ((scalar_literal | identifiant | ('(' ('>> expression') '))), expression (opérande), Grammaire :: base_type (expression) ' J'ai reçu un avertissement indiquant que _expression_ sera initialisé après _base_type_ call. Ensuite, l'analyse échoue. Donc les règles DOIVENT être assignées UNIQUEMENT dans le corps du constructeur? Ou existe-t-il un moyen de modifier l'ordre d'initialisation? – Vercetti

+0

N'est pas lié à la base: http://en.cppreference.com/w/cpp/language/initializer_list#Initialization_order et http://stackoverflow.com/a/2669911/85371. Je pense que la vraie raison est la différence entre l'assignation et l'initialisation (copie). Je ne suis pas motivé pour découvrir la différence si :) juste coller aux idiomes communs. Inner-const n'est pas très utile de toute façon. – sehe

+0

J'ai compris. Merci beaucoup, votre réponse m'a vraiment aidé. – Vercetti