Related
What's the proper way to indicate a parse fail in a boost::spirit::traits::transform_attribute? Can I throw any old exception, or is there a specific thing it wants me to do?
namespace boost
{
namespace spirit
{
namespace traits
{
template <>
struct transform_attribute<TwoNums, std::vector<char>, qi::domain>
{
typedef std::vector<char> type;
static type pre(TwoWords&) { return{}; }
static void post(TwoWords& val, type const& attr) {
std::string stringed(attr.begin(), attr.end());
//https://stackoverflow.com/questions/236129/the-most-elegant-way-to-iterate-the-words-of-a-string
std::vector<std::string> strs;
boost::split(strs, stringed, ",");
if(strs.size()!=2)
{
//What do I do here?
}
val = TwoWords(strs[0],strs[1]);
}
static void fail(FDate&) { }
};
}
}
}
Yes, raising an exception seems the only out-of-band way.
You could use qi::on_error to trap and respond to it.
However, it's a bit unclear what you need this for. It seems a bit upside down to use split inside a parser. Splitting is basically a poor version of parsing.
Why not have a rule for the sub-parsing?
1. Simple Throw...
Live On Coliru
#include <boost/algorithm/string.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
struct Invalid {};
struct TwoWords {
std::string one, two;
};
namespace boost { namespace spirit { namespace traits {
template <> struct transform_attribute<TwoWords, std::vector<char>, qi::domain> {
typedef std::vector<char> type;
static type pre(TwoWords &) { return {}; }
static void post(TwoWords &val, type const &attr) {
std::string stringed(attr.begin(), attr.end());
std::vector<std::string> strs;
boost::split(strs, stringed, boost::is_any_of(","));
if (strs.size() != 2) {
throw Invalid{};
}
val = TwoWords{ strs.at(0), strs.at(1) };
}
static void fail(TwoWords &) {}
};
} } }
template <typename It>
struct Demo1 : qi::grammar<It, TwoWords()> {
Demo1() : Demo1::base_type(start) {
start = qi::attr_cast<TwoWords>(+qi::char_);
}
private:
qi::rule<It, TwoWords()> start;
};
int main() {
Demo1<std::string::const_iterator> parser;
for (std::string const input : { ",", "a,b", "a,b,c" }) {
std::cout << "Parsing " << std::quoted(input) << " -> ";
TwoWords tw;
try {
if (parse(input.begin(), input.end(), parser, tw)) {
std::cout << std::quoted(tw.one) << ", " << std::quoted(tw.two) << "\n";
} else {
std::cout << "Failed\n";
}
} catch(Invalid) {
std::cout << "Input invalid\n";
}
}
}
Prints
Parsing "," -> "", ""
Parsing "a,b" -> "a", "b"
Parsing "a,b,c" -> Input invalid
2. Handling Errors Inside The Parser
This feels a bit hacky because it will require you to throw a expectation_failure.
This is not optimal since it assumes you know the iterator the parser is going to be instantiated with.
on_error was designed for use with expectation points
*Live On Coliru
#include <boost/algorithm/string.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
struct Invalid {};
struct TwoWords {
std::string one, two;
};
namespace boost { namespace spirit { namespace traits {
template <> struct transform_attribute<TwoWords, std::vector<char>, qi::domain> {
typedef std::vector<char> type;
static type pre(TwoWords &) { return {}; }
static void post(TwoWords &val, type const &attr) {
std::string stringed(attr.begin(), attr.end());
std::vector<std::string> strs;
boost::split(strs, stringed, boost::is_any_of(","));
if (strs.size() != 2) {
throw qi::expectation_failure<std::string::const_iterator>({}, {}, info("test"));
}
val = TwoWords{ strs.at(0), strs.at(1) };
}
static void fail(TwoWords &) {}
};
} } }
template <typename It>
struct Demo2 : qi::grammar<It, TwoWords()> {
Demo2() : Demo2::base_type(start) {
start = qi::attr_cast<TwoWords>(+qi::char_);
qi::on_error(start, [](auto&&...){});
// more verbose spelling:
// qi::on_error<qi::error_handler_result::fail> (start, [](auto&&...){[>no-op<]});
}
private:
qi::rule<It, TwoWords()> start;
};
int main() {
Demo2<std::string::const_iterator> parser;
for (std::string const input : { ",", "a,b", "a,b,c" }) {
std::cout << "Parsing " << std::quoted(input) << " -> ";
TwoWords tw;
try {
if (parse(input.begin(), input.end(), parser, tw)) {
std::cout << std::quoted(tw.one) << ", " << std::quoted(tw.two) << "\n";
} else {
std::cout << "Failed\n";
}
} catch(Invalid) {
std::cout << "Input invalid\n";
}
}
}
Prints
Parsing "," -> "", ""
Parsing "a,b" -> "a", "b"
Parsing "a,b,c" -> Failed
3. Finally: Sub-rules Rule!
Let's assume a slightly more interesting grammar in which you have a ; separated list of TwoWords:
"foo,bar;a,b"
We parse into a vector of TwoWords:
using Word = std::string;
struct TwoWords { std::string one, two; };
using TwoWordses = std::vector<TwoWords>;
Instead of using traits to "coerce" attributes, we just adapt the struct and rely on automatic attribute propagation:
BOOST_FUSION_ADAPT_STRUCT(TwoWords, one, two)
The parser mimics the data-types:
template <typename It>
struct Demo3 : qi::grammar<It, TwoWordses()> {
Demo3() : Demo3::base_type(start) {
using namespace qi;
word = *(graph - ',' - ';');
twowords = word >> ',' >> word;
start = twowords % ';';
}
private:
qi::rule<It, Word()> word;
qi::rule<It, TwoWords()> twowords;
qi::rule<It, TwoWordses()> start;
};
And the full test is Live On Coliru
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
using Word = std::string;
struct TwoWords { std::string one, two; };
using TwoWordses = std::vector<TwoWords>;
BOOST_FUSION_ADAPT_STRUCT(TwoWords, one, two);
template <typename It>
struct Demo3 : qi::grammar<It, TwoWordses()> {
Demo3() : Demo3::base_type(start) {
using namespace qi;
word = *(graph - ',' - ';');
twowords = word >> ',' >> word;
start = twowords % ';';
}
private:
qi::rule<It, Word()> word;
qi::rule<It, TwoWords()> twowords;
qi::rule<It, TwoWordses()> start;
};
int main() {
using It = std::string::const_iterator;
Demo3<It> parser;
for (std::string const input : {
",",
"foo,bar",
"foo,bar;qux,bax",
"foo,bar;qux,bax;err,;,ful",
// failing cases or cases with trailing input:
"",
"foo,bar;",
"foo,bar,qux",
})
{
std::cout << "Parsing " << std::quoted(input) << " ->\n";
TwoWordses tws;
It f = input.begin(), l = input.end();
if (parse(f, l, parser, tws)) {
for(auto& tw : tws) {
std::cout << " - " << std::quoted(tw.one) << ", " << std::quoted(tw.two) << "\n";
}
} else {
std::cout << "Failed\n";
}
if (f != l) {
std::cout << "Remaining unparsed input: " << std::quoted(std::string(f,l)) << "\n";
}
}
}
Prints
Parsing "," ->
- "", ""
Parsing "foo,bar" ->
- "foo", "bar"
Parsing "foo,bar;qux,bax" ->
- "foo", "bar"
- "qux", "bax"
Parsing "foo,bar;qux,bax;err,;,ful" ->
- "foo", "bar"
- "qux", "bax"
- "err", ""
- "", "ful"
Parsing "" ->
Failed
Parsing "foo,bar;" ->
- "foo", "bar"
Remaining unparsed input: ";"
Parsing "foo,bar,qux" ->
- "foo", "bar"
Remaining unparsed input: ",qux"
First of all I am using MSVC 2017 (latest version).
Here is my code for the nonterminal parser:
player.hpp
namespace parse
{
namespace impl
{
namespace x3 = boost::spirit::x3;
struct _tag;
using player_type = x3::rule<_tag, PlayerIterator>;
using player_vector_type = x3::rule<_tag, std::vector<PlayerIterator>>;
BOOST_SPIRIT_DECLARE(player_type);
BOOST_SPIRIT_DECLARE(player_vector_type);
}; //impl
impl::player_type player();
impl::player_vector_type player_vector();
}; //parse
player.cpp
namespace parse
{
namespace impl
{
const player_type player = "player";
const player_vector_type player_vector = "player_vector";
auto player_find = [](auto &ctx)
{
auto &attr = x3::_attr(ctx);
if(attr.which() == 0)
return x3::_val(ctx) = PlayerManager::find(boost::get<int>(attr));
return x3::_val(ctx) = PlayerManager::find(boost::get<std::string>(attr));
};
auto player_vector_find = [](auto &ctx)
{
return x3::_val(ctx) = PlayerManager::vector_find(x3::_attr(ctx));
};
auto const player_def = (x3::int_ | (+x3::char_))[player_find];
auto const player_vector_def = (((+x3::char_)[player_vector_find]));
BOOST_SPIRIT_DEFINE(player);
BOOST_SPIRIT_DEFINE(player_vector);
BOOST_SPIRIT_INSTANTIATE(player_type, iterator_type, context_type);
BOOST_SPIRIT_INSTANTIATE(player_vector_type, iterator_type, context_type);
} //impl
parse::impl::player_type player() { return impl::player; }
parse::impl::player_vector_type player_vector() { return impl::player_vector; }
}//parse
I get linker LNK2019 errors about "unresolved external symbols referenced":
Pastebin.com link with the errors
Any ideas about them?
Thanks in advance.
EDIT:
That's how I call it in my source file:
void test(std::string ¶ms)
{
std::tuple<PlayerIterator, std::vector<PlayerIterator>, std::string> tuple;
if (!x3::phrase_parse(params.begin(), params.end(), parse::player()>> parse::player_vector() >> (+x3::char_), x3::space,tuple))
{
std::cout << "Error: Parsing failed" << std::endl;
return;
}
std::cout << "Parsing succeded" << std::endl;
std::cout << "Found player, size of player vector: "<< std::get<1>(tuple).size() << ", also parsed string:" << std::get<2>(tuple);
return;
};
I'm willing to bet $10 that you mismatched the context or iterator types on the instantiations.
E.g. in your test function, the argument is std::string&, hence params.begin() will be std::string::iterator. If you had the iterator_type configured as follows:
using iterator_type = std::string::const_iterator; // very sensible!
you would have unresolved externals because the iterator type doesn't match the one actually required.
Same thing for the context. To match your invocation it needs to be exactly:
using context_type = x3::phrase_parse_context<x3::space_type>::type;
Sadly you didn't show the whole code, so you'll have to check on your own.
Notes
re-using the tag type is recipe for disaster. I don't think it can work. The rule tags are what dispatches the implementation function in the case of separated compilation units. Fix it:
using player_type = x3::rule<struct player_tag, PlayerIterator>;
using player_vector_type = x3::rule<struct player_vector_tag, std::vector<PlayerIterator>>;
copying the rules seems wasteful, consider returning by reference:
impl::player_type const& player();
impl::player_vector_type const& player_vector();
Note: this should be fine w.r.t. static initialization order fiasco
using which() on a variant is an anti-pattern. You can replace
auto player_find = [](auto &ctx) {
auto &attr = x3::_attr(ctx);
if (attr.which() == 0)
return x3::_val(ctx) = PlayerManager::find(boost::get<int>(attr));
return x3::_val(ctx) = PlayerManager::find(boost::get<std::string>(attr));
};
With
auto find = [](auto const& key) { return PlayerManager::find(key); };
auto player_find = [](auto &ctx) {
return x3::_val(ctx) = boost::apply_visitor(find, x3::_attr(ctx));
};
(+x3::char_) always matches all input
(+x3::graph) still matches all input because of the skipper
Instead you wanted a lexeme:
auto const name = x3::lexeme[+x3::graph];
auto const player_def = (x3::int_ | name) [player_find];
auto const player_vector_def = name[ player_vector_find];
May I suggest to write the test function a lot more concisely:
void test(std::string const ¶ms) {
auto comment_ = x3::lexeme[+x3::char_];
PlayerIterator player;
PlayerIterators vec;
std::string comment;
auto tuple = std::tie(player, vec, comment);
if (phrase_parse(params.cbegin(), params.cend(), parse::player() >> parse::player_vector() >> comment_, x3::space, tuple)) {
std::cout << "Parsing succeded" << std::endl;
std::cout << "Found player, size of player vector: " << vec.size() << "\n";
std::cout << "Also parsed string: " << std::quoted(comment);
} else {
std::cout << "Error: Parsing failed" << std::endl;
}
}
Full Demo
See it Live On Wandbox
stuff.h
Contains mockup PlayerManager
#pragma once
#include <string>
#include <vector>
#include <iostream>
struct PlayerIterator { };
using PlayerIterators = std::vector<PlayerIterator>;
struct PlayerManager {
static PlayerIterator find(std::string const&) { std::cout << __PRETTY_FUNCTION__ << "\n"; return {}; }
static PlayerIterator find(int) { std::cout << __PRETTY_FUNCTION__ << "\n"; return {}; }
static PlayerIterators vector_find(std::string const&) { std::cout << __PRETTY_FUNCTION__ << "\n"; return {}; }
};
test.h
#pragma once
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted.hpp>
#include "stuff.h"
namespace x3 = boost::spirit::x3;
namespace parse
{
namespace impl
{
using player_type = x3::rule<struct player_tag, PlayerIterator>;
using player_vector_type = x3::rule<struct player_vector_tag, PlayerIterators>;
BOOST_SPIRIT_DECLARE(player_type)
BOOST_SPIRIT_DECLARE(player_vector_type)
} //impl
impl::player_type const& player();
impl::player_vector_type const& player_vector();
} //parse
test.cpp
#include "stuff.h"
#include "test.h"
using iterator_type = std::string::const_iterator;
using context_type = x3::phrase_parse_context<x3::space_type>::type;
namespace parse {
namespace impl {
const player_type player = "player";
const player_vector_type player_vector = "player_vector";
auto find = [](auto const& key) { return PlayerManager::find(key); } ;
auto player_find = [](auto &ctx) { return x3::_val(ctx) = boost::apply_visitor(find, x3::_attr(ctx)); } ;
auto player_vector_find = [](auto &ctx) { return x3::_val(ctx) = PlayerManager::vector_find(x3::_attr(ctx)); } ;
auto const name = x3::lexeme[+x3::graph];
auto const player_def = (x3::int_ | name) [player_find];
auto const player_vector_def = name[ player_vector_find];
BOOST_SPIRIT_DEFINE(player)
BOOST_SPIRIT_DEFINE(player_vector)
BOOST_SPIRIT_INSTANTIATE(player_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(player_vector_type, iterator_type, context_type)
} // namespace impl
parse::impl::player_type const& player() { return impl::player; }
parse::impl::player_vector_type const& player_vector() { return impl::player_vector; }
} // namespace parse
main.cpp
#include "stuff.h"
#include "test.h"
#include <iostream>
#include <iomanip>
void test(std::string const ¶ms) {
auto comment_ = x3::lexeme[+x3::char_];
PlayerIterator player;
PlayerIterators vec;
std::string comment;
auto tuple = std::tie(player, vec, comment);
if (phrase_parse(params.cbegin(), params.cend(), parse::player() >> parse::player_vector() >> comment_, x3::space, tuple)) {
std::cout << "Parsing succeded" << std::endl;
std::cout << "Found player, size of player vector: " << vec.size() << "\n";
std::cout << "Also parsed string: " << std::quoted(comment);
} else {
std::cout << "Error: Parsing failed" << std::endl;
}
}
int main() {
test("42 someword # bogus trailing comment");
}
Prints:
static PlayerIterator PlayerManager::find(int)
static PlayerIterators PlayerManager::vector_find(const std::string &)
Parsing succeded
Found player, size of player vector: 0
Also parsed string: "# bogus trailing comment"
I'm facing with an issue in defining a grammar with variadic templates.
I started by defining some simple grammars contained into some struct (e.g. Latitude, Longitude) as follows:
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <iostream>
#include <string>
using namespace boost::spirit;
template <class Attribute>
using command_rule =
qi::rule<std::string::iterator, Attribute, ascii::space_type>;
template <class Attribute>
using command_grammar =
qi::grammar<std::string::iterator, Attribute, ascii::space_type>;
struct Latitude {
struct return_type {
double lat_;
};
struct grammar : command_grammar<return_type()> {
grammar() : grammar::base_type{latitude_} {
latitude_ = "LAT=" >> qi::double_;
}
private:
command_rule<return_type()> latitude_;
};
};
BOOST_FUSION_ADAPT_STRUCT(Latitude::return_type, (double, lat_))
struct Longitude {
struct return_type {
double lon_;
};
struct grammar : command_grammar<return_type()> {
grammar() : grammar::base_type{longitude_} {
longitude_ = "LON=" >> qi::double_;
}
private:
command_rule<return_type()> longitude_;
};
};
BOOST_FUSION_ADAPT_STRUCT(Longitude::return_type, (double, lon_))
Then, I would like to combine them in a complete grammar that is able to parse strings belonging to any of these simple grammars. To to this, I have defined a variadic template struct that tries to expand a list of sub-grammars into an expression like "grammar1 | grammar2 | ..."
template <class... Commands>
struct device_grammar : boost::spirit::qi::grammar<
std::string::iterator,
boost::variant<typename Commands::return_type...>(),
boost::spirit::ascii::space_type> {
typedef boost::variant<typename Commands::return_type...> return_type;
device_grammar() : device_grammar::base_type{rule_}{
build_rule<typename Commands::grammar...>();
}
private:
template <class CommandGrammar> void build_rule() {
rule_ = CommandGrammar();
}
template <class FirstGrammar, class SecondGrammar, class... Others>
void build_rule() {
build_rule<SecondGrammar, Others...>();
rule_ = rule_ | FirstGrammar();
}
boost::spirit::qi::rule<std::string::iterator, return_type(),
boost::spirit::ascii::space_type>
rule_;
};
typedef device_grammar<Latitude, Longitude> CoordinatesGrammar;
The code compiles (see the complete example below); the problem is that when it try to parse the input string, a segmentation fault is generated.
Can someone please help me to fix this issue?
Thanks a lot in advance.
EXAMPLE CODE (g++-4.9 or clang++-3.9):
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <iostream>
#include <string>
template <class... Commands>
struct device_grammar : boost::spirit::qi::grammar<
std::string::iterator,
boost::variant<typename Commands::return_type...>(),
boost::spirit::ascii::space_type> {
typedef boost::variant<typename Commands::return_type...> return_type;
device_grammar() : device_grammar::base_type{rule_}{
build_rule<typename Commands::grammar...>();
}
private:
template <class CommandGrammar> void build_rule() {
rule_ = CommandGrammar();
}
template <class FirstGrammar, class SecondGrammar, class... Others>
void build_rule() {
build_rule<SecondGrammar, Others...>();
rule_ = rule_ | FirstGrammar();
}
boost::spirit::qi::rule<std::string::iterator, return_type(),
boost::spirit::ascii::space_type>
rule_;
};
using namespace boost::spirit;
template <class Attribute>
using command_rule =
qi::rule<std::string::iterator, Attribute, ascii::space_type>;
template <class Attribute>
using command_grammar =
qi::grammar<std::string::iterator, Attribute, ascii::space_type>;
struct Latitude {
struct return_type {
double lat_;
};
struct grammar : command_grammar<return_type()> {
grammar() : grammar::base_type{latitude_} {
latitude_ = "LAT=" >> qi::double_;
}
private:
command_rule<return_type()> latitude_;
};
};
BOOST_FUSION_ADAPT_STRUCT(Latitude::return_type, (double, lat_))
struct Longitude {
struct return_type {
double lon_;
};
struct grammar : command_grammar<return_type()> {
grammar() : grammar::base_type{longitude_} {
longitude_ = "LON=" >> qi::double_;
}
private:
command_rule<return_type()> longitude_;
};
};
BOOST_FUSION_ADAPT_STRUCT(Longitude::return_type, (double, lon_))
typedef device_grammar<Latitude, Longitude> CoordinatesGrammar;
struct print : public boost::static_visitor<> {
void operator()(Latitude::return_type &t) const {
std::cout << "Latitude = " << t.lat_ << " deg" << std::endl;
;
}
void operator()(Longitude::return_type &t) const {
std::cout << "Longitude = " << t.lon_ << " deg" << std::endl;
;
}
};
int main() {
std::string s;
CoordinatesGrammar g;
CoordinatesGrammar::return_type v;
while (1) {
std::getline(std::cin, s);
auto it = s.begin();
if (qi::phrase_parse(it, s.end(), g, ascii::space, v)) {
print p;
boost::apply_visitor(p, v);
}
}
return 0;
}
EDIT:
As far as I understand, the problem is in the lines
rule_ = CommandGrammar();
...
rule_ = rule_ | FirstGrammar();
It seems that the grammar objects can not be temporary and have to be stored as members of the class. How can I do that?
EDIT:
I have also tried to store such objects in a std::tuple, but it seems still not working.
What you are creating closely resembles what qi's auto parser already does: http://www.boost.org/doc/libs/1_64_0/libs/spirit/doc/html/spirit/qi/reference/auto.html
If you specialize create_parser<> for your datatypes you can simply use qi::auto_ straight-away:
Live On Coliru
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace Commands {
namespace qi = boost::spirit::qi;
template <class T> using Rule = qi::rule<std::string::const_iterator, T()>;
template <typename... T>
auto parse(std::string const& s) {
boost::variant<T...> v;
auto it = s.begin();
if (qi::parse(it, s.end(), qi::auto_, v))
return v;
throw std::runtime_error(std::string(__FUNCTION__) + " failed");
}
}
struct Latitude { double lat_; };
BOOST_FUSION_ADAPT_STRUCT(Latitude, lat_)
struct Longitude { double lon_; };
BOOST_FUSION_ADAPT_STRUCT(Longitude, lon_)
namespace boost { namespace spirit { namespace traits {
template <> struct create_parser<Latitude> {
using type = Commands::Rule<Latitude>;
static type const& call() {
static type const s_rule = qi::skip(qi::space)["LAT=" >> qi::auto_];
return s_rule;
};
};
template <> struct create_parser<Longitude> {
using type = Commands::Rule<Longitude>;
static type const& call() {
static type const s_rule = qi::skip(qi::space)["LON=" >> qi::auto_];
return s_rule;
};
};
} } }
struct print {
using result_type = void;
void operator()(Latitude const &t) const { std::cout << "Latitude = " << t.lat_ << " deg" << std::endl; }
void operator()(Longitude const &t) const { std::cout << "Longitude = " << t.lon_ << " deg" << std::endl; }
};
#include <sstream>
int main() {
std::istringstream iss("LAT=4.3\n LON=5.0");
std::string s;
print printer;
while (std::getline(iss, s)) try {
auto v = Commands::parse<Latitude, Longitude>(s);
boost::apply_visitor(printer, v);
}
catch (std::exception const& e) {
std::cout << "'" << s << "': " << e.what() << "\n";
}
}
Prints
Latitude = 4.3 deg
Longitude = 5 deg
Nicer things
If you don't use qi::rule<> you don't need to hard-code the iterator either. Let's go full fun mode and get rid of the visitor too:
[Live On Coliru](http://coliru.stacked-crooked.com/a/84f7a8c9a453fc1b
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace Commands {
template <typename... T>
auto parse(std::string const& s) {
boost::variant<T...> v;
auto it = s.begin();
if (boost::spirit::qi::parse(it, s.end(), boost::spirit::qi::auto_, v))
return v;
throw std::runtime_error(std::string(__FUNCTION__) + " failed");
}
struct Latitude { double lat_; };
struct Longitude { double lon_; };
static inline std::ostream& operator<<(std::ostream& os, Latitude const &t) { return os << "Latitude = " << t.lat_ << " deg"; }
static inline std::ostream& operator<<(std::ostream& os, Longitude const &t) { return os << "Longitude = " << t.lon_ << " deg"; }
}
BOOST_FUSION_ADAPT_STRUCT(Commands::Latitude, lat_)
BOOST_FUSION_ADAPT_STRUCT(Commands::Longitude, lon_)
namespace boost { namespace spirit { namespace traits {
#define MAP_PARSER(T, expr) \
template <> struct create_parser<T> { \
using type = decltype(qi::attr_cast<T, T>(qi::copy(expr))); \
static type const& call() { static type const s_rule = qi::attr_cast<T, T>(qi::copy(expr)); return s_rule; }; \
};
#define AUTO_MAP_PARSER(T, caption) MAP_PARSER(T, qi::skip(qi::space)[qi::lit(caption) >> '=' >> qi::auto_])
AUTO_MAP_PARSER(::Commands::Longitude, "LON")
AUTO_MAP_PARSER(::Commands::Latitude, "LAT")
} } }
#include <sstream>
int main() {
std::istringstream iss("LAT=4.3\n LON=5.0");
std::string s;
while (std::getline(iss, s)) try {
using namespace Commands;
std::cout << "Parsed '" << s << "' into " << parse<Latitude, Longitude>(s) << "\n";
} catch (std::exception const& e) {
std::cout << "'" << s << "': " << e.what() << "\n";
}
}
Prints
Parsed 'LAT=4.3' into Latitude = 4.3 deg
Parsed ' LON=5.0' into Longitude = 5 deg
In addition to the Spirit Qi answer I gave:
If you can afford to enable c++1z, you can use Spirit X3 with fold-expressions:
Live On Coliru
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/x3.hpp>
namespace Commands {
namespace x3 = boost::spirit::x3;
template <typename... T>
auto parse(std::string const& s) {
using V = boost::variant<T...>;
V v;
auto it = s.begin();
if (x3::parse(it, s.end(), parser_for(v), v))
return v;
throw std::runtime_error(std::string(__FUNCTION__) + " failed");
}
struct Latitude { double lat_; };
struct Longitude { double lon_; };
auto label_for(Latitude) { return "LAT"; }
auto label_for(Longitude) { return "LON"; }
template <typename T, typename P>
auto as_cmd(P p) { return x3::rule<struct _, T>{}
= x3::skip(x3::space)[x3::lit(label_for(T{})) >> '=' >> p]; }
template <typename T> auto parser_for(T) { return as_cmd<T>(x3::double_); }
template <typename... T> auto parser_for(boost::variant<T...> _) { return (parser_for(T{}) | ...); }
static inline std::ostream& operator<<(std::ostream& os, Latitude const &t) { return os << "Latitude = " << t.lat_ << " deg"; }
static inline std::ostream& operator<<(std::ostream& os, Longitude const &t) { return os << "Longitude = " << t.lon_ << " deg"; }
}
BOOST_FUSION_ADAPT_STRUCT(Commands::Latitude, lat_)
BOOST_FUSION_ADAPT_STRUCT(Commands::Longitude, lon_)
#include <iostream>
#include <sstream>
int main() {
std::istringstream iss("LAT=4.3\n LON=5.0");
std::string s;
while (std::getline(iss, s)) try {
using namespace Commands;
std::cout << "Parsed '" << s << "' into " << parse<Latitude, Longitude>(s) << "\n";
} catch (std::exception const& e) {
std::cout << "'" << s << "': " << e.what() << "\n";
}
}
Prints
Parsed 'LAT=4.3' into Latitude = 4.3 deg
Parsed ' LON=5.0' into Longitude = 5 deg
Given a grammar that synthesizes a user-defined type, how can I write another grammar that:
Reuses the first grammar.
Synthesizes a second, distinct type, using the values underlying the first type?
In the example below, I've followed the Boost Spirit documentation to create a parser, foo_parser, that synthesizes a value of type foo_struct. I'd like to write a second parser, bar_parser, that reuses foo_parser but synthesizes a different (but obviously similar) value of type bar_struct. My naive example, commented out, causes spectacular g++ fireworks. :)
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace s {
using namespace boost::spirit;
using namespace boost::spirit::qi;
using namespace boost::spirit::ascii;
}
struct foo_struct {
int i;
char c;
};
BOOST_FUSION_ADAPT_STRUCT(
foo_struct,
(int, i)
(char, c)
)
struct bar_struct {
int i;
char c;
int j;
};
template <typename Iterator>
struct foo_parser : s::grammar<Iterator, foo_struct()>
{
foo_parser() : foo_parser::base_type(start) {
start %= s::int_ >> s::char_ ;
}
s::rule<Iterator, foo_struct()> start;
};
/*
BOOST_FUSION_ADAPT_STRUCT(
bar_struct,
(int, i)
(char, c)
(int, j)
)
template <typename Iterator>
struct bar_parser : s::grammar<Iterator, bar_struct()>
{
bar_parser() : bar_parser::base_type(start) {
// reuse a foo_parser
start %= foo >> s::int_ ;
}
foo_parser<Iterator> foo;
s::rule<Iterator, bar_struct()> start;
};
*/
I've previously devised this - somewhat - hacky way to address things
struct mybase { int a,b; };
struct myderived : mybase
{
mybase& base;
int c,d;
myderived() : base(*this) { }
};
BOOST_FUSION_ADAPT_STRUCT(mybase, (int,a)(int,b));
BOOST_FUSION_ADAPT_STRUCT(myderived, (mybase,base)(int,c)(int,d));
I'd much prefer a solution based on BOOST_FUSION_ADAPT_ADT, but I wasn't able to get it to work, see also the [spirit-general] mailing list:
http://boost.2283326.n4.nabble.com/struct-derived-struct-fusion-adapted-and-the-sequence-operator-td4259090.html
or search the list for derived base
Here's a full sample of that:
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
struct mybase { int a,b; };
struct myderived : mybase {
myderived() : base(*this) { }
mybase& base;
int c,d;
};
BOOST_FUSION_ADAPT_STRUCT(mybase, (int,a)(int,b));
BOOST_FUSION_ADAPT_STRUCT(myderived, (mybase,base)(int,c)(int,d));
int main()
{
using namespace boost::spirit::qi;
const char input[] = "1 2 3 4";
const char *f(input), *l(f+strlen(input));
rule<const char*, mybase() , space_type> base_ = int_ >> int_;
rule<const char*, myderived(), space_type> derived_ = base_ >> int_ >> int_;
myderived data;
bool ok = phrase_parse(f,l,derived_,space,data);
if (ok) std::cout << "data: " << data.a << ", " << data.b << ", " << data.c << ", " << data.d << "\n";
else std::cerr << "whoops\n";
if (f!=l)
std::cerr << "left: '" << std::string(f,l) << std::endl;
return 0;
}
Output:
data: 1, 2, 3, 4
I would like to use a parsed value as the input to a loop parser.
The grammar defines a header that specifies the (variable) size of the following string. For example, say the following string is the input to some parser.
12\r\nTest Payload
The parser should extract the 12, convert it to an unsigned int and then read twelve characters. I can define a boost spirit grammar that compiles, but an assertion in the boost spirit code fails at runtime.
#include <iostream>
#include <boost/spirit.hpp>
using namespace boost::spirit;
struct my_closure : public closure<my_closure, std::size_t> {
member1 size;
};
struct my_grammar : public grammar<my_grammar> {
template <typename ScannerT>
struct definition {
typedef rule<ScannerT> rule_type;
typedef rule<ScannerT, my_closure::context_t> closure_rule_type;
closure_rule_type header;
rule_type payload;
rule_type top;
definition(const my_grammar &self)
{
using namespace phoenix;
header = uint_p[header.size = arg1];
payload = repeat_p(header.size())[anychar_p][assign_a(self.result)];
top = header >> str_p("\r\n") >> payload;
}
const rule_type &start() const { return top; }
};
my_grammar(std::string &p_) : result(p_) {}
std::string &result;
};
int
main(int argc, char **argv)
{
const std::string content = "12\r\nTest Payload";
std::string payload;
my_grammar g(payload);
if (!parse(content.begin(), content.end(), g).full) {
std::cerr << "there was a parsing error!\n";
return -1;
}
std::cout << "Payload: " << payload << std::endl;
return 0;
}
Is it possible to tell spirit that the closure variable should be evaluated lazily? Is this behaviour supported by boost spirit?
This is much easier with the new qi parser available in Spirit 2. The following code snippet provides a full example that mostly works. An unexpected character is being inserted into the final result.
#include <iostream>
#include <string>
#include <boost/version.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_repeat.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
using boost::spirit::qi::repeat;
using boost::spirit::qi::uint_;
using boost::spirit::ascii::char_;
using boost::spirit::ascii::alpha;
using boost::spirit::qi::_1;
namespace phx = boost::phoenix;
namespace qi = boost::spirit::qi;
template <typename P, typename T>
void test_parser_attr(
char const* input, P const& p, T& attr, bool full_match = true)
{
using boost::spirit::qi::parse;
char const* f(input);
char const* l(f + strlen(f));
if (parse(f, l, p, attr) && (!full_match || (f == l)))
std::cout << "ok" << std::endl;
else
std::cout << "fail" << std::endl;
}
static void
straight_forward()
{
std::string str;
int n;
test_parser_attr("12\r\nTest Payload",
uint_[phx::ref(n) = _1] >> "\r\n" >> repeat(phx::ref(n))[char_],
str);
std::cout << "str.length() == " << str.length() << std::endl;
std::cout << n << "," << str << std::endl; // will print "12,Test Payload"
}
template <typename P, typename T>
void
test_phrase_parser(char const* input, P const& p,
T& attr, bool full_match = true)
{
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::ascii::space;
char const* f(input);
char const* l(f + strlen(f));
if (phrase_parse(f, l, p, space, attr) && (!full_match || (f == l)))
std::cout << "ok" << std::endl;
else
std::cout << "fail" << std::endl;
}
template <typename Iterator>
struct test_grammar
: qi::grammar<Iterator, std::string(), qi::locals<unsigned> > {
test_grammar()
: test_grammar::base_type(my_rule)
{
using boost::spirit::qi::_a;
my_rule %= uint_[_a = _1] >> "\r\n" >> repeat(_a)[char_];
}
qi::rule<Iterator, std::string(), qi::locals<unsigned> > my_rule;
};
static void
with_grammar_local_variable()
{
std::string str;
test_phrase_parser("12\r\nTest Payload", test_grammar<const char*>(), str);
std::cout << str << std::endl; // will print "Test Payload"
}
int
main(int argc, char **argv)
{
std::cout << "boost version: " << BOOST_LIB_VERSION << std::endl;
straight_forward();
with_grammar_local_variable();
return 0;
}
What you are looking for is lazy_p, check the example here: http://www.boost.org/doc/libs/1_35_0/libs/spirit/doc/the_lazy_parser.html