Search code examples
boostboost-spiritboost-fusion

Is it a boost spirit regression?


I have a boost spirit parser which works very well with boost 1.46.1, and which doesn't work with boost 1.54.

This parser extract informations from the following sentence, which is a variable initialization in a DSEL : "Position #start=0;0;0". Informations extracted from this sentence are stored in a structure :

  • Type of the variable will be stored (here position) ;
  • Name of variable (here start) ;
  • The "#" means that the variable is "static" ;
  • The value of the variable (0;0;0).

The code to extract these inforamtions is as follow :

#include <iostream>
#include <boost/spirit/include/classic.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi_char_class.hpp>

using namespace std;
using namespace boost::spirit;

struct VariableInitialization
{
    std::string m_type;
    bool m_is_static;
    std::string m_name;
    std::string m_value;
};

BOOST_FUSION_ADAPT_STRUCT(
        VariableInitialization,
        (std::string, m_type)
        (bool, m_is_static)
        (std::string, m_name)
        (std::string, m_value)
        )

template <typename Iterator>
struct VariableInitializationParser : qi::grammar<Iterator, VariableInitialization(), ascii::space_type> {
/*!
* IsStatic is a mapping betwen a char and a boolean
*/
struct IsStatic_ : qi::symbols<char, bool> {
    IsStatic_()
    {
        add("@", false)("#", true);
    }
}IsStatic;

VariableInitializationParser() :
    VariableInitializationParser::base_type(start) {
    using qi::lit;
    using ascii::char_;
    using qi::_val;
    using qi::_1;
    /*!
     * For now, type is one of the three following litterals :
     */
    var_type %= lit("Position")|lit("String")|lit("Numeric")|lit("Integer")|lit("Trajectory");

    /*!
     * identifier is how a variable can be named. Name of variable is an alpha (a-zA-Z) or an _,followed
     * by any alpha numeric (a-zA-Z0-9) or a _. The followings are correct :
     * _toto _T5ot_To t1oTo ...
     * The following are incorrect
     * 12toto -tiotp ...
     */
    identifier %= ((ascii::alpha|char_('_')) >> *(ascii::alnum|char_('_')));

    /*!
     * var value can be anything because it's parsed by someone else.
     */
    var_value %= qi::lexeme[*(char_)];
    start = var_type >> IsStatic >> identifier >> '=' >> var_value;
}
qi::rule<Iterator, std::string(), ascii::space_type> var_type;
qi::rule<Iterator, std::string(), ascii::space_type> identifier;
qi::rule<Iterator, std::string(), ascii::space_type> var_value;
qi::rule<Iterator, VariableInitialization(), ascii::space_type> start;
};

int main()
{
    VariableInitialization variable;

    std::string input = "Position #toto=1;2;2";
    std::string::const_iterator iter = input.begin();
    std::string::const_iterator end = input.end();
    // The phrase_parse call wil fill the structure "variable" with the good values if the syntax is correct.
    // if the syntax is not correct, the method will return false.
    // So if input = "Integer #toto= 6", variable.m_type == "Integer", variable.m_isStatic==true,
    // variable.m_name=="toto" and variable.m_vale="6".
    VariableInitializationParser<std::string::const_iterator> m_parser;
    bool ok = phrase_parse(iter, end, m_parser, boost::spirit::ascii::space, variable);
    if(!ok) return false;
    std::cout << "Boost version : " << BOOST_VERSION << std::endl;
    std::cout << "Type : " << variable.m_type << std::endl
              << "Is Static : " << variable.m_is_static << std::endl
              << "Name :" << variable.m_name << std::endl
              << "Value :" << variable.m_value << std::endl;
    return 0;
}

Output of the following code is different in Boost 1.46.1 and in boost 1.53. With boost 1.46.1 I have the following Output :

Boost version : 104601
Type : Position
Is Static : 1
Name :toto
Value :1;2;2

With boost 1.54 I have :

Boost version : 105400
Type : 
Is Static : 1
Name :toto
Value :1;2;2

As you can see in boost 1.54 the Position output is not filled by the parser.

I read (maybe not carefully) the changelog of Fusion and Spirit, and I can't find why this happens.

Does someone have an explaination ?


Solution

  • No this is not a regression. Quite clearly you depended on undocumented (possibly undefined) behaviour.

    Just change lit into string, or just slightly more compact:

    var_type = qi::raw[lit("Position")|"String"|"Numeric"|"Integer"|"Trajectory"];
    

    See it Live On Coliru

    #include <iostream>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/fusion/include/adapt_struct.hpp>
    
    using namespace std;
    using namespace boost::spirit;
    
    struct VariableInitialization
    {
        std::string m_type;
        bool m_is_static;
        std::string m_name;
        std::string m_value;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(
            VariableInitialization,
            (std::string, m_type)
            (bool, m_is_static)
            (std::string, m_name)
            (std::string, m_value)
            )
    
    template <typename Iterator>
    struct VariableInitializationParser : qi::grammar<Iterator, VariableInitialization(), ascii::space_type> {
        struct IsStatic_ : qi::symbols<char, bool> {
            IsStatic_() {
                add("@", false)("#", true);
            }
        } IsStatic;
    
        VariableInitializationParser() :
            VariableInitializationParser::base_type(start) {
                using qi::lit;
                using ascii::char_;
    
                var_type   = qi::raw[lit("Position")|"String"|"Numeric"|"Integer"|"Trajectory"];
    
                identifier = (ascii::alpha|'_') >> *(ascii::alnum|'_');
    
                var_value  = qi::lexeme[*(char_)];
                start      = var_type >> IsStatic >> identifier >> '=' >> var_value;
            }
    
        qi::rule<Iterator, std::string(), ascii::space_type> var_type;
        qi::rule<Iterator, std::string(), ascii::space_type> identifier;
        qi::rule<Iterator, std::string(), ascii::space_type> var_value;
        qi::rule<Iterator, VariableInitialization(), ascii::space_type> start;
    };
    
    int main()
    {
        VariableInitialization variable;
    
        std::string input = "Position #toto=1;2;2";
        std::string::const_iterator iter = input.begin();
        std::string::const_iterator end = input.end();
    
        VariableInitializationParser<std::string::const_iterator> m_parser;
    
        bool ok = phrase_parse(iter, end, m_parser, boost::spirit::ascii::space, variable);
        if(!ok) return false;
    
        std::cout << "Boost version : " << BOOST_VERSION        << "\n";
        std::cout << "Type : "          << variable.m_type      << "\n"
                  << "Is Static : "     << variable.m_is_static << "\n"
                  << "Name :"           << variable.m_name      << "\n"
                  << "Value :"          << variable.m_value     << "\n";
    }