Compiling this simple Spirit grammar results in what seems to be a single error (despite the huge error message), possibly having to do with my skipper or some other template parameter that I have mistakenly used.
I experimented with a variety of attribute definitions for the grammar and the start rule, but it had no effect on the error.
#include <string>
#include "/usr/local/boost/include/boost/spirit/include/qi.hpp"
#include "/usr/local/boost/include/boost/spirit/include/qi_symbols.hpp"
using std::string;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
boost::spirit::qi::rule<string::const_iterator>
skipper_rule = qi::space | '#' >> *(qi::char_ - qi::eol) >> qi::eol;
typedef BOOST_TYPEOF(skipper_rule) skipper_T;
template <typename Iterator, typename Skipper>
class Grammar1 : boost::spirit::qi::grammar<Iterator, Skipper>
{
public:
typedef boost::spirit::qi::rule<Iterator, Skipper> rule_nil_T;
typedef boost::spirit::qi::rule<Iterator, string()> rule_str_T;
rule_nil_T under = qi::char_('_');
rule_nil_T dot = qi::char_('.');
rule_nil_T star = qi::char_('*');
rule_str_T file_name_star = qi::lexeme [ (qi::alpha | under) >> *(qi::alnum | under) >> dot >> star ];
rule_nil_T start = +file_name_star;
Grammar1(void) : Grammar1::base_type(start) { };
~Grammar1(void) { };
void parseInputFile(Iterator itr, Iterator itr_end)
{
bool b;
bool r = phrase_parse(itr, itr_end, start, skipper_rule);
if (r && itr == itr_end)
{
std::cout << "Parsing succeeded\n";
} else
{
string rest(itr, itr_end);
std::cout << "stopped at: \": " << rest << "\"\n";
}
}
};
int main(int argc, char **argv)
{
Grammar1<string::const_iterator, skipper_T> g;
string input("input1.* input2.*");
g.parseInputFile(input.cbegin(), input.cend());
}
The important parts of the error message seem to be:
/usr/local/boost/include/boost/spirit/home/qi/nonterminal/rule.hpp:304:17:
error: no match for call to ‘(const function_type *REMOVED*)’
if (f(first, last, context, skipper))
^
The comment at line 304 in the above-mentioned Spirit file states that I
may be using an incompatible skipper, but I've tried the much simpler ascii::space_type skipper to no avail.
The rest of the error message:
usr/local/boost/include/boost/function/function_template.hpp:1048:7: note:
candidate is:
class function<BOOST_FUNCTION_PARTIAL_SPEC>
^
/usr/local/boost/include/boost/function/function_template.hpp:758:17: note:
boost::function4<R, T1, T2, T3, T4>:: *REMOVED*
result_type operator()(BOOST_FUNCTION_PARMS) const
^
/usr/local/boost/include/boost/function/function_template.hpp:758:17: note:
no known conversion for argument 4 from ‘const
boost::spirit::unused_type’ to ‘const boost::spirit::qi::reference<const
boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*,
std::basic_string<char> > > >&’
Any idea what I am doing wrong? My compiler is gcc 4.8.5.
You are trying to use rule_nil_T rule which has skipper inside rule_str_T rule which does not. For internal reasons Spirit can only lose skipper when calling a rule. The problem can be easily solved by inlining under, dot, and star at use site.
Note: a rule call in Qi has an overhead. Creating a rule for such a simple parser and then using it inside repeating parser is not a good idea.
Hint: you can utilize raw directive and use literal parsers inside it to get more performant and cleaner to look parser. Consider this: qi::lexeme[qi::raw[(qi::alpha | '_') >> qi::alnum % '.'] >> ".*"].
Related
I am trying to use auto-rule deduction to assign into a tuple of three elements. The second type is a string and the third is a vector of string (the type of the first parameter is not so important for this example).
I am struggling to understand why the following code does not compile.
#include <boost/fusion/include/std_tuple.hpp>
#include <boost/spirit/include/qi.hpp>
using namespace boost::spirit::qi;
using iterator = std::string::const_iterator;
void qi_compile_test1()
{
rule<iterator, std::string(), space_type> r_s1, r_s2;
rule<iterator, std::vector<std::string>(), space_type> r_vec_s1, r_vec_s2;
rule<iterator, std::tuple<float, std::string, std::vector<std::string>>(), space_type> r;
r %=
float_ >
((r_s1 > r_vec_s1) |
(r_s2 > r_vec_s2));
}
The error is as follows (The function insert is called on a string with a string as the second parameter):
/opt/wandbox/boost-1.68.0/clang-6.0.1/include/boost/spirit/home/support/container.hpp:292:15: error: no matching member function for call to 'insert'
c.insert(c.end(), val);
~~^~~~~~
/opt/wandbox/boost-1.68.0/clang-6.0.1/include/boost/spirit/home/support/container.hpp:354:51: note: in instantiation of member function 'boost::spirit::traits::push_back_container<std::__1::basic_string<char>, std::__1::basic_string<char>, void>::call' requested here
return push_back_container<Container, T>::call(c, val);
In short, the following rule is defined :
A > ((B > C) | (B > C))
I suspect the error comes from the fact that C is the same as vector < B > which can be resumed as :
A > ((B > vector<B>) | (B > vector<B))
Based on the compound attributes rules, I assumes the error comes from the following resolution :
A > (vector<B> | vector<B>) a : A, b: vector<A> --> (a > b): vector<A>
A > vector<B> a : A, b: A --> (a | b): A
Since the synthesized attribute is a tuple of three elements, I expect the auto-rule to keep the three different types in sequence:
A > ((B > vector<B>) | (B > vector<B))
A > (B > vector<B>) a : A, b: A --> (a | b): A
What I don't understand, is that the following code compiles:
void qi_compile_test2()
{
rule<iterator, int(), space_type> r_int1, r_int2;
rule<iterator, std::vector<int>(), space_type> r_vec_int1, r_vec_int2;
rule<iterator, std::tuple<float, int, std::vector<int>>(), space_type> r;
r %=
float_ >
((r_int1 > r_vec_int1) |
(r_int2 > r_vec_int2));
}
The only difference is the replacement of the type B from std::string to int. I suppose this simplifies things because std::string is a container but not the 'int' type. I don't see what would this change cause in this scenario.
Additionally, by taking back the first formula and removing the first parameter of the tuple, we are able to compile (still keeping the alternative function and the same order of sequence with string involved).
void qi_compile_test3()
{
rule<iterator, std::string(), space_type> r_s1, r_s2;
rule<iterator, std::vector<std::string>(), space_type> r_vec_s1, r_vec_s2;
rule<iterator, std::tuple<std::string, std::vector<std::string>>(), space_type> r;
r %=
((r_s1 > r_vec_s1) |
(r_s2 > r_vec_s2));
}
I would have loved to simplify the equation to the following but I don't think it can be accomplished without usage of semantic actions with lambdas to flatten the tuple.
void qi_compile_test4()
{
rule<iterator, std::tuple<std::string, std::vector<std::string>>(), space_type> r_p1, r_p2;
rule<iterator, std::tuple<float, std::string, std::vector<std::string>>(), space_type> r;
r %=
float_ >
(r_p1 | r_p2 );
}
This code can be compiled here:
https://wandbox.org/permlink/iJLRz1TKMK2pWMVb
What I am trying to accomplish here is to be able to have the last two element of the tuple being dependent of each other. I want to keep the sequence of two elements but separated with an alternative function. Removing the alternative function and keeping only one of the two statements compiles but removes the desired logic.
I want to avoid using semantic action because in my real case scenario my tuple contains much more elements (struct enumerated with fusion) because once I used semantic actions to assign the proper field in the tuple I had to assign each one of them. Anybody has any suggestions on how to resolve this?
Thanks
Unfortunately, Spirit (both Qi and X3) only concats tuples of sequence parsers1. While it seems to be natural to expect a flat representations of the parser, Spirit does not go so deep. It acts very straightforward2 and fix will require a huge refactor of attribute manipulation.
However, unaries are transparent so float_ >> lexeme[int_ >> char_] have tuple<float, int, char> result.
#include <boost/spirit/home/qi.hpp>
#include <boost/core/demangle.hpp>
#include <typeinfo>
namespace qi = boost::spirit::qi;
template <typename Iterator = char const*, typename Context = qi::unused_type, typename Parser>
void print_parser_attr_type(Parser const& p)
{
using attr_type = typename decltype(boost::spirit::compile<qi::domain>(p))::template attribute<Context, Iterator>::type;
std::cout << boost::core::demangle(typeid(attr_type).name()) << '\n';
}
int main()
{
print_parser_attr_type(qi::float_ >> ((qi::int_ >> qi::char_) | (qi::int_ >> qi::char_)));
}
Output:
boost::fusion::vector<float, boost::fusion::vector<int, char> >
It may surprise you that float_ >> no_case[int_ >> char_] will perfectly parse into tuple<float, tuple<int, char>> but float_ >> (int_ >> char_) will not.
I am trying to learn to parse using boost.spirit parser. I am using Windows 8.1 with VisualStudio 2015. I have installed and successfuly compiled the test program from the boost.spirit installation document so my installation of boost seems fine.
I have been following the tutorial on the boost.org on using the paser. I created the following code to parse a double:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
template <typename Iterator>
bool myParser(Iterator first, Iterator last) {
using qi::double_;
qi::rule<Iterator, double(), ascii::space_type> myrule;
myrule %= double_;
bool r = parse(first, last, myrule, ascii::space);
return r;
}
int main() {
std::string dstr = std::string("2.1");
bool r = myParser(dstr.begin(), dstr.end());
return 0;
}
When I compile I get the following error message from rule.hpp line 304:
'bool boost::function4<R,T0,T1,T2,T3>::operator ()(T0,T1,T2,T3) const': cannot convert 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::ascii>> '
Any help will be greatly appreciated. Thanks
As is mentioned by jv_ in the link, you are using a skipper, but not calling the phrase_parse API which would accept a skipper. So, the parse call tries to bind the ascii::space parser to the first exposed attribute (which is a double).
That assignment fails.
In all likelihood you don't want a skipper for this simple grammar, and I'd write:
#include <boost/spirit/include/qi.hpp>
template <typename Iterator> bool myParser(Iterator first, Iterator last) {
namespace qi = boost::spirit::qi;
return qi::parse(first, last, qi::double_ >> qi::eoi);
}
int main() {
std::string const dstr("2.1");
bool r = myParser(dstr.begin(), dstr.end());
return r?0:1;
}
Note the qi::eol which checks that all of the input was consumed.
My AST node is struct
struct node_type
{
type::code type_id;
boost::variant<int, std::string> value;
};
Adapter and grammar:
BOOST_FUSION_ADAPT_STRUCT(
client::node_type,
(client::type::code, id)
(boost::variant<int, std::string>, value)
)
namespace client
{
struct or_op
{
node_type left, right;
};
namespace type
{
enum code{NUMBER, STRING, BOOL};
}
// Grammar
template <typename Iterator>
struct pair_grammar : qi::grammar<
Iterator,
node_type(), // Grammar generates node_type
ascii::space_type
>
{
pair_grammar() : pair_grammar::base_type(
expr // main 'rule'
)
{
using qi::lit;
using qi::lexeme;
using ascii::char_;
using qi::int_;
using ascii::string;
using namespace qi::labels;
using phoenix::at_c;
using phoenix::push_back;
expr = int_[at_c<0>(qi::_val) = 0, at_c<1>(qi::_val) = qi::_1];
}
qi::rule<Iterator, node_type(), ascii::space_type> expr;
};
}
The code above doesn't compile. First of all i get warning
error: macro "BOOST_FUSION_ADAPT_STRUCT_FILLER_0" passed 3 arguments, but takes just 2
and then a lot of errors, starting with
qi/nonterminal/rule.hpp:303:17: error: no match for call to '(const function_type {aka const boost::function<bool(const char*&, const char* const&, boost::spirit::context<boost::fusion::cons<client::node_type&, boost::fusion::nil>, boost::fusion::vector0<> >&, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >&)>}) (const char*&, const char* const&, boost::spirit::qi::rule<const char*, client::node_type(), 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::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>::context_type&, const boost::spirit::unused_type&)'
What am I doing wrong? Thanks.
EDIT:
Macro warning appeared because of ',' in macro. Typedef solved problem
typedef boost::variant<int, std::string> node_value_t;
struct node_type
{
type::code type_id;
node_value_t value;
};
BOOST_FUSION_ADAPT_STRUCT(
client::node_type,
(client::type::code, type_id)
(client::node_value_t, value)
)
But code still doesn't compile. I also tried
number = int_[qi::_val = phoenix::construct<node_type>(type::NUMBER, qi::_1)];
But that didnt help.
EDIT 2: Simplified original rule. Still no success.
A number of issues here.
The message
error: macro "BOOST_FUSION_ADAPT_STRUCT_FILLER_0" passed 3 arguments, but takes just 2
occurs because of the , in the template arguments for variant<>. You can fix it using a typedef:
namespace client {
namespace type { enum code{NUMBER, STRING, BOOL}; }
struct node_type {
type::code type_id;
typedef boost::variant<int, std::string> vt_type;
vt_type value;
};
}
BOOST_FUSION_ADAPT_STRUCT(
client::node_type,
(client::type::code, type_id)
(client::node_type::vt_type, value)
)
You were assigning an int to the attribute of enum type. The implicit conversion is not allowed. Instead, supply the required enum type:
expr = int_ [at_c<0>(qi::_val) = client::type::NUMBER, at_c<1>(qi::_val) = qi::_1];
At this point, everything compiles and works: Live On Coliru
using namespace client;
pair_grammar<std::string::const_iterator> grammar;
std::string const input = "123";
auto f(input.begin()), l(input.end());
node_type node;
bool ok = qi::phrase_parse(f, l, grammar, ascii::space, node);
assert(ok);
assert(f == l);
assert(node.type_id == type::NUMBER);
assert(node.value == node_type::vt_type(123));
I don't think this solution is optimal, though.
Consider using the Spirit directives where possible, and staying away from Semantic actions that uglify the grammar, are a frequent source of errors/UB, make compile times even longer... [1]:
pair_grammar() : pair_grammar::base_type(expr)
{
expr = qi::attr(client::type::NUMBER) >> qi::int_;
}
See that Live On Coliru too:
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = qi::ascii;
namespace client
{
namespace type
{
enum code{NUMBER, STRING, BOOL};
}
struct node_type
{
type::code type_id;
typedef boost::variant<int, std::string> vt_type;
vt_type value;
};
}
/*Adapter and grammar:*/
BOOST_FUSION_ADAPT_STRUCT(
client::node_type,
(client::type::code, type_id)
(client::node_type::vt_type, value)
)
namespace client
{
struct or_op
{
node_type left, right;
};
// Grammar
template <typename Iterator>
struct pair_grammar : qi::grammar<
Iterator,
node_type(), // Grammar generates node_type
ascii::space_type
>
{
pair_grammar() : pair_grammar::base_type(expr)
{
expr = qi::attr(client::type::NUMBER) >> qi::int_;
}
qi::rule<Iterator, node_type(), ascii::space_type> expr;
};
}
int main()
{
using namespace client;
pair_grammar<std::string::const_iterator> grammar;
std::string const input = "123";
auto f(input.begin()), l(input.end());
node_type node;
bool ok = qi::phrase_parse(f, l, grammar, ascii::space, node);
assert(ok);
assert(f == l);
assert(node.type_id == type::NUMBER);
assert(node.value == node_type::vt_type(123));
}
[1] Boost Spirit: "Semantic actions are evil"?
I'm trying to compile a simple grammar using Boost.Spirit. I'm using g++ 4.7.0 and boost 1.49.0-1.1 on Arch Linux x86_64.
The eventual goal here is an assembler. There will be multiple operands with one class each. All the operand types together are stored in a boost::variant type.
I've had success compiling this sample up to the direct rule when it is also the base_type of the grammar, but introducing the operand rule (and making it the base type) causes g++ 4.7.0 to complain that:
example.cpp:61:7: required from ‘Grammar<Iterator>::Grammar() [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >]’
example.cpp:76:21: required from here
/usr/include/boost/spirit/home/qi/detail/attributes.hpp:23:63: error: no matching function for call to ‘DirectOperand::DirectOperand()’
/usr/include/boost/spirit/home/qi/detail/attributes.hpp:23:63: note: candidates are:
example.cpp:20:12: note: DirectOperand::DirectOperand(const DirectValue&)
example.cpp:20:12: note: candidate expects 1 argument, 0 provided
example.cpp:16:7: note: DirectOperand::DirectOperand(const DirectOperand&)
example.cpp:16:7: note: candidate expects 1 argument, 0 provided
I don't understand why it should be looking for a default constructor for DirectOperand, since the semantic action should call it with the constructor.
I've tried lots of variations, including
operand = directOp[_val = _1];
and even writing a helper function to "force" the type, like:
static Operand makeDirectOperand( const DirectOperand& op ) { return op; }
// ...
operand = directOp[&makeDirectOp];
but no matter what I do, it complains about the missing default constructor.
When I actually defined a zero-argument constructor, I found that it compiled, but that that DirectOperand::value_ never changed from the default value I assigned.
Here's the code. It's as short as I could make it.
#include <cstdint>
#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_uint.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/variant.hpp>
typedef std::uint16_t DataWord;
typedef boost::variant<std::string, DataWord> DirectValue;
class DirectOperand {
private:
DirectValue value_;
public:
explicit DirectOperand( const DirectValue& value ) :
value_( value ) {}
const DirectValue& value() const { return value_; }
};
// For example purposes, but there will be multiple operand types
// here.
typedef boost::variant<DirectOperand> Operand;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
template <typename Iterator>
struct Grammar : qi::grammar<Iterator, Operand(), ascii::space_type> {
Grammar() : Grammar::base_type( operand ) {
using qi::lexeme;
using ascii::char_;
using qi::uint_parser;
using namespace qi::labels;
uint_parser<DataWord, 16, 1, 4> uhex_p;
uint_parser<DataWord, 10, 1, 5> uint_p;
word =
char_( "a-zA-Z._" ) [_val += _1]
>> *char_( "a-zA-Z0-9._" ) [_val += _1]
;
number = (
"0x" >> uhex_p
| uint_p
)
[_val = _1]
;
direct %= ( word | number );
directOp %= direct;
// This would be ( directOp | indirectOp | etc)
operand %= directOp;
}
qi::rule<Iterator, DataWord(), ascii::space_type> number;
qi::rule<Iterator, std::string()> word;
qi::rule<Iterator, DirectValue(), ascii::space_type> direct;
qi::rule<Iterator, DirectOperand(), ascii::space_type> directOp;
qi::rule<Iterator, Operand(), ascii::space_type> operand;
};
int main() {
std::string line;
typedef std::string::iterator iterator_type;
typedef Grammar<iterator_type> Grammar;
Grammar grammar {};
}
I believe that the instantiation of the attribute qi::rule (directOp here) requires a default constructor.
If you are loathe to include a default constructor in DirectOperand, you could try wrapping it up in a boost::optional for the purpose of deferring initialization.
In boost::spirit, I am trying to use the +(...) syntax to match one or more strings, like here:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
template <typename Iterator>
bool parse(Iterator first, Iterator last)
{
using qi::char_;
qi::rule< Iterator, std::string(), ascii::space_type > text;
qi::rule< Iterator, std::string() > myword;
text = '"' >> +( myword ) >> '"'; // ERROR!
myword = +char_;
bool r = qi::phrase_parse(first, last, text, ascii::space);
if (first != last)
return false;
return r;
}
}
But I'm getting the following error:
foo.cpp:20: instantiated from 'bool client::parse
boost/spirit/home/qi/detail/assign_to.hpp:109: error: \
invalid static_cast from type \
'const std::basic_string<char, std::char_traits<char>, std::allocator<char> >' \
to type 'char'
Does anyone know what I'm doing wrong?
Your code compiles just fine with Boost V1.47 (Spirit V2.5). In this version of Spirit the attribute handling has been completely rewritten which fixes this issue (and a lot of other issues).