Search code examples
c++parsingboostboost-spiritboost-spirit-qi

Spirit phrase_parse expects 6 arguments instead of 5


A follow up to this question Handling boost spirit expression on string. Again thanks @sehe! But when I try to wrap all the grammar and ast in the namespaces and call them from within a class, I get an error by the phrase_parse api saying its expecting 6 arguments instead of 5.

The code I'm trying to implement:



#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <iomanip>
#include <list>
#include <string>

namespace client { namespace ast {
    struct signed_;
    struct program;

    using operand = boost::variant<unsigned int, bool,
                                   boost::recursive_wrapper<signed_>,
                                   boost::recursive_wrapper<program>>;

    struct signed_ { char sign; operand operand_; };
    struct operation { char operator_; operand operand_; };
    struct program { operand first; std::list<operation> rest; };
}} // namespace client::ast

BOOST_FUSION_ADAPT_STRUCT(client::ast::signed_, sign, operand_)
BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, operator_, operand_)
BOOST_FUSION_ADAPT_STRUCT(client::ast::program, first, rest)

namespace client { namespace ast
{

    ///////////////////////////////////////////////////////////////////////////
    //  The AST evaluator
    ///////////////////////////////////////////////////////////////////////////
    struct eval {
        using result_type = int;

        int operator()(auto const& n) const { return n; }

        int operator()(operation const& x, int lhs) const {
            int rhs = boost::apply_visitor(*this, x.operand_);
            switch (x.operator_) {
            case '+': return lhs + rhs;
            case '-': return lhs - rhs;
            case '*': return lhs * rhs;
            case '/': return lhs / rhs;
            case '|': return lhs | rhs;
            case '&': return lhs & rhs;
            }
            BOOST_ASSERT(0);
            return 0;
        }

        int operator()(signed_ const& x) const {
            int rhs = boost::apply_visitor(*this, x.operand_);
            switch (x.sign) {
            case '-': return -rhs;
            case '+': return +rhs;
            case '!': return !rhs;
            }
            BOOST_ASSERT(0);
            return 0;
        }

        int operator()(program const& x) const {
            int state = boost::apply_visitor(*this, x.first);
            for (auto& oper : x.rest) {
                state = (*this)(oper, state);
            }
            return state;
        }
    };
}} // namespace client::ast

namespace client
{
    namespace qi = boost::spirit::qi;

    template <typename Iterator>
    struct calculator : qi::grammar<Iterator, ast::program(), qi::space_type> {
        calculator() : calculator::base_type(expression) {
            using namespace qi::labels;

            expression = term >> *(qi::char_("-+|&") >> term);
            term       = factor >> *(qi::char_("*/!") >> factor);
            str        = '"' >> *~qi::char_('"') >> '"';
            equality_comparison = (str >> '=' >> '=' >> str)[_val = (_1 == _2)];
            inequality_comparison = (str >> '!' >> '=' >> str)[_val = (_1 != _2)];
            factor     = inequality_comparison | equality_comparison | qi::uint_ | qi::bool_ | qi::char_("-+!") >> factor |
                '(' >> expression >> ')';
        }

      private:
        qi::rule<Iterator, ast::program(), qi::space_type> expression, term;
        qi::rule<Iterator, ast::operand(), qi::space_type> factor, equality_comparison , inequality_comparison;
        qi::rule<Iterator, std::string()> str;
    };
}

class Expression_handler{
    public:

    
    int Execute_expression(std::string str){
            using iterator_type = std::string::const_iterator;
    using calculator    = client::calculator<iterator_type>;
    using ast_program   = client::ast::program;
    using ast_eval      = client::ast::eval;

     calculator const calc;  // Our grammar
     ast_eval const   eval;  // Evaluates the program
         ast_program program;

        auto f(std::begin(str)), l(std::end(str));
        bool r = phrase_parse(f, l, calc, client::qi::space, program);

        if (r && f == l) {
            std::cout << "\nResult: " << quoted(str) << " -> " << eval(program) << std::endl;
            return (eval(program));
            
        }
        else{
            std::cout << "\nFailed at: " << quoted(std::string (f, l)) << "\n";
            
        }
            

        return 0;
    };
};

int main(){
    Expression_handler exp;
    exp.Execute_expression("\"abc\" == \"abc\"");
    return 0;
}

The error generated for this code:

ion_handler.cpp:37:24: warning: use of ‘auto’ in parameter declaration only available with ‘-std=c++20’ or ‘-fconcepts’
   37 |         int operator()(auto const& n) const { return n; }
      |                        ^~~~
In file included from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:36,
                 from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
                 from /usr/include/boost/spirit/home/qi.hpp:21,
                 from /usr/include/boost/spirit/include/qi.hpp:16,
                 from Expression_handler.cpp:5:
/usr/include/boost/spirit/home/qi/reference.hpp: In instantiation of ‘bool boost::spirit::qi::reference<Subject>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Context = boost::spirit::context<boost::fusion::cons<client::ast::program&, boost::fusion::nil_>, boost::spirit::locals<> >; Skipper = boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >; Attribute = client::ast::program; Subject = const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, client::ast::program(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type>]’:
/usr/include/boost/spirit/home/qi/parse.hpp:168:45:   required from ‘bool boost::spirit::qi::phrase_parse(Iterator&, Iterator, const Expr&, const Skipper&, boost::spirit::qi::skip_flag, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Expr = client::calculator<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > >; Skipper = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>; Attr = client::ast::program]’
/usr/include/boost/spirit/home/qi/parse.hpp:201:32:   required from ‘bool boost::spirit::qi::phrase_parse(Iterator&, Iterator, const Expr&, const Skipper&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Expr = client::calculator<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > >; Skipper = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>; Attr = client::ast::program]’
Expression_handler.cpp:114:30:   required from here
/usr/include/boost/spirit/home/qi/reference.hpp:43:35: error: no matching function for call to ‘boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, client::ast::program(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type>::parse(__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >&, boost::spirit::context<boost::fusion::cons<client::ast::program&, boost::fusion::nil_>, boost::spirit::locals<> >&, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&, client::ast::program&) const’
   43 |             return ref.get().parse(first, last, context, skipper, attr_);
      |                    ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
                 from /usr/include/boost/spirit/home/qi.hpp:21,
                 from /usr/include/boost/spirit/include/qi.hpp:16,
                 from Expression_handler.cpp:5:
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:282:14: note: candidate: ‘template<class Context, class Skipper, class Attribute> bool boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Context = Context; Skipper = Skipper; Attribute = Attribute; Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; T1 = client::ast::program(); T2 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>; T3 = boost::spirit::unused_type; T4 = boost::spirit::unused_type]’
  282 |         bool parse(Iterator& first, Iterator const& last
      |              ^~~~~
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:282:14: note:   template argument deduction/substitution failed:
In file included from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:36,
                 from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
                 from /usr/include/boost/spirit/home/qi.hpp:21,
                 from /usr/include/boost/spirit/include/qi.hpp:16,
                 from Expression_handler.cpp:5:
/usr/include/boost/spirit/home/qi/reference.hpp:43:35: note:   cannot convert ‘first’ (type ‘__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >’) to type ‘__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&’
   43 |             return ref.get().parse(first, last, context, skipper, attr_);
      |                    ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
                 from /usr/include/boost/spirit/home/qi.hpp:21,
                 from /usr/include/boost/spirit/include/qi.hpp:16,
                 from Expression_handler.cpp:5:
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:333:14: note: candidate: ‘template<class Context, class Skipper, class Attribute, class Params> bool boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&, const Params&) const [with Context = Context; Skipper = Skipper; Attribute = Attribute; Params = Params; Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; T1 = client::ast::program(); T2 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>; T3 = boost::spirit::unused_type; T4 = boost::spirit::unused_type]’
  333 |         bool parse(Iterator& first, Iterator const& last
      |              ^~~~~
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:333:14: note:   template argument deduction/substitution failed:
In file included from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:36,
                 from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
                 from /usr/include/boost/spirit/home/qi.hpp:21,
                 from /usr/include/boost/spirit/include/qi.hpp:16,
                 from Expression_handler.cpp:5:
/usr/include/boost/spirit/home/qi/reference.hpp:43:35: note:   candidate expects 6 arguments, 5 provided
   43 |             return ref.get().parse(first, last, context, skipper, attr_);
      |                    ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Which other argument is it expecting and how do I fix this?


Solution

  • Your string isn't const, so the iterator type doesn't match the one you declared. The key message is:

    boost/spirit/home/qi/reference.hpp|43 col 35| note: cannot convert ‘first’ (type ‘__gnu_cxx::__normal_iterator<char*, __cxx11::basic_string<char> >’) to type ‘__gnu_cxx::__normal_iterator<const char*, __cxx11::basic_string<char> >&’

    Simply add const or const&:

    int Execute_expression(std::string const& str)
    

    Or don't use auto to declare f and l:

    std::string::const_iterator f = std::begin(str), l = std::end(str);
    

    Live On Coliru

    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/phoenix.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/variant.hpp>
    #include <iomanip>
    #include <list>
    #include <string>
    
    namespace client { namespace ast {
        struct signed_;
        struct program;
    
        using operand = boost::variant<unsigned int, bool,
                                       boost::recursive_wrapper<signed_>,
                                       boost::recursive_wrapper<program>>;
    
        struct signed_   { char sign; operand operand_;              };
        struct operation { char operator_; operand operand_;         };
        struct program   { operand first; std::list<operation> rest; };
    }} // namespace client::ast
    
    BOOST_FUSION_ADAPT_STRUCT(client::ast::signed_, sign, operand_)
    BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, operator_, operand_)
    BOOST_FUSION_ADAPT_STRUCT(client::ast::program, first, rest)
    
    namespace client { namespace ast
    {
        ///////////////////////////////////////////////////////////////////////////
        //  The AST evaluator
        ///////////////////////////////////////////////////////////////////////////
        struct eval {
            using result_type = int;
    
            int operator()(auto const& n) const { return n; }
    
            int operator()(operation const& x, int lhs) const {
                int rhs = boost::apply_visitor(*this, x.operand_);
                switch (x.operator_) {
                case '+': return lhs + rhs;
                case '-': return lhs - rhs;
                case '*': return lhs * rhs;
                case '/': return lhs / rhs;
                case '|': return lhs | rhs;
                case '&': return lhs & rhs;
                }
                BOOST_ASSERT(0);
                return 0;
            }
    
            int operator()(signed_ const& x) const {
                int rhs = boost::apply_visitor(*this, x.operand_);
                switch (x.sign) {
                case '-': return -rhs;
                case '+': return +rhs;
                case '!': return !rhs;
                }
                BOOST_ASSERT(0);
                return 0;
            }
    
            int operator()(program const& x) const {
                int state = boost::apply_visitor(*this, x.first);
                for (auto& oper : x.rest) {
                    state = (*this)(oper, state);
                }
                return state;
            }
        };
    }} // namespace client::ast
    
    namespace client
    {
        namespace qi = boost::spirit::qi;
    
        template <typename Iterator>
        struct calculator : qi::grammar<Iterator, ast::program(), qi::space_type> {
            calculator() : calculator::base_type(expression) {
                using namespace qi::labels;
    
                expression = term >> *(qi::char_("-+|&") >> term);
                term       = factor >> *(qi::char_("*/!") >> factor);
                str        = '"' >> *~qi::char_('"') >> '"';
                str_eq     = (str >> "==" >> str)[_val = (_1 == _2)];
                str_neq    = (str >> "!=" >> str)[_val = (_1 != _2)];
                factor     = str_neq | str_eq | qi::uint_ | qi::bool_ |
                    qi::char_("-+!") >> factor | '(' >> expression >> ')';
            }
    
          private:
            qi::rule<Iterator, ast::program(), qi::space_type> expression, term;
            qi::rule<Iterator, ast::operand(), qi::space_type> factor, str_eq, str_neq;
            qi::rule<Iterator, std::string()>                  str;
        };
    }
    
    struct Expression_handler {
        static int Execute_expression(std::string const& str) {
            using iterator_type = std::string::const_iterator;
            using calculator    = client::calculator<iterator_type>;
            namespace ast = client::ast;
    
            calculator const calc; // Our grammar
            ast::eval const  eval; // Evaluates the program
            ast::program     program;
    
            auto f(std::begin(str)), l(std::end(str));
            bool r = phrase_parse(f, l, calc, client::qi::space, program);
    
            if (r && f == l) {
                std::cout << "Result: " << quoted(str, '\'') << " -> " << eval(program)
                          << std::endl;
                return eval(program);
            } else {
                std::cout << "Failed at: " << quoted(std::string(f, l), '\'') << "\n";
            }
    
            return 0;
        };
    };
    
    int main(){
        Expression_handler exp;
        exp.Execute_expression(R"("abc" == "abc")");
        exp.Execute_expression(R"("abc" != "abc")");
        exp.Execute_expression(R"("abc" == "cba")");
        exp.Execute_expression(R"("abc" != "cba")");
    }
    

    Prints

    Result: '"abc" == "abc"' -> 1
    Result: '"abc" != "abc"' -> 0
    Result: '"abc" == "cba"' -> 0
    Result: '"abc" != "cba"' -> 1