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?
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);
#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