Search code examples
c++parsingboostgrammarboost-spirit-qi

Template compilation errors with Boost::Spirit::Qi for simple grammar


I am trying to parse the grammar:

@ ( )  

using Boost::Spirit::Qi.

I got the parsing working with phrase_parse and now I'm creating a grammar class (one step at a time).

Here are my failure messages from Visual Studio 2010 (on Windows 7):

1>------ Build started: Project: Event_Grammar, Configuration: Debug Win32 ------
1>  main.cpp
1>c:\boost_1_57_0\boost\spirit\home\qi\nonterminal\rule.hpp(304): error C2664: 'bool boost::function4<R,T0,T1,T2,T3>::operator ()(T0,T1,T2,T3) const' : cannot convert parameter 4 from 'const boost::spirit::unused_type' to 'const boost::spirit::qi::char_class<Tag> '
1>          with
1>          [
1>              R=bool,
1>              T0=std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>> &,
1>              T1=const std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>> &,
1>              T2=boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type &,boost::fusion::nil_>,boost::fusion::vector0<>> &,
1>              T3=const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space,boost::spirit::char_encoding::ascii>> &
1>          ]
1>          and
1>          [
1>              Tag=boost::spirit::tag::char_code<boost::spirit::tag::space,boost::spirit::char_encoding::ascii>
1>          ]
1>          Reason: cannot convert from 'const boost::spirit::unused_type' to 'const boost::spirit::qi::char_class<Tag>'
1>          with
1>          [
1>              Tag=boost::spirit::tag::char_code<boost::spirit::tag::space,boost::spirit::char_encoding::ascii>
1>          ]
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>          c:\boost_1_57_0\boost\spirit\home\qi\reference.hpp(43) : see reference to function template instantiation 'bool boost::spirit::qi::rule<Iterator,T1,T2,T3,T4>::parse<Context,Skipper,Attribute>(Iterator &,const Iterator &,Context &,const Skipper &,Attribute &) const' being compiled
1>          with
1>          [
1>              Iterator=std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>>,
1>              T1=boost::spirit::ascii::space_type,
1>              T2=boost::spirit::unused_type,
1>              T3=boost::spirit::unused_type,
1>              T4=boost::spirit::unused_type,
1>              Context=const boost::spirit::unused_type,
1>              Skipper=boost::spirit::unused_type,
1>              Attribute=const boost::spirit::unused_type
1>          ]
1>          c:\boost_1_57_0\boost\spirit\home\qi\detail\parse.hpp(46) : see reference to function template instantiation 'bool boost::spirit::qi::reference<Subject>::parse<Iterator,const boost::spirit::unused_type,boost::spirit::unused_type,const boost::spirit::unused_type>(Iterator &,const Iterator &,Context &,const Skipper &,Attribute &) const' being compiled
1>          with
1>          [
1>              Subject=const boost::spirit::qi::rule<std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>>,boost::spirit::ascii::space_type,boost::spirit::unused_type,boost::spirit::unused_type,boost::spirit::unused_type>,
1>              Iterator=std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>>,
1>              Context=const boost::spirit::unused_type,
1>              Skipper=boost::spirit::unused_type,
1>              Attribute=const boost::spirit::unused_type
1>          ]
1>          c:\boost_1_57_0\boost\spirit\home\qi\detail\parse_auto.hpp(166) : see reference to function template instantiation 'bool boost::spirit::qi::detail::parse_impl<Expr>::call<Iterator>(Iterator &,Iterator,const Expr &)' being compiled
1>          with
1>          [
1>              Expr=Event_Grammar<std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>>,boost::spirit::ascii::space_type>,
1>              Iterator=std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>>
1>          ]
1>          c:\mks_sandboxes\script_compiler_dissector\shared libraries\multiple platform\script language\src\event_grammar\src\event_parser.hpp(22) : see reference to function template instantiation 'bool boost::spirit::qi::parse<Iterator,Event_Grammar<Iterator,Skipper>>(Iterator &,Iterator,Expr &)' being compiled
1>          with
1>          [
1>              Iterator=std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>>,
1>              Skipper=boost::spirit::ascii::space_type,
1>              Expr=Event_Grammar<std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>>,boost::spirit::ascii::space_type>
1>          ]
1>          c:\mks_sandboxes\script_compiler_dissector\shared libraries\multiple platform\script language\src\event_grammar\src\main.cpp(41) : see reference to function template instantiation 'bool Event_Parser<std::_String_const_iterator<_Elem,_Traits,_Alloc>>(Iterator,Iterator)' being compiled
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>,
1>              _Alloc=std::allocator<char>,
1>              Iterator=std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char>>
1>          ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

main.cpp:

/*!
 *  \file   main.cpp
 *  \brief  This file contains code to validate the Script Compiler
 *          Event grammar.
 *  \note   This project uses the Boost::Spirit library for parsing.
 */
#include <iostream>
#include <string>
#include <cstdlib>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

#include "event_parser.hpp"

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

using qi::double_;
using qi::_1;
using ascii::space;
using ascii::char_;
using phoenix::ref;
using qi::phrase_parse;

int main(void)
{
    const char  program_header[] =
        "\n"
        "Event Grammar Tester:\n"
        "    A program to validate the grammar for\n"
        "    a Script Language Event.\n"
        "\n";

    std::cout.write(program_header, sizeof(program_header) - 1);

    const std::string   parse_test_string = "@ ( )";
    std::string         token_names;
    bool result = Event_Parser(parse_test_string.begin(), parse_test_string.end());

    std::cout << "Parsing ";
    if (result)
    {
        std::cout << "succeeded";
    }
    else
    {
        std::cout << "failed";
    }
    std::cout << " for statement \""
              << parse_test_string
              << "\"\n";

    std::cout << "\nPaused.  Press Enter to continue.\n";
    std::cin.ignore(1000000, '\n');

    return EXIT_SUCCESS;
}

event_parser.hpp

/*!
 *  \file   event_parser.hpp
 *  \brief  This file defines a parser Script Language Events, this is the root.
 *  \note   This file should be used with Boost::Spirit parsers.
 */
#ifndef EVENT_PARSER_HPP
#define EVENT_PARSER_HPP
#include "event_grammar.hpp"

#include <string>
#include <boost/spirit/include/qi.hpp>

template <typename Iterator>
bool
Event_Parser(Iterator first, Iterator last)
{
    using qi::parse;
    using ascii::space;
    using ascii::char_;

    Event_Grammar<Iterator, ascii::space_type>  parser;
    bool result = parse(first, last, parser);
    if (first != last)
        return false;
    return result;
}

#endif // EVENT_PARSER_HPP

event_grammar.hpp

/*!
 *  \file   event_grammar.hpp
 *  \brief  This class defines the grammar for the Script Language Events.
 *  \note   This class uses features from Boost::Spirit.
 */
#ifndef EVENT_GRAMMAR_HPP
#define EVENT_GRAMMAR_HPP

#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <string>


template <typename Iterator, typename Skipper>
struct Event_Grammar
    : boost::spirit::qi::grammar<Iterator, Skipper>
{
    Event_Grammar() : Event_Grammar::base_type(start)
        {
            using boost::spirit::ascii::char_;
            using boost::spirit::qi::eps;

            start = 
                (
                    char_('@') >> char_('(') >> char_(')')
                )
                ;

        }
    boost::spirit::qi::rule<Iterator, Skipper> start;
};


#endif // EVENT_GRAMMAR_HPP

I'm only looking for a pass or fail for the parser.

I'm using Boost 1.57.0.

What am I missing in my grammar structure?

Am I correctly using the grammar in the parser function?


Solution

  • The compiler error leads you directly to this line:

        // If you are seeing a compilation error here stating that the
        // fourth parameter can't be converted to a required target type
        // then you are probably trying to use a rule or a grammar with
        // an incompatible skipper type.
        if (f(first, last, context, skipper))
    

    With Spirit, it is a good idea to read the comments around the blocks appearing in the template instantiation trace!

    The problem is clearly that you need to pass a skipper. You were planning on doing that:

    bool result = qi::phrase_parse(first, last, parser, ascii::space);
    

    Live On Coliru

    /*!
     *  \file   event_grammar.hpp
     *  \brief  This class defines the grammar for the Script Language Events.
     *  \note   This class uses features from Boost::Spirit.
     */
    #ifndef EVENT_GRAMMAR_HPP
    #define EVENT_GRAMMAR_HPP
    
    #include <boost/spirit/include/qi.hpp>
    #include <boost/variant.hpp>
    #include <string>
    
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace phoenix = boost::phoenix;
    
    
    template <typename Iterator, typename Skipper>
    struct Event_Grammar
        : boost::spirit::qi::grammar<Iterator, Skipper>
    {
        Event_Grammar() : Event_Grammar::base_type(start)
            {
                using boost::spirit::ascii::char_;
                using boost::spirit::qi::eps;
    
                start = 
                    (
                        char_('@') >> char_('(') >> char_(')')
                    )
                    ;
    
            }
        boost::spirit::qi::rule<Iterator, Skipper> start;
    };
    
    
    #endif // EVENT_GRAMMAR_HPP
    
    /*!
     *  \file   event_parser.hpp
     *  \brief  This file defines a parser Script Language Events, this is the root.
     *  \note   This file should be used with Boost::Spirit parsers.
     */
    #ifndef EVENT_PARSER_HPP
    #define EVENT_PARSER_HPP
    //#include "event_grammar.hpp"
    
    #include <string>
    #include <boost/spirit/include/qi.hpp>
    
    template <typename Iterator>
    bool Event_Parser(Iterator first, Iterator last)
    {
        using ascii::space;
        using ascii::char_;
    
        Event_Grammar<Iterator, ascii::space_type>  parser;
        bool result = qi::phrase_parse(first, last, parser, ascii::space);
        if (first != last)
            return false;
        return result;
    }
    
    #endif //ndef EVENT_PARSER_HPP
    /*!
     *  \file   main.cpp
     *  \brief  This file contains code to validate the Script Compiler
     *          Event grammar.
     *  \note   This project uses the Boost::Spirit library for parsing.
     */
    #include <iostream>
    #include <string>
    #include <cstdlib>
    
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix_core.hpp>
    #include <boost/spirit/include/phoenix_operator.hpp>
    
    //#include "event_parser.hpp"
    
    using qi::double_;
    using qi::_1;
    using ascii::space;
    using ascii::char_;
    using phoenix::ref;
    using qi::phrase_parse;
    
    int main(void)
    {
        const char  program_header[] =
            "\n"
            "Event Grammar Tester:\n"
            "    A program to validate the grammar for\n"
            "    a Script Language Event.\n"
            "\n";
    
        std::cout.write(program_header, sizeof(program_header) - 1);
    
        const std::string   parse_test_string = "@ ( )";
        std::string         token_names;
        bool result = Event_Parser(parse_test_string.begin(), parse_test_string.end());
    
        std::cout << "Parsing ";
        if (result)
        {
            std::cout << "succeeded";
        }
        else
        {
            std::cout << "failed";
        }
        std::cout << " for statement \""
                  << parse_test_string
                  << "\"\n";
    
        return EXIT_SUCCESS;
    }