[Replaced code by a complete program and versions update]
The code below fails under Windows with the following message (sorry manualy translated from
French, I don't know how to force Visual C++ to work in English). This is
under Windows. It works under Linux. You can compile with
Linux: gcc LimaTokenizerParser.cpp -o LimaTokenizerPars -lboost_system-mt -lstdc++
Windows(fails): cl LimaTokenizerParser.cpp /Ic:\boost\path\include /EHsc
I use Boost 1.50 with Visual C++ 2010 under Windows and Boost 1.48
and gcc 4.6.3 under Linux.
The assertion in boost grammar.hpp is accompanied by this comment:
// If you see the assertion below failing then the start rule
// passed to the constructor of the grammar is not compatible with
// the grammar (i.e. it uses different template parameters).
but I don't understand how to understand it in my case...
Any idea ?
Regards,
Gaël
Code:
LimaTokenizerParser.cpp
#include "SpiritTokenizerParser.hpp"
#include <iostream>
#include <fstream>
void readStream(std::istream &is,std::string &dest)
{
while (is.good() && !is.eof())
{
const int bufferSize = 10240;
char buffer[bufferSize];
is.read(buffer,bufferSize);
const int size = is.gcount();
if (size>0)
dest.insert(dest.end(),buffer,buffer+size);
}
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Needs exactly one argument" << std::endl;
return 1;
}
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using ascii::space;
typedef std::string::const_iterator iterator_type;
typedef tokenizer<iterator_type> tokenizer;
// #ERROR DOES NOT WORK ON WINDOWS PORT
tokenizer tokenizer_parser;
std::string str;
std::ifstream file(argv[1]);
readStream(file, str);
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
tokenizer_automaton automaton;
bool r = false;
// #ERROR DOES NOT WORK ON WINDOWS PORT
r = phrase_parse(iter, end, tokenizer_parser, skipper, automaton);
if (r && iter == end)
{
std::cout << "Parsing succeeded: "<<automaton.size()<<" states" << std::endl;
}
else
{
std::string rest(iter, end);
std::cout << "Parsing failed. Stopped at: \": " << rest << "\"\n";
}
return 0;
}
SpiritTokenizerParser.hpp
#ifndef SPIRITTOKENIZERPARSER_HPP
#define SPIRITTOKENIZERPARSER_HPP
#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>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <string>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
#define skipper qi::space | ascii::char_('#') >> *(ascii::char_ - qi::eol) >> qi::eol
typedef BOOST_TYPEOF(skipper) skipper_type;
enum transitions {
STORE,
FLUSH,
TOKEN,
EXIT,
LTOKEN
};
typedef std::vector<std::string> tokenizer_precondition;
typedef std::vector<std::string> tokenizer_postcondition;
struct tokenizer_transition
{
std::vector<tokenizer_precondition> preconditions;
std::vector<std::string> event;
std::vector<tokenizer_postcondition> postconditions;
transitions transition;
std::string target;
std::vector<std::string> statuses;
};
struct tokenizer_state
{
std::string id;
std::vector<tokenizer_transition> transitions;
};
typedef std::vector<tokenizer_state> tokenizer_automaton;
BOOST_FUSION_ADAPT_STRUCT(
tokenizer_transition,
(std::vector<tokenizer_precondition>, preconditions)
(std::vector<std::string>, event)
(std::vector<tokenizer_postcondition>, postconditions)
(transitions, transition)
(std::string, target)
(std::vector<std::string>, statuses)
)
BOOST_FUSION_ADAPT_STRUCT(
tokenizer_state,
(std::string, id)
(std::vector<tokenizer_transition>, transitions)
)
using ascii::space_type;
template <typename Iterator>
struct tokenizer : qi::grammar<Iterator, tokenizer_automaton(), skipper_type>
{
struct transitionsymbol_ : qi::symbols<char, unsigned>
{
// > is +1 store
// / is +1 flush (forget the current token)
// = is +1 token
// ^ is exit
transitionsymbol_()
{
add
(">", STORE)
("/", FLUSH)
("=", TOKEN)
("^", EXIT)
;
}
} transitionsymbol;
tokenizer() : tokenizer::base_type(start)
{
using qi::alnum;
using qi::lexeme;
using ascii::char_;
start %= *state ;
state %= '(' >> identifier >> ')' >> '{' >> *transition >> '}';
transition %= '-' >> *precondition >> event >> *postcondition >> transitionsymbol >> identifier >> -('(' >> identifier % ',' >> ')');
identifier %= lexeme[+(alnum | char_('_'))];
precondition %= '[' >> (identifier % '|') >> ']';
event %= identifier % '|';
postcondition %= identifier % '|';
}
qi::rule<Iterator, tokenizer_automaton(), skipper_type> start;
qi::rule<Iterator, tokenizer_state(), skipper_type> state;
qi::rule<Iterator, tokenizer_transition(), skipper_type> transition;
qi::rule<Iterator, std::string(), skipper_type> identifier;
qi::rule<Iterator, std::vector<std::string>(), skipper_type> precondition;
qi::rule<Iterator, std::vector<std::string>(), skipper_type> event;
qi::rule<Iterator, std::vector<std::string>(), skipper_type> postcondition;
};
//]
#endif // SPIRITTOKENIZERPARSER_HPP
Messages:
LimaTokenizerParser.cpp
c:\Program Files\boost\boost_1_50\include\boost/spirit/home/qi/nonterminal/grammar.hpp(77) : error C2664: 'boost::mpl::assertion_failed'<FF>: can not convert parameter 1 from 'boost::mpl::failed ************(__cdecl boost::spirit::qi::grammar<Iterator,T1,T2>::{ctor}::incompatible_start_rule::* ***********)(boost::spirit::qi::rule<Iterator,T1,boost::spirit::qi::alternative<Elements>>)' en 'boost::mpl::assert<false>::type'
with
[
Iterator=iterator_type,
T1=tokenizer_automaton (void),
T2=skipper_type,
Elements=boost::fusion::cons<boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space,boost::spirit::char_encoding::standard>>,boost::fusion::cons<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,false,false>,boost::fusion::cons<boost::spirit::qi::kleene<boost::spirit::qi::difference<boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::char_,boost::spirit::char_encoding::ascii>>,boost::spirit::qi::eol_parser>>,boost::fusion::cons<boost::spirit::qi::eol_parser,boost::fusion::nil>>>>,boost::fusion::nil>>
]
No constructor could take the source type, or the constructor overload resolution was ambiguous
e:\projets\amose\sources\lima_linguisticprocessing\src\linguisticprocessing\core\flattokenizer\SpiritTokenizerParser.hpp(89)<FF>: see reference to function template instantiation 'boost::spirit::qi::grammar<Iterator,T1,T2>::grammar<Iterator,tokenizer_automaton(void),boost::spirit::qi::alternative<Elements>,boost::spirit::unused_type,boost::spirit::unused_type>(const boost::spirit::qi::rule<Iterator,T1,boost::spirit::qi::alternative<Elements>> &,const std::string &)' en cours de compilation
with
[
Iterator=iterator_type,
T1=tokenizer_automaton (void),
T2=skipper_type,
Elements=boost::fusion::cons<boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space,boost::spirit::char_encoding::standard>>,boost::fusion::cons<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,false,false>,boost::fusion::cons<boost::spirit::qi::kleene<boost::spirit::qi::difference<boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::char_,boost::spirit::char_encoding::ascii>>,boost::spirit::qi::eol_parser>>,boost::fusion::cons<boost::spirit::qi::eol_parser,boost::fusion::nil>>>>,boost::fusion::nil>>
]
TokenizerParser.hpp(88)<FF>: during the compilation of the member function ':: tokenizer tokenizer <iterator> (void)' model class
with
[
Iterator=iterator_type
]
LimaTokenizerParser.cpp(33)<FF>: see reference to class template instantiation 'tokenizer <iterator>' being compiled
with
[
Iterator=iterator_type
]
When posting to the Spirit list (yep, I saw this posted there), it is best to provide something minimal. Making something minimal helps a lot. It avoids the visual clutter that tends to waste time when understanding the code. It is just a matter of elimination anyway that anyone asking for support could do. Attached is what I mean by minimal. If I've seen something like this instead, it would have taken me just a few secs, instead of 10s of minutes to understand the problem.
Anyway, here's the code distilled and as minimal as it can be while still exhibiting the problem:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <string>
namespace qi = boost::spirit::qi;
typedef qi::space_type skipper_type;
template <typename Iterator>
struct tokenizer : qi::grammar<Iterator, skipper_type>
{
tokenizer() : tokenizer::base_type(start)
{
}
qi::rule<Iterator, skipper_type> start;
};
int main()
{
typedef std::string::const_iterator iterator_type;
typedef tokenizer<iterator_type> tokenizer;
// #ERROR DOES NOT WORK ON WINDOWS PORT
tokenizer tokenizer_parser;
return 0;
}
I notice that if I rename "skipper_type" to "skipper_type_" (note trailing underscore). Then it compiles fine! This leads me to believe that it is an MSVC bug.
Solution: just change the name skipper_type.
Related
I have a more complex rule, but this one will suffice for this question (I hope). Consider the rule:
result = double_ >> *(char_ > int_);
where result is declared in terms of a struct result in namespace ast:
qi::rule<Iterator, ast::result(), qi::space_type> result;
Then how does ast::result have to look like?
According to the boost::spirit docs (http://www.boost.org/doc/libs/develop/libs/spirit/doc/html/spirit/abstracts/attributes/compound_attributes.html), the attribute of
char_ > int_ is tuple<char, int> or std::pair<char, int>.
So, I tried:
namespace ast
{
using second_type = std::vector<std::pair<char, int>>;
struct result
{
double first;
second_type second;
};
} // namespace ast
in addition to
BOOST_FUSION_ADAPT_STRUCT(
ast::result,
(double, first),
(ast::second_type, second)
)
But this gives me the compile error:
error: no matching function for call to 'std::pair<char, int>::pair(const char&)'
This rule is simple, creating the AST struct that the result will be stored in should be simple too... but how?
Here is a complete test program with my attempt:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace ast
{
using second_type = std::vector<std::pair<char, int>>;
struct result
{
double first;
second_type second;
friend std::ostream& operator<<(std::ostream& os, result const& result);
};
std::ostream& operator<<(std::ostream& os, second_type::value_type val)
{
return os << val.first << ' ' << val.second;
}
std::ostream& operator<<(std::ostream& os, result const& result)
{
os << result.first;
for (auto& i : result.second)
os << ' ' << i;
return os;
}
} // namespace ast
BOOST_FUSION_ADAPT_STRUCT(
ast::result,
(double, first),
(ast::second_type, second)
)
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
template <typename Iterator>
class test_grammar : public qi::grammar<Iterator, ast::result(), qi::space_type>
{
private:
qi::rule<Iterator, ast::result(), qi::space_type> result;
public:
test_grammar() : test_grammar::base_type(result, "result_grammar")
{
using qi::double_;
using qi::char_;
using qi::int_;
result = double_ >> *(char_ > int_);
}
};
} // namespace client
int main()
{
std::string const input{"3.4 a 5 b 6 c 7"};
using iterator_type = std::string::const_iterator;
using test_grammar = client::test_grammar<iterator_type>;
namespace qi = boost::spirit::qi;
test_grammar program;
iterator_type iter{input.begin()};
iterator_type const end{input.end()};
ast::result out;
bool r = qi::phrase_parse(iter, end, program, qi::space, out);
if (!r || iter != end)
{
std::cerr << "Parsing failed." << std::endl;
return 1;
}
std::cout << "Parsed: " << out << std::endl;
}
SirGuy changed the AST to suit the default synthesized attributes. At the cost of, indeed complicating the AST.
However, you could leverage attribute compatibity rules by adapting std::pair. In fact, that is as simple as including 1 header:
#include <boost/fusion/include/std_pair.hpp>
Then, everything compiles without change, printing:
Parsed: 3.4 a 5 b 6 c 7
I made the following changes:
#include <boost/fusion/tuple.hpp>
using second_type = std::vector<boost::fusion::tuple<char, int>>;
std::ostream& operator<<(std::ostream& os, second_type::value_type val)
{
return os << boost::fusion::get<0>(val) << ' ' << boost::fusion::get<1>(val);
}
and the result compiled for me. There are most certainly other solutions available too.
I'm using boost::irange and created a helper function to simplify the code by removing the need for explicit template parameters. I don't understand why it doesn't work. Here's the code:
#include <iostream>
#include <boost/range/irange.hpp>
template<typename T>
boost::irange<T> range_from_zero(T limit)
{
return boost::irange<T>(T(), limit);
}
int main() {
size_t end = 100;
for (auto i : range_from_zero(0,end))
std::cout << i << ' ';
return 0;
}
There's a live version here https://ideone.com/VVvW6e, which produces compilation errors
prog.cpp:5:8: error: 'irange<T>' in namespace 'boost' does not name a type
boost::irange<T> range_from_zero(T limit)
^
prog.cpp: In function 'int main()':
prog.cpp:12:41: error: 'range_from_zero' was not declared in this scope
for (auto i : range_from_zero(0,end))
If I use boost::irange directly in the range-for, then it works:
#include <iostream>
#include <boost/range/irange.hpp>
int main() {
size_t end = 100;
for (auto i : boost::irange<size_t>(0,end))
std::cout << i << ' ';
return 0;
}
this works fine: https://ideone.com/TOWY6H
I thought maybe is was a problem using range-for on the return of a function, but it isn't; this works using a std::vector:
#include <iostream>
#include <boost/range/irange.hpp>
template<typename T>
std::vector<T> range_from_zero(T limit)
{
auto range = boost::irange<T>(T(), limit);
return { std::begin(range), std::end(range) };
}
int main() {
size_t end = 100;
for (auto i : range_from_zero(end))
std::cout << i << ' ';
return 0;
}
See https://ideone.com/TYRXnC
Any ideas, please?
But, first off, what's wrong with Live On Coliru
for (size_t i : irange(0, 100))
or even Live On Coliru
size_t end = 100;
for (auto i : irange(0ul, end))
irange is a function template, and it cannot be used as a return type.
The return type is integer_range or strided_integer_range. As such, irange is already the function you were looking for.
Only, you didn't pass arguments that could be unambiguously deduced. If you can to allow this, "copy" irange() implementation using separate template argument types for the boundary values and use e.g. std::common_type<T1,T2>::type as the range element.
Here's my stab at writing range_from_zero without naming implementation details in the interface:
Live On Coliru
#include <iostream>
#include <boost/range/irange.hpp>
template <typename T>
auto izrange(T upper) -> decltype(boost::irange(static_cast<T>(0), upper)) {
return boost::irange(static_cast<T>(0), upper);
}
int main() {
size_t end = 100;
for (size_t i : izrange(end))
std::cout << i << ' ';
}
I have a boost spirit parser which works very well with boost 1.46.1, and which doesn't work with boost 1.54.
This parser extract informations from the following sentence, which is a variable initialization in a DSEL : "Position #start=0;0;0".
Informations extracted from this sentence are stored in a structure :
Type of the variable will be stored (here position) ;
Name of variable (here start) ;
The "#" means that the variable is "static" ;
The value of the variable (0;0;0).
The code to extract these inforamtions is as follow :
#include <iostream>
#include <boost/spirit/include/classic.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
using namespace std;
using namespace boost::spirit;
struct VariableInitialization
{
std::string m_type;
bool m_is_static;
std::string m_name;
std::string m_value;
};
BOOST_FUSION_ADAPT_STRUCT(
VariableInitialization,
(std::string, m_type)
(bool, m_is_static)
(std::string, m_name)
(std::string, m_value)
)
template <typename Iterator>
struct VariableInitializationParser : qi::grammar<Iterator, VariableInitialization(), ascii::space_type> {
/*!
* IsStatic is a mapping betwen a char and a boolean
*/
struct IsStatic_ : qi::symbols<char, bool> {
IsStatic_()
{
add("#", false)("#", true);
}
}IsStatic;
VariableInitializationParser() :
VariableInitializationParser::base_type(start) {
using qi::lit;
using ascii::char_;
using qi::_val;
using qi::_1;
/*!
* For now, type is one of the three following litterals :
*/
var_type %= lit("Position")|lit("String")|lit("Numeric")|lit("Integer")|lit("Trajectory");
/*!
* identifier is how a variable can be named. Name of variable is an alpha (a-zA-Z) or an _,followed
* by any alpha numeric (a-zA-Z0-9) or a _. The followings are correct :
* _toto _T5ot_To t1oTo ...
* The following are incorrect
* 12toto -tiotp ...
*/
identifier %= ((ascii::alpha|char_('_')) >> *(ascii::alnum|char_('_')));
/*!
* var value can be anything because it's parsed by someone else.
*/
var_value %= qi::lexeme[*(char_)];
start = var_type >> IsStatic >> identifier >> '=' >> var_value;
}
qi::rule<Iterator, std::string(), ascii::space_type> var_type;
qi::rule<Iterator, std::string(), ascii::space_type> identifier;
qi::rule<Iterator, std::string(), ascii::space_type> var_value;
qi::rule<Iterator, VariableInitialization(), ascii::space_type> start;
};
int main()
{
VariableInitialization variable;
std::string input = "Position #toto=1;2;2";
std::string::const_iterator iter = input.begin();
std::string::const_iterator end = input.end();
// The phrase_parse call wil fill the structure "variable" with the good values if the syntax is correct.
// if the syntax is not correct, the method will return false.
// So if input = "Integer #toto= 6", variable.m_type == "Integer", variable.m_isStatic==true,
// variable.m_name=="toto" and variable.m_vale="6".
VariableInitializationParser<std::string::const_iterator> m_parser;
bool ok = phrase_parse(iter, end, m_parser, boost::spirit::ascii::space, variable);
if(!ok) return false;
std::cout << "Boost version : " << BOOST_VERSION << std::endl;
std::cout << "Type : " << variable.m_type << std::endl
<< "Is Static : " << variable.m_is_static << std::endl
<< "Name :" << variable.m_name << std::endl
<< "Value :" << variable.m_value << std::endl;
return 0;
}
Output of the following code is different in Boost 1.46.1 and in boost 1.53.
With boost 1.46.1 I have the following Output :
Boost version : 104601
Type : Position
Is Static : 1
Name :toto
Value :1;2;2
With boost 1.54 I have :
Boost version : 105400
Type :
Is Static : 1
Name :toto
Value :1;2;2
As you can see in boost 1.54 the Position output is not filled by the parser.
I read (maybe not carefully) the changelog of Fusion and Spirit, and I can't find why this happens.
Does someone have an explaination ?
No this is not a regression. Quite clearly you depended on undocumented (possibly undefined) behaviour.
Just change lit into string, or just slightly more compact:
var_type = qi::raw[lit("Position")|"String"|"Numeric"|"Integer"|"Trajectory"];
See it Live On Coliru
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
using namespace std;
using namespace boost::spirit;
struct VariableInitialization
{
std::string m_type;
bool m_is_static;
std::string m_name;
std::string m_value;
};
BOOST_FUSION_ADAPT_STRUCT(
VariableInitialization,
(std::string, m_type)
(bool, m_is_static)
(std::string, m_name)
(std::string, m_value)
)
template <typename Iterator>
struct VariableInitializationParser : qi::grammar<Iterator, VariableInitialization(), ascii::space_type> {
struct IsStatic_ : qi::symbols<char, bool> {
IsStatic_() {
add("#", false)("#", true);
}
} IsStatic;
VariableInitializationParser() :
VariableInitializationParser::base_type(start) {
using qi::lit;
using ascii::char_;
var_type = qi::raw[lit("Position")|"String"|"Numeric"|"Integer"|"Trajectory"];
identifier = (ascii::alpha|'_') >> *(ascii::alnum|'_');
var_value = qi::lexeme[*(char_)];
start = var_type >> IsStatic >> identifier >> '=' >> var_value;
}
qi::rule<Iterator, std::string(), ascii::space_type> var_type;
qi::rule<Iterator, std::string(), ascii::space_type> identifier;
qi::rule<Iterator, std::string(), ascii::space_type> var_value;
qi::rule<Iterator, VariableInitialization(), ascii::space_type> start;
};
int main()
{
VariableInitialization variable;
std::string input = "Position #toto=1;2;2";
std::string::const_iterator iter = input.begin();
std::string::const_iterator end = input.end();
VariableInitializationParser<std::string::const_iterator> m_parser;
bool ok = phrase_parse(iter, end, m_parser, boost::spirit::ascii::space, variable);
if(!ok) return false;
std::cout << "Boost version : " << BOOST_VERSION << "\n";
std::cout << "Type : " << variable.m_type << "\n"
<< "Is Static : " << variable.m_is_static << "\n"
<< "Name :" << variable.m_name << "\n"
<< "Value :" << variable.m_value << "\n";
}
Here's a sample code
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct input : qi::grammar<Iterator, unsigned()>
{
input() : input::base_type(start)
{
using qi::lit;
using qi::double_;
start = lit("ADD")
>> +(
+lit(" ")
>> double_
>> +lit(" ")
>> double_
);
}
qi::rule<Iterator, unsigned()> start;
};
int main()
{
input<std::string::const_iterator> input_parser; // Our grammar
std::string str = "ADD 1132.324 2342.234";
unsigned result;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = qi::parse(iter, end, input_parser, result);
}
I get the following error.
/usr/local/include/boost/spirit/home/qi/nonterminal/rule.hpp:201:
instantiated from
‘boost::spirit::qi::rule&
boost::spirit::qi::rule::operator=(const Expr&)
[with Expr =
boost::proto::exprns_::expr&,
const
boost::proto::exprns_::expr&>,
1l>&, const
boost::spirit::terminal&>,
2l>&, const
boost::proto::exprns_::expr&>,
1l>&>, 2l>&, const
boost::spirit::terminal&>,
2l>&>, 1l>&>, 2l>, Iterator =
__gnu_cxx::__normal_iterator,
std::allocator > >, T1 =
unsigned int ()(), T2 =
boost::fusion::unused_type, T3 =
boost::fusion::unused_type, T4 =
boost::fusion::unused_type]’
mini.c++:34: instantiated from
‘input::input() [with
Iterator =
__gnu_cxx::__normal_iterator,
std::allocator > >]’
mini.c++:49: instantiated from here
/usr/local/include/boost/spirit/home/qi/operator/plus.hpp:62:
error: no type named ‘type’ in
‘struct
boost::spirit::traits::container_valueâ€
The weird part is, if I use the minus (-) operation i.e.
start = lit("ADD")
>> -(
+lit(" ")
>> double_
>> +lit(" ")
>> double_
);
... it compiles perfectly fine!
Compiling on gcc 4.3.4.
The attribute exposed by your start rule is unsigned, while the plus-parser exposes a container type holding the attributes of the wrapped elements. Here are the attribute propagation rules from the docs:
a: A --> +a: vector<A>
(i.e. if the parser a exposes an attribute of type A, a parser +a will expose a (standard) container holding instances of A, for instance std::vector<A>).
In your case, the embedded parser exposes double's. For this reason, you need to change the code in order to work as expected:
template <typename Iterator>
struct input : qi::grammar<Iterator, std::vector<double>()>
{
input() : input::base_type(start)
{
using qi::lit;
using qi::double_;
start = lit("ADD") >> +(+lit(" ") >> double_ >> +lit(" ") >> double_);
}
qi::rule<Iterator, std::vector<double>()> start;
};
int main()
{
input<std::string::const_iterator> input_parser; // Our grammar
std::string str = "ADD 1132.324 2342.234";
std::vector<double> result;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = qi::parse(iter, end, input_parser, result);
}
But unfortunately, things are not as easy as it seems. The current version of Spirit has a bug preventing the code to work (even the soon to be released Boost V1.46 will still have this bug, but it's fixed in SVN trunk). The problem is, that plus does not 'flatten' the embedded elements into the provided container, resulting in every second double parsed by the code above to be lost.
The workaround is to avoid sequences exposing more than one attribute inside plus:
start = lit("ADD") >> +(+lit(" ") >> double_);
verifying afterwards that the number of elements parsed was even.
A side note: you seem to want to skip whitespace in between elements in the input. This can be achieved easier by using a skip parser:
template <typename Iterator>
struct input : qi::grammar<Iterator, std::vector<double>(), qi::space_type>
{
input() : input::base_type(start)
{
using qi::lit;
using qi::double_;
start = lit("ADD") >> +double_;
}
qi::rule<Iterator, std::vector<double>(), qi::space_type> start;
};
int main()
{
input<std::string::const_iterator> input_parser; // Our grammar
std::string str = "ADD 1132.324 2342.234";
std::vector<double> result;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = qi::phrase_parse(iter, end, input_parser, qi::space, result);
}
which at the same time circumvents the issue described above.
I have read the other thread about copy or reference semantics for boost::spirt::qi::rule. I am using Boost 1.42.
using boost::spirit::qi::phrase_parse;
typedef boost::spirit::qi::rule < std::string::const_iterator, boost::spirit::ascii::space_type > rule_type;
std::list < rule_type > ruleList;
std::string const s("abcdef");
std::string::const_iterator iter = s.begin(), end = s.end();
std::cout << typeid(char_).name() << std::endl;
ruleList.push_back(char_);
ruleList.push_back(*ruleList.back());
assert(phrase_parse(iter, s.end(), ruleList.back(), boost::spirit::ascii::space));
assert(iter == s.end());
This fails with...
Assertion `phrase_parse(iter, s.end(), ruleList.back(), traits::space())' failed.
Aborted (core dumped)
Is there a way to store rules in a STL list or deque? (References don't die until removed).
With Boost V1.45, this (essentially your code from above) works without problems (MSVC2010, g++ 4.5.1):
#include <list>
#include <string>
#include <iostream>
#include <boost/spirit/include/qi.hpp>
using namespace boost::spirit;
int main()
{
typedef qi::rule<std::string::const_iterator, ascii::space_type> rule_type;
std::list<rule_type> ruleList;
std::string const s("abcdef");
std::string::const_iterator iter = s.begin(), end = s.end();
std::cout << typeid(qi::char_).name() << std::endl;
ruleList.push_back(qi::char_);
ruleList.push_back(*ruleList.back());
assert(qi::phrase_parse(iter, s.end(), ruleList.back(), ascii::space));
assert(iter == s.end());
return 0;
}
Therefore, I assume it's a bug in the version of Spirit you're using.
I could not get your example to compile. Aside from not using the correct types from ...::qi, you added a () to the trait::space type.
This works w/o problem for me (boost 1.44)
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <vector>
#include <cassert>
using boost::spirit::qi::phrase_parse;
typedef boost::spirit::qi::rule < std::string::const_iterator, boost::spirit::qi::space_type > rule_type;
int main() {
std::list < rule_type > ruleList;
std::string const s("abcdef");
std::string::const_iterator iter = s.begin(), end = s.end();
ruleList.push_back(*boost::spirit::qi::char_);
assert(phrase_parse(iter, s.end(), ruleList.back(), boost::spirit::qi::space));
assert(iter == s.end());
}
~>g++ test.cpp && ./a.out
~>
please note I use qi::space_type and `qi::space instead of the ascii namespace. I have no idea what/where the trait namespace is.