I'm trying for the first time to write my own grammar with spirit::qi, and I don't succeed in integrating a skip parser that should ignore C style comments as well as whitespaces. Here's my source file (I reduced the grammar to just a few rules, in order to be more concise:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "formulaNode.h"
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
using FormulaNodePtr = compiler::FormulaNode*;
using ExpressionPtr = compiler::Expression*;
template <typename Iterator>
struct ArithmeticGrammar : qi::grammar<Iterator, FormulaNodePtr(), qi::space_type> {
ArithmeticGrammar() : ArithmeticGrammar::base_type(formula, "Formula") {
using namespace qi;
localSkipper = qi::space | ("/*" >> *(qi::char_ - "*/") >> "*/");
formula = unsignedFactor [_val = _1];
unsignedFactor =
numericConstant [_val = phoenix::new_<compiler::Constant>(_1)];
numericConstant = qi::as_string[
qi::lexeme[ -qi::char_('-') >>
(+qi::digit >> -(qi::char_('.') >> +qi::digit)) >>
-(qi::char_('e') >> -qi::char_('-') >> +qi::digit)
]];
}
qi::rule<Iterator, FormulaNodePtr(), qi::space_type> formula;
qi::rule<Iterator, ExpressionPtr(), qi::space_type> unsignedFactor;
qi::rule<Iterator, std::string(), qi::space_type> numericConstant;
qi::rule<Iterator, qi::space_type> localSkipper;
};
// Parsing function
bool parseExpression(const std::string& input, FormulaNodePtr& result)
{
using Iterator = std::string::const_iterator;
ArithmeticGrammar<Iterator> grammar;
Iterator it = input.begin();
Iterator end = input.end();
bool success = qi::phrase_parse(it, end, grammar, grammar.localSkipper, result);
if (!success || it != end)
{
if (result == nullptr) {
std::cout << "Parsing failed\n";
}
delete result;
result = nullptr;
}
return success && (it == end);
}
int main()
{
std::string input;
std::cout << "Enter input: ";
std::getline(std::cin, input);
while (!input.empty()) {
FormulaNodePtr result = nullptr;
if (parseExpression(input, result))
{
std::cout << "Parsed successfully. Expression tree:" << std::endl;
result->print(std::cout, 0);
delete result;
} else {
std::cout << "Parsing failed. Please enter a valid expression." << std::endl;
}
std::getline(std::cin, input);
}
return EXIT_SUCCESS;
}
The way I use the skipper parser is taken from various internet examples. Yet when I try to compile this, I get this error message - can someone explain that to me?
g++ -std=c++11 -Wall -Wextra -pedantic Main2.cpp -o parser
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 Main2.cpp:1:
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp: In instantiation of ‘bool boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Context = boost::spirit::context<boost::fusion::cons<compiler::FormulaNode*&, boost::fusion::nil_>, boost::spirit::locals<> >; Skipper = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, 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, boost::spirit::unused_type> >; Attribute = compiler::FormulaNode*; Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; T1 = compiler::FormulaNode*(); 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]’:
/usr/include/boost/spirit/home/qi/reference.hpp:43:72: required from ‘bool boost::spirit::qi::reference<Subject>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; Context = boost::spirit::context<boost::fusion::cons<compiler::FormulaNode*&, boost::fusion::nil_>, boost::spirit::locals<> >; Skipper = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, 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, boost::spirit::unused_type> >; Attribute = compiler::FormulaNode*; Subject = const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, compiler::FormulaNode*(), 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:164:13: 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<const char*, std::__cxx11::basic_string<char> >; Expr = ArithmeticGrammar<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > >; Skipper = boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, 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, boost::spirit::unused_type>; Attr = compiler::FormulaNode*]’
/usr/include/boost/spirit/home/qi/parse.hpp:197:32: required from ‘bool boost::spirit::qi::phrase_parse(Iterator&, Iterator, const Expr&, const Skipper&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; Expr = ArithmeticGrammar<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > >; Skipper = boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, 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, boost::spirit::unused_type>; Attr = compiler::FormulaNode*]’
Main2.cpp:49:83: required from here
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:304:17: error: no match for call to ‘(const function_type {aka const boost::function<bool(__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::spirit::context<boost::fusion::cons<compiler::FormulaNode*&, boost::fusion::nil_>, boost::fusion::vector<> >&, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&)>}) (__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, compiler::FormulaNode*(), 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>::context_type&, const boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, 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, boost::spirit::unused_type> >&)’
if (f(first, last, context, skipper))
^~
In file included from /usr/include/boost/function/detail/maybe_include.hpp:43,
from /usr/include/boost/function/detail/function_iterate.hpp:14,
from /usr/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:67,
from /usr/include/boost/function.hpp:70,
from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:16,
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 Main2.cpp:1:
/usr/include/boost/function/function_template.hpp:754:17: note: candidate: ‘boost::function4<R, T1, T2, T3, T4>::result_type boost::function4<R, T1, T2, T3, T4>::operator()(T0, T1, T2, T3) const [with R = bool; T0 = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&; T1 = const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&; T2 = boost::spirit::context<boost::fusion::cons<compiler::FormulaNode*&, boost::fusion::nil_>, boost::fusion::vector<> >&; T3 = const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&; boost::function4<R, T1, T2, T3, T4>::result_type = bool]’
result_type operator()(BOOST_FUNCTION_PARMS) const
^~~~~~~~
/usr/include/boost/function/function_template.hpp:754:17: note: no known conversion for argument 4 from ‘const boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, 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, boost::spirit::unused_type> >’ to ‘const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&’
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 Main2.cpp:1:
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp: In instantiation of ‘bool boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Context = const boost::spirit::unused_type; Skipper = boost::spirit::unused_type; Attribute = const boost::spirit::unused_type; Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; T1 = 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>; T2 = boost::spirit::unused_type; T3 = boost::spirit::unused_type; T4 = boost::spirit::unused_type]’:
/usr/include/boost/spirit/home/qi/reference.hpp:43:72: required from ‘bool boost::spirit::qi::reference<Subject>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; Context = const boost::spirit::unused_type; Skipper = boost::spirit::unused_type; Attribute = const boost::spirit::unused_type; Subject = const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, 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, boost::spirit::unused_type>]’
/usr/include/boost/spirit/home/qi/skip_over.hpp:27:30: required from ‘void boost::spirit::qi::skip_over(Iterator&, const Iterator&, const T&) [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, 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, boost::spirit::unused_type> >]’
/usr/include/boost/spirit/home/qi/parse.hpp:169:26: 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<const char*, std::__cxx11::basic_string<char> >; Expr = ArithmeticGrammar<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > >; Skipper = boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, 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, boost::spirit::unused_type>; Attr = compiler::FormulaNode*]’
/usr/include/boost/spirit/home/qi/parse.hpp:197:32: required from ‘bool boost::spirit::qi::phrase_parse(Iterator&, Iterator, const Expr&, const Skipper&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; Expr = ArithmeticGrammar<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > >; Skipper = boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, 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, boost::spirit::unused_type>; Attr = compiler::FormulaNode*]’
Main2.cpp:49:83: required from here
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:304:17: error: no match for call to ‘(const function_type {aka const boost::function<bool(__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type&, boost::fusion::nil_>, boost::fusion::vector<> >&, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&)>}) (__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, 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, boost::spirit::unused_type>::context_type&, const boost::spirit::unused_type&)’
if (f(first, last, context, skipper))
^~
In file included from /usr/include/boost/function/detail/maybe_include.hpp:43,
from /usr/include/boost/function/detail/function_iterate.hpp:14,
from /usr/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:67,
from /usr/include/boost/function.hpp:70,
from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:16,
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 Main2.cpp:1:
/usr/include/boost/function/function_template.hpp:754:17: note: candidate: ‘boost::function4<R, T1, T2, T3, T4>::result_type boost::function4<R, T1, T2, T3, T4>::operator()(T0, T1, T2, T3) const [with R = bool; T0 = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&; T1 = const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&; T2 = boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type&, boost::fusion::nil_>, boost::fusion::vector<> >&; T3 = const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&; boost::function4<R, T1, T2, T3, T4>::result_type = bool]’
result_type operator()(BOOST_FUNCTION_PARMS) const
^~~~~~~~
/usr/include/boost/function/function_template.hpp:754:17: note: no known conversion for argument 4 from ‘const boost::spirit::unused_type’ to ‘const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&’
When I replace "grammar.localSkipper" with "qi::space", it compiles without error. But of course, I don't have comment skipping.
First, don't use raw pointers in semantic actions or as attribute types:
Second, the skipper MUST be compatible with the rule's declared skipper type. See Boost spirit skipper issues. Most likely you will come across one of these asserts in the diagnostics:
BOOST_STATIC_ASSERT_MSG(
(is_convertible<Skipper const&, skipper_type>::value),
"The passed skipper is not compatible/convertible to one "
"that the rule was instantiated with");
In your code, the declared skipper type is qi::space_type
BUT you try to pass localSkipper
. Fixing the incorrect declaration of it without its own skipper:
qi::rule<Iterator> localSkipper;
Now fix the rules:
using Skipper = qi::rule<Iterator>;
qi::rule<Iterator, FormulaNodePtr(), Skipper> formula;
qi::rule<Iterator, ExpressionPtr(), Skipper> unsignedFactor;
// lexemes
qi::rule<Iterator, std::string()> numericConstant;
Skipper localSkipper;
NOTE The
numericConstant
drops the skipper so it is implicitly a lexeme (see again above link). That meansqi::lexeme[]
can be avoided.Consider replacing that rule with just
numericConstant = qi::raw[ qi::double_ ];
Even better, do not expose the skipper at all, since it's not a "user-serviceable part":
private:
using Skipper = qi::rule<Iterator>;
qi::rule<Iterator, FormulaNodePtr(), Skipper> formula;
qi::rule<Iterator, ExpressionPtr(), Skipper> unsignedFactor;
// lexemes
qi::rule<Iterator, std::string()> numericConstant;
Skipper localSkipper;
Now to avoid dangling references you have to very subtly alias the localSkipper in qi::skip
:
start = qi::skip(localSkipper.alias())[formula];
Catering for all of the above (and missing code including a pointer conversion):
#define BOOST_ALLOW_DEPRECATED_HEADERS
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
namespace compiler {
struct Expression {
virtual ~Expression() = default;
virtual void print(std::ostream& os, int indent) const = 0;
};
struct Constant : Expression {
Constant(const std::string& value) : value(value) {}
void print(std::ostream& os, int indent) const override {
os << std::string(indent, ' ') << "Constant: " << value << std::endl;
}
std::string value;
};
struct FormulaNode {
FormulaNode(Expression* expression) : expression(expression) {}
void print(std::ostream& os, int indent) const { expression->print(os, indent); }
std::unique_ptr<Expression> expression;
};
}
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
using FormulaNodePtr = compiler::FormulaNode*;
using ExpressionPtr = compiler::Expression*;
template <typename Iterator> //
struct ArithmeticGrammar : qi::grammar<Iterator, FormulaNodePtr()> {
ArithmeticGrammar() : ArithmeticGrammar::base_type(start) {
using namespace qi::labels;
start = qi::skip(localSkipper.alias())[formula];
formula = unsignedFactor[_val = phoenix::new_<compiler::FormulaNode>(_1)];
unsignedFactor = numericConstant[_val = phoenix::new_<compiler::Constant>(_1)];
numericConstant = qi::as_string[
-qi::char_('-') >>
(+qi::digit >> -(qi::char_('.') >> +qi::digit)) >>
-(qi::char_('e') >> -qi::char_('-') >> +qi::digit)
];
}
private:
using Skipper = qi::rule<Iterator>;
qi::rule<Iterator, FormulaNodePtr()> start;
qi::rule<Iterator, FormulaNodePtr(), Skipper> formula;
qi::rule<Iterator, ExpressionPtr(), Skipper> unsignedFactor;
// lexemes
qi::rule<Iterator, std::string()> numericConstant;
Skipper localSkipper = qi::space | "/*" >> *(qi::char_ - "*/") >> "*/";
};
// Parsing function
bool parseExpression(std::string const& input, FormulaNodePtr& result) {
using Iterator = std::string::const_iterator;
static ArithmeticGrammar<Iterator> const grammar;
if (!parse(begin(input), end(input), grammar >> qi::eoi, result)) {
delete result;
result = nullptr;
return false;
}
return true;
}
int main() {
std::cout << "Enter input: ";
for (std::string input; getline(std::cin, input); std::cout<<"Enter input: ") {
if (input.empty())
break;
FormulaNodePtr result = nullptr;
if (parseExpression(input, result)) {
std::cout << "Parsed successfully. Expression tree:" << std::endl;
result->print(std::cout, 0);
delete result;
} else {
std::cout << "Parsing failed. Please enter a valid expression." << std::endl;
}
}
}
Printing e.g.