Idiomatic complete matching with post skipping - c++14

What is the most idiomatic way to do post-skipping? More specific I want to ensure there is no "non-skippable" (garbage) characters in my input after matching my top rule.
auto const blankOrComment
= ascii::space
| x3::lexeme ['#' >> *(x3::char_ - x3::eol) >> -x3::eol ]
;
auto const program = rule<AstProgram>("program")
= *(as<AstDefinition> (definition > ";"))
;
auto const programEntry = x3::skip(blankOrComment) [program];
One idea, I consider quite ugly was to do a separate parse call for the blankOrComment afterwards, if the main iterator position is not the end iterator. The current better idea I have is to change the root rule:
auto const programEntry = x3::skip(blankOrComment) [program >> x3::omit[*blankOrComment]];
Is there a more idiomatic way?

The simplest hack is to tack on >> eps: Live On Coliru
Note I'd strive to make the skipper more self-descriptive:
auto const skipper
= space
| '#' >> *(char_ - eol) >> (eol|eoi)
;
Likewise you can make that postskip hack more self-descriptive:
auto const post_skip = eps;
auto const program = "program" >> post_skip;
Live On Coliru
#include <iostream>
#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
namespace Parser {
namespace x3 = boost::spirit::x3;
namespace rules {
using namespace x3;
auto const skipper
= space
| '#' >> *(char_ - eol) >> (eol|eoi)
;
auto const post_skip = eps;
auto const program = "program" >> post_skip;
}
auto const programEntry = x3::skip(rules::skipper) [rules::program];
}
int main() {
using It = std::string::const_iterator;
for (std::string const input : {
"",
" program ",
"#hello\n program # comment\n",
}) {
It f = input.begin(), l = input.end();
if(parse(f, l, Parser::programEntry)) {
std::cout << "Parse success\n";
} else {
std::cout << "Parse failed\n";
}
std::cout << "Remaining: '" << std::string(f,l) << "'\n";
}
}
Prints
Parse failed
Remaining: ''
Parse success
Remaining: ''
Parse success
Remaining: ''

Related

apply 'with<>' to a skipper parser to get context

My first version of this question was rich with misunderstandings. My answer below suits my needs. But I kept at it to understand what could be done with with<>. What I get is that it intended to inject context into a parser. Then the parser is called from with_directive::parse (in x3's with.hpp) In the following code, that is just what happens.
#include <boost/spirit/home/x3.hpp>
using namespace boost::spirit::x3;
struct eol_parser_cnt : parser<eol_parser_cnt>
{
struct context {
int line = 0;
std::string::iterator iter_pos;
};
template <typename Iterator, typename Context, typename Attribute>
bool parse(Iterator& first, Iterator const& last
, Context const& context, unused_type, Attribute& attr) const
{
//std::cout << context.line;
auto& ctx = context;
return boost::spirit::x3::parse(first, last, lit(' ') | (lit("//") >> *(char_ - eol) >> eol));
}
};
const auto& our_skipper = eol_parser_cnt{};
eol_parser_cnt::context lines;
auto with_skipper = with<eol_parser_cnt::context>(lines)[our_skipper];
int main()
{
std::string str("12 word");
auto first = str.begin();
phrase_parse(first, str.end(), int_ >> *char_("a-z"), with_skipper);
}
Putting a break point in eol_parser_cnt::parse and I see it working. The debugger consistently shows the context is there and that it is the structure of eol_parser_cnt::context. I can change the value of line in the debugger and the next hit shows that value, it is a real object. But, try to uncomment the line std::cout << context.line; and the compiler complains that is it an unused_type. So, I just don't get it.
test.cpp(15,30): error C2039: 'line': is not a member of 'boost::spirit::x3::context<ID,T,Context>'
with
[
ID=eol_parser_cnt::context,
T=eol_parser_cnt::context,
Context=boost::spirit::x3::unused_type
]
F:\cpp\boost_1_76_0\boost\spirit\home\x3\support\context.hpp(18): message : see declaration of 'boost::spirit::x3::context<ID,T,Context>'
with
[
ID=eol_parser_cnt::context,
T=eol_parser_cnt::context,
Context=boost::spirit::x3::unused_type
]
F:\cpp\boost_1_76_0\boost\spirit\home\x3\directive\with.hpp(62): message : see reference to function template instantiation 'bool eol_parser_cnt::parse<Iterator,boost::spirit::x3::context<ID,T,Context>,Attribute>(Iterator &,const Iterator &,const boost::spirit::x3::context<ID,T,Context> &,boost::spirit::x3::unused_type,Attribute &) const' being compiled
with
[
Iterator=std::_String_iterator<std::_String_val<std::_Simple_types<char>>>,
ID=eol_parser_cnt::context,
T=eol_parser_cnt::context,
Context=boost::spirit::x3::unused_type,
Attribute=const boost::spirit::x3::unused_type
]
Well, it took a while but I was going to understand this. I re-read C++ Template Metaprogramming seriously this time. Then looking at the x3 code I finally understood that with<> is just another parser wrapper. It is interesting to debug an optimized build and see just how much code disappears. VS shows all the disappeared stuff on the stack as inlined and what was 9 layers of parser calls becomes 2 into the likes of with_error_handling::on_error. Everything from my call to parse_rhs_main (in rule.hpp, line 232), is gone.
So because with<> is just another parser wrapper and if the skipper is wrapped, the context is only avaliable to the skipper. But no big deal as the skipper is in the context of the main parser. The difference is that in the skipper object a get<skipper_eol_cnt>(context) returns a reference to the skipper_eol_cnt::context{}. Whereas in the main parser we have to us get<skipper_tag>(context) and this returns the with<> parser object. val is a member of that parser, the reference to the skipper_eol_cnt::context{}. So get<skipper_tag>(context).val retrieves the context we are looking for.
So here it is, using with<> applied to a skipper.
#include<iostream>
#include <iomanip>
#include <vector>
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted.hpp>
using namespace boost::spirit::x3;
struct skipper_eol_cnt : parser<skipper_eol_cnt>
{
struct context {
int line = 1;
std::string::iterator iter_pos;
};
template <typename Iterator, typename Context, typename Attribute>
bool parse(Iterator& first, Iterator const& last
, Context const& context, unused_type, Attribute& attr) const
{
const char* start_cmt = "/*";
const char* end_cmt = "*/";
if (first == last)
return false;
bool matched = false;
//here we are getting from the 'with<>' wrapper
auto& ctx = get<skipper_eol_cnt>(context);
//skip: space | '//comment'
boost::spirit::x3::parse(first, last, lit(' ') | (lit("//") >> *(char_ - eol)));//eol counted below
//skip: '/*comment*/'
if (detail::string_parse(start_cmt, first, last, unused, case_compare<Iterator>())) {
for (; first != last; ++first) {
if (detail::string_parse(end_cmt, first, last, unused, case_compare<Iterator>()))
break;
if (*first == '\n')
++ctx.line, ctx.iter_pos = first;
}
}
Iterator iter = first;
for (; iter != last && (*iter == '\r' || *iter == '\n'); ++iter) {
matched = true;
if (*iter == '\n') // LF
++ctx.line, ctx.iter_pos = iter;
}
//{static int pos = 0; if (pos < ctx.line) { pos = ctx.line; std::cout << pos << std::endl; }}
if (matched) first = iter;
return matched;
}
};
auto const& skip_eol_cnt = skipper_eol_cnt{};
struct with_error_handling {
template<typename It, typename Ctx>
error_handler_result on_error(It f, It l, expectation_failure<It> const& ef, Ctx const& ctx) const {
It erit = f + std::distance(f, ef.where());
//here we are getting the wrapped skipper so need the 'with<>.val'
const auto& sctx = get<skipper_tag>(ctx).val;
It bit = erit;
for (; *bit != '\n'/* && bit != f*/; --bit)
;
It eit = erit;
for (; *eit != '\n' && eit != l; ++eit)
;
int str_pos = erit - bit - 1;
std::ostringstream oss;
oss << "Expecting " << ef.which() << "\n at line: " << sctx.line
<< "\n\t" << std::string(bit + 1, eit)
<< "\n\t" << std::setw(str_pos) << std::setfill('-') << "" << "^";
get<with_error_handling>(ctx).push_back(oss.str());;
return error_handler_result::fail;
}
};
//attr sections
struct section_type {
std::string name;
int line;
std::string::iterator iter_pos;
};
BOOST_FUSION_ADAPT_STRUCT(section_type, name)
using sections_type = std::vector<section_type>;
struct parser_find_sections : parser<parser_find_sections> {
template<typename Iterator, typename Context, typename RContext, typename Attribute>
bool parse(Iterator& first, Iterator const& last, Context const& context, RContext const& rcontext, Attribute& section) const {
const auto& sssctx = get<skipper_eol_cnt>(context); //now here this doesn't work, unused_type, but
const auto& sctx = get<skipper_tag>(context).val;
auto to_line = [&sctx, first](auto& ctx) {
_attr(ctx).line = sctx.line;
//_attr(ctx).iter_pos = first; // this one will get at 'section color(x,x)'
_attr(ctx).iter_pos = _where(ctx).begin(); // this one is '(x,x)'
};
static_assert(BOOST_VERSION / 100 % 1000 >= 77);
////NOTE!!! if you have a boost version of less than 1.77, x3::seek will fail here
////quick fix, copy from: https://github.com/boostorg/spirit/blob/boost-1.78.0/include/boost/spirit/home/x3/directive/seek.hpp
////and paste to your boost file...
return phrase_parse(first, last, *(seek["section"] >> (*alpha)[to_line]), get<skipper_tag>(context), section);
}
};
auto const parse_section = rule<with_error_handling, std::pair<int, int>>("the_sec_parser") = [] {
return '(' > int_ > ',' > int_ > ')';
}();
template<typename T>
std::ostream& operator << (std::ostream& os, std::pair<T, T>& t) {
return os << t.first << ',' << t.second;
}
//errors
std::vector<std::string> errors;
auto with_errors = with<with_error_handling>(errors)[parse_section];
auto test_section = [](auto& content, auto& section) {
//attr
std::pair<int, int> attr;
skipper_eol_cnt::context ctx{ section.line, section.iter_pos };
auto with_skip_cnt = with< skipper_eol_cnt>(ctx)[skip_eol_cnt];
auto first(section.iter_pos);
return std::tuple(phrase_parse(first, content.end(), with_errors, with_skip_cnt, attr), attr);
};
int main() {
std::string str(R"(//line 1
section red(5, 6)
section green( 7, 8) //line 3
section blue(9, 10) //no error
/*comment
bunch of lines of stuff....
*/ section white(11, a 12) //error on line 7
section black( 13,14)
)");
//get the list of sections
auto with_skip_cnt = with<skipper_eol_cnt>(skipper_eol_cnt::context{})[skip_eol_cnt];
sections_type secs;
auto first(str.begin());
phrase_parse(first, str.end(), parser_find_sections(), with_skip_cnt, secs);
for (auto& item : secs)
std::cout << item.name << "\t at line: " << item.line << std::endl;
//section 'blue', at 2, has no error
auto [r, attr] = test_section(str, secs.at(2));
if (r)
std::cout << "\nthe " << secs.at(2).name << " hase vals: " << attr << "\n\n";
//section 'white', at 3, has an error
test_section(str, secs.at(3));
if (errors.size())
std::cout << errors.front() << std::endl;
return 0;
}

Ceres Solver for logistic growth Curve Fit

I attempt to test how well ceres solver autodiff in fitting a simple logistic growth curve against actual observed data , to my surprise the solver seemed unable to provide solution . Using other c++ solvers , result can be easily obtained with ease , k=9643.61,c=84.61 and b=3.8121. I am not sure is the code having issue or just the ceres solver autodiff are not that well built? Any advice pls?
below is the sample
#include "ceres/ceres.h"
#include "glog/logging.h"
#include <cmath>
#include <iostream>
#include <stdio.h>
using ceres::AutoDiffCostFunction;
using ceres::CauchyLoss;
using ceres::CostFunction;
using ceres::Problem;
using ceres::Solve;
using ceres::Solver;
struct ExponentialResidual
{
ExponentialResidual(double x, double y)
: x_(x), y_(y) {}
template <typename T>
bool operator()(const T *const k,
const T *const c,
const T *const b,
//const T* const g,
T *residual) const
{
residual[0] = y_ - (k[0] / (1.0 + (pow((x_ / c[0]), b[0]))));
return true;
}
private:
const double x_;
const double y_;
};
const int kNumObservations = 247;
double data[] = {
0,3,
1,4,
2,4,
3,4,
4,7,
5,8,
6,8,
7,8,
8,8,
9,8,
10,10,
11,12,
12,12,
13,12,
14,16,
15,16,
16,18,
17,18,
18,18,
19,19,
20,19,
21,22,
22,22,
23,22,
24,22,
25,22,
26,22,
27,22,
28,22,
29,22,
30,22,
31,22,
32,22,
33,23,
34,23,
35,25,
36,29,
37,32,
38,36,
39,50,
40,55,
41,83,
42,93,
43,99,
44,117,
45,129,
46,149,
47,158,
48,197,
49,238,
50,428,
51,553,
52,673,
53,790,
54,900,
55,1030,
56,1183,
57,1306,
58,1518,
59,1624,
60,1796,
61,2031,
62,2161,
63,2320,
64,2470,
65,2626,
66,2766,
67,2908,
68,3116,
69,3333,
70,3483,
71,3662,
72,3793,
73,3963,
74,4119,
75,4228,
76,4346,
77,4530,
78,4683,
79,4817,
80,4987,
81,5072,
82,5182,
83,5251,
84,5305,
85,5389,
86,5425,
87,5482,
88,5532,
89,5603,
90,5691,
91,5742,
92,5780,
93,5820,
94,5851,
95,5945,
96,6002,
97,6071,
98,6176,
99,6298,
100,6353,
101,6383,
102,6428,
103,6467,
104,6535,
105,6589,
106,6656,
107,6726,
108,6742,
109,6779,
110,6819,
111,6855,
112,6872,
113,6894,
114,6941,
115,6978,
116,7009,
117,7059,
118,7137,
119,7185,
120,7245,
121,7417,
122,7604,
123,7619,
124,7629,
125,7732,
126,7762,
127,7819,
128,7857,
129,7877,
130,7970,
131,8247,
132,8266,
133,8303,
134,8322,
135,8329,
136,8336,
137,8338,
138,8369,
139,8402,
140,8445,
141,8453,
142,8494,
143,8505,
144,8515,
145,8529,
146,8535,
147,8556,
148,8572,
149,8587,
150,8590,
151,8596,
152,8600,
153,8606,
154,8616,
155,8634,
156,8637,
157,8639,
158,8640,
159,8643,
160,8648,
161,8658,
162,8663,
163,8668,
164,8674,
165,8677,
166,8683,
167,8696,
168,8704,
169,8718,
170,8725,
171,8729,
172,8734,
173,8737,
174,8755,
175,8764,
176,8779,
177,8800,
178,8815,
179,8831,
180,8840,
181,8861,
182,8884,
183,8897,
184,8904,
185,8943,
186,8956,
187,8964,
188,8976,
189,8985,
190,8999,
191,9001,
192,9002,
193,9023,
194,9038,
195,9063,
196,9070,
197,9083,
198,9094,
199,9103,
200,9114,
201,9129,
202,9149,
203,9175,
204,9200,
205,9212,
206,9219,
207,9235,
208,9240,
209,9249,
210,9257,
211,9267,
212,9274,
213,9285,
214,9291,
215,9296,
216,9306,
217,9317,
218,9334,
219,9340,
220,9354,
221,9360,
222,9374,
223,9385,
224,9391,
225,9397,
226,9459,
227,9559,
228,9583,
229,9628,
230,9810,
231,9868,
232,9915,
233,9946,
234,9969,
235,10031,
236,10052,
237,10147,
238,10167,
239,10219,
240,10276,
241,10358,
242,10505,
243,10576,
244,10687,
245,10769,
246,10919,
};
int main(int argc, char const *argv[])
{
google::InitGoogleLogging(argv[0]);
double k = 20000.0;
//double c=0.5;
double c = kNumObservations / 2.0;
double b = 0.5;
double g = 1.0;
Problem problem;
for (int i = 0; i < kNumObservations; i++)
{
problem.AddResidualBlock(
new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1, 1>(
new ExponentialResidual(data[2 * i]*1.00, data[2 * i + 1]*1.00)),
new CauchyLoss(0.5), &k, &c, &b);
}
Solver::Options options;
options.max_num_iterations = 1000;
options.linear_solver_type = ceres::DENSE_QR;
//options.trust_region_strategy_type=ceres::DOGLEG;
//options.gradient_tolerance=1e-8;
//options.parameter_tolerance=1e-10;
//options.function_tolerance=1e-8;
options.minimizer_progress_to_stdout = true;
Solver::Summary summary;
Solve(options, &problem, &summary);
//std::cout<<summary.BriefReport()<<std::endl;
std::cout << summary.FullReport() << std::endl;
std::cout << "Final k: " << k << " c: " << c << " b: " << b << " g: " << g << "\n";
/* code */
return 0;
}
look like if I ignore the first observed data 0,3 , ceres solver is able to obtain result !

Boost spirit skip parser with at least one whitespace

In the grammar i'm implementing, there are elements separated by whitespace. With a skip parser, the spaces between the elements are skipped automatically, but this also allows no space, which is not what i want. Sure, i could explicitly write a grammar that includes these spaces, but it seems to me (with the complexity and flexibility offered by spirit) that there is a better way to do this. Is there?
Here is an example:
#include <cstdlib>
#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main(int argc, char** argv)
{
if(argc != 2)
{
std::exit(1);
}
std::string str = argv[1];
auto iter = str.begin();
bool r = qi::phrase_parse(iter, str.end(), qi::char_ >> qi::char_, qi::blank);
if (r && iter == str.end())
{
std::cout << "parse succeeded\n";
}
else
{
std::cout << "parse failed. Remaining unparsed: " << std::string(iter, str.end()) << '\n';
}
}
This allows ab as well as a b. I want only the latter to be allowed.
Related to this: How do the skip parsers work, exactly? One supplies something like qi::blank, is then the kleene star applied to form the skip parser? I would like to get some enlightenment here, maybe this also helps on solving this problem.
Additional information: My real parser looks something like this:
one = char_("X") >> repeat(2)[omit[+blank] >> +alnum] >> qi::omit[+qi::blank] >> +alnum;
two = char_("Y") >> repeat(3)[omit[+blank] >> +alnum];
three = char_("Z") >> repeat(4)[omit[+blank] >> +alnum] >> qi::omit[+qi::blank] >> +alnum;
main = one | two | three;
which makes the grammar quite noisy, which i would like to avoid.
First off, the grammar specs I usually see this kind of requirement in are (always?) RFCs. In 99% of cases there is no issue, consider e.g.:
myrule = skip(space) [ uint_ >> uint_ ];
This already implicitly requires at least 1 whitespace character between the numbers, for the simple reason that there would be 1 number, otherwise. The same simplification occurs in surprisingly many cases (see e.g. the simplifications made around the ubiquitous WSP productions in this answer last week Boost.Spirit qi value sequence vector).
With that out of the way, skippers apply zero or more times, by definition, so no there is not a way to get what you want with an existing stateful directive like skip(). See also http://stackoverflow.com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965 or the docs - under lexeme, [no_]skip and skip_flag::dont_postskip).
Looking at your specific grammar, I'd do this:
bool r = qi::phrase_parse(iter, end, token >> token, qi::blank);
Here, you can add a negative lookahead assertion inside a lexeme to assert that "the end of the token was reached" - which in your parser would be mandated as !qi::graph:
auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);
See a demo:
Live On Coliru
#include <iostream>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main() {
for (std::string const str : { "ab", " ab ", " a b ", "a b" }) {
auto iter = str.begin(), end = str.end();
auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);
bool r = qi::phrase_parse(iter, end, token >> token, qi::blank);
std::cout << " --- " << std::quoted(str) << " --- ";
if (r) {
std::cout << "parse succeeded.";
} else {
std::cout << "parse failed.";
}
if (iter != end) {
std::cout << " Remaining unparsed: " << std::string(iter, str.end());
}
std::cout << std::endl;
}
}
Prints
--- "ab" --- parse failed. Remaining unparsed: ab
--- " ab " --- parse failed. Remaining unparsed: ab
--- " a b " --- parse succeeded.
--- "a b" --- parse succeeded.
BONUS Review notes
My guidelines would be:
your skipper should be the grammar's responsibility. It's sad that all Qi samples lead people to believe you need to let the caller decide that
end-iterator checking does not equal error-checking. It's very possible to parse things correctly without consuming all input. Which is why reporting the "remaining input" should not just happen in the case that parsing failed.
If trailing unparsed input is an error, spell it out:
Live On Coliru
#include <iostream>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main() {
for (std::string const str : { "ab", " ab ", " a b ", "a b happy trees are trailing" }) {
auto iter = str.begin(), end = str.end();
auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);
bool r = qi::parse(iter, end, qi::skip(qi::space) [ token >> token >> qi::eoi ]);
std::cout << " --- " << std::quoted(str) << " --- ";
if (r) {
std::cout << "parse succeeded.";
} else {
std::cout << "parse failed.";
}
if (iter != end) {
std::cout << " Remaining unparsed: " << std::quoted(std::string(iter, str.end()));
}
std::cout << std::endl;
}
}
Prints
--- "ab" --- parse failed. Remaining unparsed: "ab"
--- " ab " --- parse failed. Remaining unparsed: " ab "
--- " a b " --- parse succeeded.
--- "a b happy trees are trailing" --- parse failed. Remaining unparsed: "a b happy trees are trailing"

boost last_write_time is back one hour

I have trying to solve a bug on the following code, where I get an hour difference from boost last_write_time.
To explain it better: I create a file, and then I try to extract the time it was created with boost::filesystem::path.
void PrintTime(boost::filesystem::path _file) {
time_t sys_time{ last_write_time(_file) };
ptime p_time{ boost::posix_time::from_time_t(sys_time) };
boost::posix_time::time_duration time_dur{ p_time.time_of_day() };
long h{ time_dur.hours() }; //1a
long m{ time_dur.minutes() };
long s{ time_dur.seconds() };
//...print h, m, s.
}
//1a: Here when for example the time I expect is 12:34:56,
//I always get 11:34:56
Any idea why is that?
Is there timezone somewhere in boost last_write_time?
My os displays the right time when I check the file through the system.
You have to translate to the "presentation" time-zone, like "when [you] check the file through the system". The timestamp from the filesystem is UTC time.
E.g. if you do
std::cout << boost::posix_time::second_clock::local_time() << "\n";
std::cout << boost::posix_time::second_clock::universal_time() << "\n";
you'll probably get
2018-Feb-27 16:03:12
2018-Feb-27 15:03:12
Fix:
#include <boost/date_time/c_local_time_adjustor.hpp>
void PrintTime(boost::filesystem::path _file) {
using boost::posix_time::ptime;
using adj = boost::date_time::c_local_adjustor<ptime>;
time_t const sys_time = last_write_time(_file);
ptime const utc = boost::posix_time::from_time_t(sys_time);
ptime const local = adj::utc_to_local(utc);
DEMO
See it Live On Coliru
#include <boost/filesystem.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <boost/date_time/posix_time/conversion.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/c_local_time_adjustor.hpp>
void PrintTime(boost::filesystem::path _file) {
using boost::posix_time::ptime;
using adj = boost::date_time::c_local_adjustor<ptime>;
time_t const sys_time = last_write_time(_file);
ptime const utc = boost::posix_time::from_time_t(sys_time);
ptime const local = adj::utc_to_local(utc);
std::cout << "utc: " << utc << "\n";
std::cout << "local: " << local << "\n";
{
long h{ local.time_of_day().hours() };
long m{ local.time_of_day().minutes() };
long s{ local.time_of_day().seconds() };
//...print h, m, s.
std::cout << h << ":" << m << ":" << s << '\n';
}
}
int main() {
PrintTime("main.cpp");
}
Prints (on my system):
utc: 2018-Feb-27 15:19:45
local: 2018-Feb-27 16:19:45
16:19:45

rules working on 1.46 boost::spirit and stopped working on boost spirit 1.55

constant_double_quotation_string %= char_( '"' ) >>
*( spirit::qi::string( "\\\"" )[ _val += _1 ] |
( char_ - '"' ) ) >> char_( '"' );
constant_single_quotation_string %= char_( '\'' ) >>
*( spirit::qi::string( "\\\'" )[ _val += _1 ] |
( char_ - '\'' ) ) >> char_( '\'' );
now it is saying char is not a class or structure or union type with gcc 4.7.2?
Elaborating on my earlier answer
In case you actually do want to expose the unescaped value, I'd suggest:
not using raw (obviously, because we don't wish to mirror the exact input sequence in the presence of escaped characters)
still not using semantic actions
instead playing clever with lit('\\') to match the escape character without adding it to the output sequence.
Here I chose to use a single rule definition for both the double-/single quoted literal parsers. Instead, I pass in the expected quote character as an inherited attribute:
qi::rule<It, std::string(char)>
q_literal;
q_literal = lit(_r1) >> *('\\' >> char_ | (char_ - lit(_r1))) >> lit(_r1);
start = q_literal('"') | q_literal('\'');
Demo
Live On Coliru
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
template <typename It, typename Skipper = qi::space_type>
struct my_grammar : qi::grammar<It, std::string(), Skipper> {
my_grammar() : my_grammar::base_type(start) {
using namespace qi;
start = q_literal('"') | q_literal('\'');
q_literal = lit(_r1) >> *('\\' >> char_ | (char_ - lit(_r1))) >> lit(_r1);
BOOST_SPIRIT_DEBUG_NODES( (start)(q_literal) )
}
private:
qi::rule<It, std::string(), Skipper> start;
// drop skipper to make these rules implicitly 'lexeme'
// see: https://stackoverflow.com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965
qi::rule<It, std::string(char)> q_literal;
};
int main() {
using It = std::string::const_iterator;
my_grammar<It> g;
for (std::string const& input : {
"\"hello world\"",
"\"hello \\\"world\\\"\"",
"'bye world'",
"'bye \"\\'world\\'\"'",
"bogus" })
{
std::cout << "\n------- Parsing: " << input << '\n';
It f = input.begin(), l = input.end();
std::string result;
bool ok = qi::phrase_parse(f, l, g, qi::space, result);
if (ok)
std::cout << "Parse success: " << result << "\n";
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed input '" << std::string(f,l) << "'\n";
}
}
Printing the unescaped literals:
------- Parsing: "hello world"
Parse success: hello world
------- Parsing: "hello \"world\""
Parse success: hello "world"
------- Parsing: 'bye world'
Parse success: bye world
------- Parsing: 'bye "\'world\'"'
Parse success: bye "'world'"
------- Parsing: bogus
Parse failed
Remaining unparsed input 'bogus'
You don't even specify the declared type of the constant_single_quotation_string rule.
Here's some observations and a working approach:
Since you
apparently do not want the synthesized attribute value to be the input sequence unescaped you can simply use the qi::raw[] directive to mirror the input sequence directly. This way you can simplify the rule itself
You don't need %= (auto rule assignment) or semantic actions ([_val+=_1]) at all; ¹
Instead if you e.g. didn't want the opening/closing quotes as a part
of the attribute, just replace qi::char_('"') with qi::lit('"') (or indeed, just '"')
Simplified:
qi::rule<It, std::string()>
dq_literal,
sq_literal;
dq_literal = raw [ '"' >> *("\\\"" | ~char_('"')) >> '"' ];
sq_literal = raw [ "'" >> *("\\'" | ~char_("'")) >> "'" ];
Full Demo
Live On Coliru
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
template <typename It, typename Skipper = qi::space_type>
struct my_grammar : qi::grammar<It, std::string(), Skipper> {
my_grammar() : my_grammar::base_type(start) {
using namespace qi;
start = dq_literal
| sq_literal;
dq_literal = raw [ '"' >> *("\\\"" | ~char_('"')) >> '"' ];
sq_literal = raw [ "'" >> *("\\'" | ~char_("'")) >> "'" ];
BOOST_SPIRIT_DEBUG_NODES(
(start)(dq_literal)(sq_literal)
)
}
private:
qi::rule<It, std::string(), Skipper> start;
// drop skipper to make these rules implicitly 'lexeme'
// see: https://stackoverflow.com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965
qi::rule<It, std::string()>
dq_literal,
sq_literal;
};
int main() {
using It = std::string::const_iterator;
my_grammar<It> g;
for (std::string const& input : {
"\"hello world\"",
"\"hello \\\"world\\\"\"",
"'bye world'",
"'bye \"\\'world\\'\"'",
"bogus" })
{
std::cout << "\n------- Parsing: " << input << '\n';
It f = input.begin(), l = input.end();
std::string result;
bool ok = qi::phrase_parse(f, l, g, qi::space, result);
if (ok)
std::cout << "Parse success: " << result << "\n";
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed input '" << std::string(f,l) << "'\n";
}
}
Printing:
------- Parsing: "hello world"
Parse success: "hello world"
------- Parsing: "hello \"world\""
Parse success: "hello \"world\""
------- Parsing: 'bye world'
Parse success: 'bye world'
------- Parsing: 'bye "\'world\'"'
Parse success: 'bye "\'world\'"'
------- Parsing: bogus
Parse failed
Remaining unparsed input 'bogus'
¹ see also Boost Spirit: "Semantic actions are evil"?

Resources