I use boost spirit to parse a color. That worked quite well,
but after I changed the the iterator type, the skipper stopped working.
"rgb(1.0,1.0,0.5)" // this works
" rgb(0.2,0.2,0.2)" // this fails
Here is the header:
struct ColorGrammar : public qi::grammar<StringIterator, Color(), chs::space_type>
{
//! Iterator type for this grammar
typedef StringIterator ItType;
//! Skipper type used in this grammar
typedef chs::space_type Skipper;
//! Rule to parse a number with up to 3 digits
qi::uint_parser<uint8, 10, 1, 3> number;
//! Rule to parse a hex digit
qi::uint_parser<uint8, 16, 1, 1> hexdigit;
ColorGrammar();
//! Rule for rgb(...)
qi::rule<ItType, Color(), qi::locals<float, float>, Skipper> rule_rgb;
//! Rule for rgba(...)
qi::rule<ItType, Color(), qi::locals<float, float, float>, Skipper> rule_rgba;
//! Mainrule
qi::rule<ItType, Color(), Skipper> rule_color;
};
Here is the cpp
ColorGrammar::ColorGrammar()
: ColorGrammar::base_type(rule_color, "color-grammar")
{
using namespace qi::labels;
using boost::phoenix::construct;
auto& _1 = qi::_1;
rule_rgb = '(' >> qi::float_[_a = _1] >> ',' >> qi::float_[_b = _1] >> ',' >> qi::float_[_val = phx::construct<Color>(_a, _b, _1)] >> ')';
rule_rgba = '(' >> qi::float_[_a = _1] >> ',' >> qi::float_[_b = _1] >> ',' >> qi::float_[_c = _1] >> ',' >> qi::float_[_val = phx::construct<Color>(_a, _b, _c, _1)] >> ')';
rule_color = (qi::lit("rgb") >> rule_rgb)
| (qi::lit("rgba") >> rule_rgba);
}
And the call:
Color out;
StringIterator begin = str.cbegin();
StringIterator end = str.cend();
bool result = qi::phrase_parse(begin, end, color_, chs::space, out);
I'm sure, it is only a little misstake, but I am not able to see it.
Maybe i watched too long at the source... can you see a misstake?
I can't see what's wrong: I've taken the effort to reconstruct your SSCCE.
http://liveworkspace.org/code/1pDtmn$1
In the process, it seems I must have removed the problem. I suggest you do the same.
Oh, and this is how I'd write this:
no more phoenix
no more constructors
no more qi::locals
no more needless copying
using expectation points
In short: no more fuss.
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <cstdint>
namespace qi = boost::spirit::qi;
namespace chs = boost::spirit::ascii; //qi;
typedef std::string::const_iterator StringIterator;
struct Color
{
float r,g,b,a;
};
BOOST_FUSION_ADAPT_STRUCT(Color, (float, r)(float, g)(float, b)(float, a))
template <typename ItType, typename Skipper>
struct ColorGrammar : public qi::grammar<StringIterator, Color(), Skipper>
{
ColorGrammar()
: ColorGrammar::base_type(rule_color, "color-grammar")
{
using namespace qi;
rule_rgb = lit("rgb") >> '(' > float_ > ',' > float_ > ',' > float_ > attr(1.0f) > ')';
rule_rgba = lit("rgba") >> '(' > float_ > ',' > float_ > ',' > float_ > ',' > float_ > ')';
rule_color = rule_rgb | rule_rgba;
}
private:
qi::uint_parser<uint8_t, 10, 1, 3> number; // unused
qi::uint_parser<uint8_t, 16, 1, 1> hexdigit; // unused
qi::rule<ItType, Color(), Skipper> rule_rgb, rule_rgba, rule_color;
};
int main()
{
Color out;
std::string str = " rgb ( 0.3 , .4 , 0.5 )";
StringIterator begin = str.cbegin();
StringIterator end = str.cend();
ColorGrammar<StringIterator, chs::space_type> color_;
bool result = qi::phrase_parse(begin, end, color_, chs::space, out);
std::cout << std::boolalpha << result << '\n';
std::cout << "remains: '" << std::string(begin, end) << "'\n";
}
Live on http://liveworkspace.org/code/35htD$3
Related
I have this example code, which parses the string str correctly.
How to I make it work if there any extra characters before and/or after the string? For example if I did str = std::string("AAA") + str + std::string("AAA")
frame.h
#define BOOST_SPIRIT_USE_PHOENIX_V3
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
namespace ascii = boost::spirit::ascii;
struct frame
{
std::string addr;
std::string func;
std::string file;
std::string fullname;
std::string line;
std::map<std::string, std::string> kv;
};
template <typename Iterator>
struct argsArray : qi::grammar<Iterator, std::map<std::string, std::string>()>
{
argsArray() : argsArray::base_type(query)
{
query =
qi::lit("args=[") >> pair >> *(qi::lit(',') >> pair) >> qi::lit(']');
pair = qi::lit("{name=") >> quoted_string >> qi::lit(",value=") >>
quoted_string >> qi::lit("}");
key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
quoted_string %= boost::spirit::lexeme['"' >> +(qi::char_ - '"') >> '"'];
}
qi::rule<Iterator, std::map<std::string, std::string>()> query;
qi::rule<Iterator, std::pair<std::string, std::string>()> pair;
qi::rule<Iterator, std::string()> key;
qi::rule<Iterator, std::string()> quoted_string;
};
template <typename Iterator>
struct frameParser : qi::grammar<Iterator, frame(), ascii::space_type>
{
frameParser() : frameParser::base_type(frame_rule)
{
static const auto _addr = phx::bind(&frame::addr, qi::_r1);
static const auto _func = phx::bind(&frame::func, qi::_r1);
static const auto _file = phx::bind(&frame::file, qi::_r1);
static const auto _fullname = phx::bind(&frame::fullname, qi::_r1);
static const auto _line = phx::bind(&frame::line, qi::_r1);
static const auto _kv = phx::bind(&frame::kv, qi::_r1);
func = qi::lit("func=") >> quoted_string;
addr = qi::lit("addr=") >> quoted_string;
file = qi::lit("file=") >> quoted_string;
fullname = qi::lit("fullname=") >> quoted_string;
line = qi::lit("line=") >> quoted_string;
func_rule = func[_func = qi::_1];
addr_rule = addr[_addr = qi::_1];
file_rule = file[_file = qi::_1];
fullname_rule = fullname[_fullname = qi::_1];
line_rule = line[_line = qi::_1];
kv_rule = arrTest[_kv = qi::_1];
quoted_string %= boost::spirit::lexeme['"' >> +(qi::char_ - '"') >> '"'];
frame_rule = qi::lit("frame={") >>
(addr_rule(qi::_val) ^ qi::lit(',') ^ func_rule(qi::_val) ^
qi::lit(',') ^ file_rule(qi::_val) ^ qi::lit(',') ^
fullname_rule(qi::_val) ^ qi::lit(',') ^ line_rule(qi::_val) ^
qi::lit(',') ^ kv_rule(qi::_val)) >>
qi::lit('}');
BOOST_SPIRIT_DEBUG_NODES(
(frame_rule)(func_rule)(addr_rule)(fullname_rule)(line_rule))
}
qi::rule<Iterator, void(frame&), ascii::space_type> func_rule, addr_rule,
file_rule, fullname_rule, line_rule, kv_rule;
qi::rule<Iterator, frame(), ascii::space_type> frame_rule;
qi::rule<Iterator, std::string()> addr, func, file, fullname, line;
qi::rule<Iterator, std::string()> quoted_string;
argsArray<Iterator> arrTest;
};
test.cc
#include <iostream>
#include "gtest/gtest.h"
#include "parser/frame.h"
TEST(ParseFrameString, Test1)
{
std::string str = R"(frame={addr="0x0000000000414008",)"
R"(func="main",)"
R"(args=[{name="argc",value="1"},)"
R"({name="argv",value="0x7fffffffe1a8"}],)"
R"(file="/home/stiopa/development/gdbFront/main.cc",)"
R"(fullname="/home/stiopa/development/gdbFront/main.cc",)"
R"(line="90"}")";
typedef std::string::const_iterator It;
const frameParser<It> g;
It iter(str.begin()), end(str.end());
frame frame;
bool r = phrase_parse(iter, end, g, boost::spirit::ascii::space, frame);
EXPECT_EQ(r, true);
EXPECT_EQ(frame.addr, "0x0000000000414008");
EXPECT_EQ(frame.func, "main");
std::map<std::string, std::string> kv{{"argc", "1"},
{"argv", "0x7fffffffe1a8"}};
EXPECT_EQ(frame.kv, kv);
EXPECT_EQ(frame.file, "/home/stiopa/development/gdbFront/main.cc");
EXPECT_EQ(frame.fullname, "/home/stiopa/development/gdbFront/main.cc");
EXPECT_EQ(frame.line, "90");
}
The simple, low-tech solution would be to use qi::seek from the repository:
#include <boost/spirit/repository/include/qi_seek.hpp>
namespace qir = boost::spirit::repository::qi;
And then:
bool r = phrase_parse(iter, end, qir::seek[g], boost::spirit::ascii::space, frame);
DEMO
Live On Coliru
#define BOOST_SPIRIT_USE_PHOENIX_V3
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>
namespace qi = boost::spirit::qi;
namespace qir = boost::spirit::repository::qi;
namespace phx = boost::phoenix;
namespace ascii = boost::spirit::ascii;
struct frame
{
std::string addr;
std::string func;
std::string file;
std::string fullname;
std::string line;
std::map<std::string, std::string> kv;
};
template <typename Iterator>
struct argsArray : qi::grammar<Iterator, std::map<std::string, std::string>()>
{
argsArray() : argsArray::base_type(query)
{
query =
qi::lit("args=[") >> pair >> *(qi::lit(',') >> pair) >> qi::lit(']');
pair = qi::lit("{name=") >> quoted_string >> qi::lit(",value=") >>
quoted_string >> qi::lit("}");
key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
quoted_string %= boost::spirit::lexeme['"' >> +(qi::char_ - '"') >> '"'];
}
qi::rule<Iterator, std::map<std::string, std::string>()> query;
qi::rule<Iterator, std::pair<std::string, std::string>()> pair;
qi::rule<Iterator, std::string()> key;
qi::rule<Iterator, std::string()> quoted_string;
};
template <typename Iterator>
struct frameParser : qi::grammar<Iterator, frame(), ascii::space_type>
{
frameParser() : frameParser::base_type(frame_rule)
{
static const auto _addr = phx::bind(&frame::addr, qi::_r1);
static const auto _func = phx::bind(&frame::func, qi::_r1);
static const auto _file = phx::bind(&frame::file, qi::_r1);
static const auto _fullname = phx::bind(&frame::fullname, qi::_r1);
static const auto _line = phx::bind(&frame::line, qi::_r1);
static const auto _kv = phx::bind(&frame::kv, qi::_r1);
func = qi::lit("func=") >> quoted_string;
addr = qi::lit("addr=") >> quoted_string;
file = qi::lit("file=") >> quoted_string;
fullname = qi::lit("fullname=") >> quoted_string;
line = qi::lit("line=") >> quoted_string;
func_rule = func[_func = qi::_1];
addr_rule = addr[_addr = qi::_1];
file_rule = file[_file = qi::_1];
fullname_rule = fullname[_fullname = qi::_1];
line_rule = line[_line = qi::_1];
kv_rule = arrTest[_kv = qi::_1];
quoted_string %= boost::spirit::lexeme['"' >> +(qi::char_ - '"') >> '"'];
frame_rule = qi::lit("frame={") >>
(addr_rule(qi::_val) ^ qi::lit(',') ^ func_rule(qi::_val) ^
qi::lit(',') ^ file_rule(qi::_val) ^ qi::lit(',') ^
fullname_rule(qi::_val) ^ qi::lit(',') ^ line_rule(qi::_val) ^
qi::lit(',') ^ kv_rule(qi::_val)) >>
qi::lit('}');
BOOST_SPIRIT_DEBUG_NODES(
(frame_rule)(func_rule)(addr_rule)(fullname_rule)(line_rule))
}
qi::rule<Iterator, void(frame&), ascii::space_type> func_rule, addr_rule,
file_rule, fullname_rule, line_rule, kv_rule;
qi::rule<Iterator, frame(), ascii::space_type> frame_rule;
qi::rule<Iterator, std::string()> addr, func, file, fullname, line;
qi::rule<Iterator, std::string()> quoted_string;
argsArray<Iterator> arrTest;
};
#include <iostream>
//#include "parser/frame.h"
int main()
{
std::string str = R"(frame={addr="0x0000000000414008",)"
R"(func="main",)"
R"(args=[{name="argc",value="1"},)"
R"({name="argv",value="0x7fffffffe1a8"}],)"
R"(file="/home/stiopa/development/gdbFront/main.cc",)"
R"(fullname="/home/stiopa/development/gdbFront/main.cc",)"
R"(line="90"}")";
str = "AAA" + str + "AAA";
typedef std::string::const_iterator It;
const frameParser<It> g;
It iter(str.begin()), end(str.end());
frame frame;
bool r = phrase_parse(iter, end, qir::seek[g], boost::spirit::ascii::space, frame);
assert(r == true);
assert(frame.addr == "0x0000000000414008");
assert(frame.func == "main");
std::map<std::string, std::string> kv{{"argc", "1"},
{"argv", "0x7fffffffe1a8"}};
assert(frame.kv == kv);
assert(frame.file == "/home/stiopa/development/gdbFront/main.cc");
assert(frame.fullname == "/home/stiopa/development/gdbFront/main.cc");
assert(frame.line == "90");
}
Tests still pass.
And here's a free code review. Please see
Boost Spirit: "Semantic actions are evil"?
Boost spirit skipper issues
you didn't parse the delimiting ',' correctly at all. You must require it, unless end-of-frame ('}'). You cannot accept multiple in a row
Live On Coliru
#define BOOST_SPIRIT_USE_PHOENIX_V3
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>
namespace qi = boost::spirit::qi;
namespace qir = boost::spirit::repository::qi;
namespace phx = boost::phoenix;
namespace ascii = boost::spirit::ascii;
struct frame {
std::string addr;
std::string func;
std::string file;
std::string fullname;
std::string line;
std::map<std::string, std::string> kv;
};
BOOST_FUSION_ADAPT_STRUCT(frame, addr, func, file, fullname, line, kv)
template <typename Iterator>
struct argsArray : qi::grammar<Iterator, std::map<std::string, std::string>()>
{
argsArray() : argsArray::base_type(query)
{
query = "args=[" >> pair >> *(',' >> pair) >> ']';
pair = "{name=" >> quoted_string >> ",value=" >> quoted_string >> "}";
key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
quoted_string = '"' >> +(qi::char_ - '"') >> '"';
}
private:
qi::rule<Iterator, std::map<std::string, std::string>()> query;
qi::rule<Iterator, std::pair<std::string, std::string>()> pair;
qi::rule<Iterator, std::string()> key;
qi::rule<Iterator, std::string()> quoted_string;
};
template <typename Iterator>
struct frameParser : qi::grammar<Iterator, frame(), ascii::space_type>
{
frameParser() : frameParser::base_type(frame_rule)
{
quoted_string = '"' >> +(qi::char_ - '"') >> '"';
delim = (&qi::lit('}')) | ',';
field_rule = qi::lexeme [ qi::lit(qi::_r1) >> '=' ] >> quoted_string >> delim;
kv_rule = arrTest >> delim;
frame_rule = "frame={" >>
(field_rule(+"addr") ^
field_rule(+"func") ^
field_rule(+"file") ^
field_rule(+"fullname") ^
field_rule(+"line") ^
kv_rule
) >> '}';
BOOST_SPIRIT_DEBUG_NODES((frame_rule)(field_rule))
}
private:
qi::rule<Iterator> delim;
qi::rule<Iterator, std::string(char const*), ascii::space_type> field_rule;
qi::rule<Iterator, std::map<std::string, std::string>()> kv_rule;
qi::rule<Iterator, frame(), ascii::space_type> frame_rule;
qi::rule<Iterator, std::string()> quoted_string;
argsArray<Iterator> arrTest;
};
#include <iostream>
//#include "parser/frame.h"
int main()
{
std::string str = R"(frame={addr="0x0000000000414008",)"
R"(func="main",)"
R"(args=[{name="argc",value="1"},)"
R"({name="argv",value="0x7fffffffe1a8"}],)"
R"(file="/home/stiopa/development/gdbFront/main.cc",)"
R"(fullname="/home/stiopa/development/gdbFront/main.cc",)"
R"(line="90"}")";
str = "AAA" + str + "AAA";
typedef std::string::const_iterator It;
const frameParser<It> g;
It iter(str.begin()), end(str.end());
frame frame;
bool r = phrase_parse(iter, end, qir::seek[g], boost::spirit::ascii::space, frame);
if (iter != end)
std::cout << "Remaining unparsed: '" << std::string(iter,end) << "'\n";
assert(r == true);
assert(frame.addr == "0x0000000000414008");
assert(frame.func == "main");
std::map<std::string, std::string> kv{{"argc", "1"},
{"argv", "0x7fffffffe1a8"}};
assert(frame.kv == kv);
assert(frame.file == "/home/stiopa/development/gdbFront/main.cc");
assert(frame.fullname == "/home/stiopa/development/gdbFront/main.cc");
assert(frame.line == "90");
}
Prints:
Remaining unparsed: '"AAA'
Tests still pass.
Note your original sample input had a trailing ", which you simply ignored.
Here json
{"ts":1827908701,"updates":[[4,30623409,17,81282347579,1425823449632," ... ","tfs"],[80,1,0],[7,81282347579,30652308]]}
How can I get the value 4 of updates using the library boost?
I know how to take such a value of "ts". But I do not understand how to take the value of the two brackets
Technically, this answer shows how to use Boost to extract that value:
Live On Coliru
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <map>
namespace qi = boost::spirit::qi;
std::string const sample = R"({"ts":1827908701,"updates":[[4,30623409,17,81282347579,1425823449632," ... ","tfs"],[80,1,0],[7,81282347579,30652308]]})";
namespace qd_json { // quick and dirty JSON handling
struct null {};
using text = std::string;
using value = boost::make_recursive_variant<
null,
text, // "string" (roughly!)
double, // number
std::map<text, boost::recursive_variant_>, // object
std::vector<boost::recursive_variant_>, // array
bool
>::type;
using member = std::pair<text, value>;
using object = std::map<text, value>;
using array = std::vector<value>;
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, value(), Skipper>
{
grammar() : grammar::base_type(value_) {
using namespace qi;
text_ = '"' >> raw [*('\\' >> char_ | ~char_('"'))] >> '"';
null_ = "null" >> attr(null{});
bool_ = "true" >> attr(true) | "false" >> attr(false);
value_ = null_ | bool_ | text_ | double_ | object_ | array_;
member_ = text_ >> ':' >> value_;
object_ = '{' >> -(member_ % ',') >> '}';
array_ = '[' >> -(value_ % ',') >> ']';
////////////////////////////////////////
// Bonus: properly decoding the string:
text_ = lexeme [ '"' >> *ch_ >> '"' ];
ch_ = +(
~char_("\"\\")) [ _val += _1 ] |
qi::lit("\x5C") >> ( // \ (reverse solidus)
qi::lit("\x22") [ _val += '"' ] | // " quotation mark U+0022
qi::lit("\x5C") [ _val += '\\' ] | // \ reverse solidus U+005C
qi::lit("\x2F") [ _val += '/' ] | // / solidus U+002F
qi::lit("\x62") [ _val += '\b' ] | // b backspace U+0008
qi::lit("\x66") [ _val += '\f' ] | // f form feed U+000C
qi::lit("\x6E") [ _val += '\n' ] | // n line feed U+000A
qi::lit("\x72") [ _val += '\r' ] | // r carriage return U+000D
qi::lit("\x74") [ _val += '\t' ] | // t tab U+0009
qi::lit("\x75") // uXXXX U+XXXX
>> _4HEXDIG [ append_utf8(qi::_val, qi::_1) ]
);
BOOST_SPIRIT_DEBUG_NODES((text_)(value_)(member_)(object_)(array_)(null_)(bool_))
}
private:
qi::rule<It, text()> text_, ch_;
qi::rule<It, null()> null_;
qi::rule<It, bool()> bool_;
qi::rule<It, value(), Skipper> value_;
qi::rule<It, member(), Skipper> member_;
qi::rule<It, object(), Skipper> object_;
qi::rule<It, array(), Skipper> array_;
struct append_utf8_f {
template <typename...> struct result { typedef void type; };
template <typename String, typename Codepoint>
void operator()(String& to, Codepoint codepoint) const {
auto out = std::back_inserter(to);
boost::utf8_output_iterator<decltype(out)> convert(out);
*convert++ = codepoint;
}
};
boost::phoenix::function<append_utf8_f> append_utf8;
qi::uint_parser<uint32_t, 16, 4, 4> _4HEXDIG;
};
template <typename Range, typename It = typename boost::range_iterator<Range const>::type>
value parse(Range const& input) {
grammar<It> g;
It first(boost::begin(input)), last(boost::end(input));
value parsed;
bool ok = qi::phrase_parse(first, last, g, qi::space, parsed);
if (ok && (first == last))
return parsed;
throw std::runtime_error("Remaining unparsed: '" + std::string(first, last) + "'");
}
namespace accessors {
static double dbl_(qd_json::value const&v) { return boost::get<double>(v); }
static int int_(qd_json::value const&v) { return boost::get<double>(v); }
static std::string txt_(qd_json::value const&v) { return boost::get<qd_json::text>(v); }
static qd_json::array arr_(qd_json::value const&v) { return boost::get<qd_json::array>(v); }
static qd_json::object obj_(qd_json::value const&v) { return boost::get<qd_json::object>(v); }
}
}
using It = std::string::const_iterator;
int main()
{
using namespace qd_json::accessors;
auto root = obj_(qd_json::parse(sample));
for(auto& updates : arr_(root["updates"]))
for(auto& first : arr_(updates))
{
std::cout << int_(first) << "\n";
return 0;
}
}
Prints:
4
The parser was of course courtesy older answers (Getting values from a json file using boost/property_tree, with multiple elements/arrays/sub-arrays C++) and if you need a more specific answer, I suggest you show the code that you had already got.
I need to parse following EBNF expression with Boost::Spirit.
period ::= date_part [time_part] , date_part [time_part]
time_part ::= hours:minutes[:seconds]
date_part ::= day.month.year
For example, 10.06.2014 10:00:15, 11.07.2014. I made my grammar in two ways, but can't exactly get working example.
1) First attempt
struct Parser: grammar<std::string::const_iterator, space_type>
{
Parser(): Parser::base_type(datetime_)
{
using boost::spirit::int_;
using boost::spirit::qi::_1;
using boost::spirit::qi::_2;
using boost::spirit::qi::_val;
datetime_ =
(date_ >> time_)
[
_val =
phoenix::construct<ptime>
(
date(_1[2]), _1[1], _1[0]),
hours(_2[0]) + minutes(_2[1]) + seconds[_2[0]]
)
|
_val =
phoenix::construct<ptime>
(
date(_1[2]), _1[1], _1[0]),
seconds(0)
)
];
date_ %= int_ % '.';
time_ %= int_ % ':';
BOOST_SPIRIT_DEBUG_NODE(datetime_);
BOOST_SPIRIT_DEBUG_NODE(date_);
BOOST_SPIRIT_DEBUG_NODE(time_);
}
rule<std::string::const_iterator, std::vector<int>(), space_type> date_, time_;
rule<std::string::const_iterator, ptime(), space_type> datetime_;
}
Parser parser;
std::string strTest("10.06.2014 10:00:15, 11.07.2014");
std::string::const_iterator it_begin(strTest.begin());
std::string::const_iterator it_end(strTest.end());
bool result = phrase_parse(it_begin, it_end, parser, space);
Errors:
/media/Data/Projects/Qt/Planner/parser.h:108: ошибка: no matching function for call to 'boost::gregorian::date::date(boost::phoenix::detail::make_index_composite<boost::phoenix::actor<boost::spirit::argument<0> >, int>::type)'
And so on. I can't cast boost::spirit::argument<0> to int or date::years_type. I tryed date((int)_1[2]), (int)_1[1], (int)_1[0])) and dynamic_cast<int>(_1[2]), but with no success (.
2) Second attempt
struct Parser: grammar<std::string::const_itearator, space_type>
{
Parser(ConditionTree& a_lTree):
Parser::base_type(time_period_),
m_lTree(a_lTree)
{
using boost::spirit::int_;
using boost::spirit::qi::_1;
using boost::spirit::qi::_2;
using boost::spirit::qi::_3;
using boost::spirit::qi::_4;
using boost::spirit::qi::_5;
using boost::spirit::qi::_val;
time_period_ = ( datetime_ > ',' > datetime_ ) [ _val = phoenix::construct<time_period>((int)_1, (int)_3) ];
datetime_ = (date_ >> time_duration_) [ _val = phoenix::construct<ptime>((int)_1, (int)_2) | _val = phoenix::construct<ptime>((int)_1, seconds(0)) ] ;
date_ = (int_ > '.' > int_ > '.' > int_) [ _val = phoenix::construct<date>((int)_5, (int)_3, (int)_1) ];
time_duration_ = (int_ > ':' > int_ > ':' > int_) [ _val = phoenix::construct<time_duration>((int)_1, (int)_3, (int)_5, 0)];
BOOST_SPIRIT_DEBUG_NODE(time_period_);
BOOST_SPIRIT_DEBUG_NODE(datetime_);
BOOST_SPIRIT_DEBUG_NODE(date_);
BOOST_SPIRIT_DEBUG_NODE(time_duration_);
}
rule<std::string::const_itarator, time_period(), space_type> time_period_;
rule<std::string::const_itarator, ptime(), space_type> datetime_;
rule<std::string::const_itarator, date(), space_type> date_;
rule<std::string::const_itarator, time_duration(), space_type> time_duration_;
ConditionTree& m_lTree;
};
Error:
/media/Data/Projects/Qt/Planner/parser.h:114: ошибка: invalid cast from type 'const _1_type {aka const boost::phoenix::actor<boost::spirit::argument<0> >}' to type 'int'...
Why I can't cast boost::spirit::argument<0> to int????
Better question, why would you be able to cast a placeholder type to a specific primitive type?
The place holder is a lazy actor only, so you should use Phoenix cast_ to cast it, if at all (hint: this should not be necessary): Live On Coliru
Output
<period_>
<try>10.06.2014 10:00:15,</try>
<date_>
<try>10.06.2014 10:00:15,</try>
<success> 10:00:15, 11.07.201</success>
<attributes>[[10, 6, 2014]]</attributes>
</date_>
<time_>
<try> 10:00:15, 11.07.201</try>
<success>, 11.07.2014</success>
<attributes>[[10, 0, 15]]</attributes>
</time_>
<date_>
<try> 11.07.2014</try>
<success></success>
<attributes>[[11, 7, 2014]]</attributes>
</date_>
<time_>
<try></try>
<fail/>
</time_>
<success></success>
<attributes>[[[[10, 6, 2014], [10, 0, 15]], [[11, 7, 2014], [empty]]]]</attributes>
</period_>
Parse success
Full Sample
#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
namespace Ast {
using boost::optional;
struct date { unsigned day, month, year; };
struct time { unsigned hours, minutes, seconds; };
struct date_time { date date_part; optional<time> time_part; };
struct period { date_time start, end; };
}
BOOST_FUSION_ADAPT_STRUCT(Ast::date, (unsigned,day)(unsigned,month)(unsigned,year))
BOOST_FUSION_ADAPT_STRUCT(Ast::time, (unsigned,hours)(unsigned,minutes)(unsigned,seconds))
BOOST_FUSION_ADAPT_STRUCT(Ast::date_time, (Ast::date,date_part)(Ast::optional<Ast::time>, time_part))
BOOST_FUSION_ADAPT_STRUCT(Ast::period, (Ast::date_time,start)(Ast::date_time,end))
template <typename Iterator>
struct Parser : qi::grammar<Iterator, Ast::period(), qi::space_type>
{
int test;
Parser() : Parser::base_type(period_)
{
using namespace qi;
static const int_parser<unsigned, 10, 2, 2> _2digit = {};
static const int_parser<unsigned, 10, 4, 4> _4digit = {};
time_ = _2digit >> ":" >> _2digit >> ":" >> _2digit;
date_ = _2digit >> "." >> _2digit >> "." >> _4digit;
date_time_ = date_ >> -time_;
period_ = date_time_ >> "," >> date_time_;
BOOST_SPIRIT_DEBUG_NODES((period_)(time_)(date_))
}
private:
qi::rule<Iterator, Ast::period(), qi::space_type> period_;
qi::rule<Iterator, Ast::date(), qi::space_type> date_;
qi::rule<Iterator, Ast::time(), qi::space_type> time_;
qi::rule<Iterator, Ast::date_time(), qi::space_type> date_time_;
};
int main()
{
using It = std::string::const_iterator;
Parser<It> parser;
std::string input("10.06.2014 10:00:15, 11.07.2014");
It f(input.begin()), l(input.end());
Ast::period parsed;
bool ok = qi::phrase_parse(f, l, parser, qi::space, parsed);
if (ok)
{
std::cout << "Parse success\n";
}
else
{
std::cout << "Parse failed\n";
}
if (f!=l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
I have some complicated structures and i want to extract their data from a text using
boost::spirit library (I've selected this one for efficiency purpose).
but i will ask my question in simpler way.
assume, we have two structures like these:
struct person
{
std::string name;
uint8_t age;
};
and
struct fruit
{
std::string color;
std::double average_weight;
};
and our text that included these data is presented below:
"... (jane, 23) (david, 19) (mary, 30) [yello,100] [green, 60.6] [red, 30.5]"
now, the problem is "extracting these data in suitable format"
for example by call handler for each struct or push_back them on vector.
any help would be greatly appreciated!
is there any code sample about that?!
call handlers for parsed structures.
#include <string>
#define BOOST_RESULT_OF_USE_DECLTYPE
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/qi.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
namespace fusion = boost::fusion;
struct person
{
std::string name;
uint8_t age;
};
BOOST_FUSION_ADAPT_STRUCT
(
person,
(std::string, name)
(uint8_t, age)
);
struct fruit
{
std::string color;
double average_weight;
};
BOOST_FUSION_ADAPT_STRUCT
(
fruit,
(std::string, color)
(double, average_weight)
);
template <typename _Iterator>
struct parser :
qi::grammar<_Iterator, void(), ascii::space_type>
{
parser() :
parser::base_type(main)
{
main =
*(
_person[ ([](const person &person_)
{
// Add handler here
}) ]
| _fruit[ ([](const fruit &fruit_)
{
// Add handler here
}) ]
);
_person = qi::lit('(') >> *(qi::char_ - ',') >> ',' >> qi::ushort_ >> ')';
_fruit = qi::lit('[') >> *(qi::char_ - ',') >> ',' >> qi::double_ >> ']';
}
qi::rule<_Iterator, void(), ascii::space_type> main;
qi::rule<_Iterator, person(), ascii::space_type> _person;
qi::rule<_Iterator, fruit(), ascii::space_type> _fruit;
};
int main()
{
typedef std::string::const_iterator iterator;
std::string input_ = "(jane, 23000) (david, 19) (mary, 30) [yello,100] [green, 60.6] [red, 30.5]";
iterator iterator_ = std::begin(input_);
bool result_ = qi::phrase_parse(iterator_, iterator(std::end(input_)), parser<iterator>(), ascii::space)
&& iterator_ == std::end(input_);
return 0;
}
P.S. Not all compiler can build that code because of lambdas in semantic actions. (msvs don't) In this case you have to use something else (phoenix::bind for example)
store parsed structures in a vector
typedef boost::variant <
person,
fruit
> variant;
template <typename _Iterator>
struct parser :
qi::grammar<_Iterator, std::vector < variant > (), ascii::space_type>
{
parser() :
parser::base_type(main)
{
main = *(_person | _fruit);
_person = qi::lit('(') >> *(qi::char_ - ',') >> ',' >> qi::ushort_ >> ')';
_fruit = qi::lit('[') >> *(qi::char_ - ',') >> ',' >> qi::double_ >> ']';
}
qi::rule<_Iterator, std::vector < variant > (), ascii::space_type> main;
qi::rule<_Iterator, person(), ascii::space_type> _person;
qi::rule<_Iterator, fruit(), ascii::space_type> _fruit;
};
I have a map of string-rule pairs and I would like to create a "joint rule"(rule_t joint_rule;) of them somehow. If I do this this way:
joint_rule = convert_logformat["%h"] >> convert_logformat["%t"];
than the joint rule with the parse_phrase matches the string
std::string entry = "127.0.0.1 [16/Aug/2012:01:50:02 +0000]";
But if I create the joint rule this way:
for (it = convert_logformat.begin(); it != convert_logformat.end(); it++)
{
joint_rule = joint_rule.copy() >> (*it).second.copy();
}
It does not match the same string. Why? How could I achieve something similar to the latter?
Relevant code:
template <typename Iterator>
bool parse_logentry(Iterator first, Iterator last, std::vector<char>& ip, std::vector<char>& timestamp, std::vector<char>& req, unsigned int& status, unsigned int& transferred_bytes, std::vector<char>& referer, std::vector<char>& ua)
{
using boost::spirit::qi::char_;
using boost::spirit::qi::int_;
using boost::spirit::qi::uint_;
using boost::spirit::qi::phrase_parse;
using boost::spirit::ascii::space;
using boost::spirit::ascii::space_type;
using boost::phoenix::ref;
using boost::phoenix::push_back;
using boost::spirit::qi::_1;
using boost::spirit::qi::lexeme;
using boost::spirit::qi::rule;
typedef boost::spirit::qi::rule<Iterator, std::string(), space_type> rule_t;
rule_t ip_rule, timestamp_rule, user_rule, req_rule, ref_rule, ua_rule, bytes_rule, status_rule;
ip_rule %= lexeme[(+char_("0-9."))[ref(ip) = _1]];
timestamp_rule %= lexeme[('[' >> +(~char_(']')) >> ']')[ref(timestamp) = _1]];
user_rule %= lexeme[(+~char_(" "))];
req_rule %= lexeme[('"' >> +(~char_('"')) >> '"')[ref(req) = _1]];
ref_rule %= lexeme[('"' >> +(~char_('"')) >> '"')[ref(referer) = _1]];
ua_rule %= lexeme[('"' >> +(~char_('"')) >> '"')[ref(ua) = _1]];
bytes_rule %= uint_[ref(transferred_bytes) = _1];
status_rule %= uint_[ref(status) = _1];
std::map<std::string, rule_t> convert_logformat;
typename std::map<std::string, rule_t>::iterator it;
convert_logformat.insert(std::pair<std::string, rule_t>("%h", ip_rule));
convert_logformat.insert(std::pair<std::string, rule_t>("%t", timestamp_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%r", req_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%>s", status_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%b", bytes_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%u", user_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%{User-agent}i", ua_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%{Referer}i", ref_rule));
rule_t joint_rule;
//joint_rule = convert_logformat["%h"] >> convert_logformat["%t"];
for (it = convert_logformat.begin(); it != convert_logformat.end(); it++)
{
joint_rule = joint_rule.copy() >> (*it).second.copy();
std::cout << (*it).first << ": " << typeid((*it).second).name() << "\n";
}
std::cout << "convert_logformath: " << typeid(convert_logformat["%h"]).name() << "\n";
bool r = phrase_parse(first, last, joint_rule, space);
if (first != last)
return false;
return r;
}
Ahem. It is really quite simple. You should initialize your variables :)
rule_t joint_rule; // what is it initialized to?
for (auto it = convert_logformat.begin(); it != convert_logformat.end(); it++)
{
joint_rule = joint_rule.copy() >> (*it).second.copy();
}
Change the first line to
rule_t joint_rule = qi::eps;
And it works:
sehe#mint12:/tmp$ ./test
127.0.0.1
16/Aug/2012:01:50:02 +0000
Your parser lacks some (good) common practice. See below for tidied up source (C++11).
Note that using a map to store the rules looks odd, because maps iteration will order by the key, not insertion order.
See the code live at http://liveworkspace.org/code/a7f2f94840d63fce43d8c3f56236330e
// #define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <typeinfo>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
template <typename Iterator>
struct Grammar : qi::grammar<Iterator, std::string(), qi::space_type>
{
Grammar() : Grammar::base_type(joint_rule)
{
using namespace qi;
ip_rule %= lexeme[ (+char_("0-9."))[phx::ref(ip) = _1] ];
timestamp_rule %= lexeme[ ('[' >> +(~char_(']')) >> ']')[phx::ref(timestamp) = _1] ];
user_rule %= lexeme[ (+~char_(" ")) ];
req_rule %= lexeme[ ('"' >> +(~char_('"')) >> '"')[phx::ref(req) = _1] ];
ref_rule %= lexeme[ ('"' >> +(~char_('"')) >> '"')[phx::ref(referer) = _1] ];
ua_rule %= lexeme[ ('"' >> +(~char_('"')) >> '"')[phx::ref(ua) = _1] ];
bytes_rule %= uint_[phx::ref(transferred_bytes) = _1 ];
status_rule %= uint_[phx::ref(status) = _1 ];
auto convert_logformat = std::map<std::string, rule_t> {
{ "%h" , ip_rule } ,
{ "%t" , timestamp_rule },
// { "%r" , req_rule } ,
// { "%>s" , status_rule } ,
// { "%b" , bytes_rule } ,
// { "%u" , user_rule } ,
// { "%{User-agent}i", ua_rule } ,
// { "%{Referer}i" , ref_rule }
};
joint_rule = eps;
for (auto const& p: convert_logformat)
{
joint_rule = joint_rule.copy() >> p.second.copy();
}
BOOST_SPIRIT_DEBUG_NODE(ip_rule);
BOOST_SPIRIT_DEBUG_NODE(timestamp_rule);
BOOST_SPIRIT_DEBUG_NODE(user_rule);
BOOST_SPIRIT_DEBUG_NODE(req_rule);
BOOST_SPIRIT_DEBUG_NODE(ref_rule);
BOOST_SPIRIT_DEBUG_NODE(ua_rule);
BOOST_SPIRIT_DEBUG_NODE(bytes_rule);
BOOST_SPIRIT_DEBUG_NODE(status_rule);
}
typedef qi::rule<Iterator, std::string(), qi::space_type> rule_t;
rule_t ip_rule, timestamp_rule, user_rule, req_rule, ref_rule, ua_rule, bytes_rule, status_rule;
rule_t joint_rule;
std::vector<char> ip;
std::vector<char> timestamp;
std::vector<char> req;
unsigned int status;
unsigned int transferred_bytes;
std::vector<char> referer;
std::vector<char> ua;
};
template <typename Iterator>
bool parse_logentry(Iterator first, Iterator last,
Grammar<Iterator>& parser)
{
bool r = phrase_parse(first, last, parser, qi::space);
return (r && (first == last));
}
int main(void)
{
std::string entry = "127.0.0.1 [16/Aug/2012:01:50:02 +0000]";
//std::string entry = "127.0.0.1 [16/Aug/2012:01:50:02 +0000] \"GET /check.htm HTTP/1.1\" 200 17 \"-\" \"AgentName/0.1 libwww-perl/5.833\"";
Grammar<std::string::iterator> parser;
if (parse_logentry(entry.begin(), entry.end(), parser))
{
for (auto i : parser.ip)
std::cout << i;
std::cout << "\n";
for (auto ts: parser.timestamp)
std::cout << ts;
std::cout << "\n";
}
else
{
std::cout << "not ok\n";
}
return 0;
}
Note that, among other things, this setup allows you to enable debugging of your grammar by simply defining BOOST_SPIRIT_DEBUG at the start.