Re: Parse succeeds, but doesn't propagate to first attribute with extra actions.

Henri Menke <[email protected]> Thu, 21 Nov 2019 11:32:35 +1300
Newsgroups gmane.comp.parsers.spirit.general
Message-ID <[email protected]>
Dear Dan,

Sorry for the confusion, I now see why you need the rule to be

     '=' >> bool_ | attr(true)

However, I think your code is not as concise as it could be.  I suggest
you keep using the `set' lambda in all the places and complement it with
a `set_or' lambda which either sets the member to the attribute of the
parser or a default value.

I also don't see why you reverted to `words[face]' instead of
`set(&font::face_name, words)' which I find much cleaner.

Probably you could also use the expectation operator `>' in more places
to minimize backtracking for faster parsing and earlier error reporting.

Cheers, Henri

Live example also on Wandbox:

     https://wandbox.org/permlink/yRexnqNB8JzT5Ota

---

#include <boost/spirit/home/x3.hpp>
#include <iostream>

struct font {
     std::string face_name;
     size_t point = 0;
     bool bold = false;
     bool italic = false;
     bool strike = false;
     size_t id = 0;
};

namespace parsers {

// Thanks to the ingenious Seth!
// https://stackoverflow.com/a/49269239
auto const font_parser = boost::spirit::x3::rule<class _, font>{"font"} = [] {
     using namespace boost::spirit::x3;

     auto const set = [](auto member, auto p) {
         auto propagate = [member](auto &ctx) {
             traits::move_to(_attr(ctx), _val(ctx).*(member));
         };
         return as_parser(p)[propagate];
     };

     auto const set_or = [&set](auto member, auto p, auto def) {
         return set(member, p) | set(member, attr(def));
     };

     auto const words = lexeme[+char_("a-zA-Z ")];

     return lit("font(") > set(&font::face_name, words)
     >> *(
         ';'
         >> (
               lit("size") >> set(&font::point, '=' > int_)
               | lit("bold") >> set_or(&font::bold, '=' > bool_, true)
               | lit("italic") >> set_or(&font::italic, '=' > bool_, true)
               | lit("strike") >> set_or(&font::strike, '=' > bool_, true)
         )
     );
}();

} // namespace parsers

int main() {
     font afont;
     std::string target;
     std::string test_str(
         "font(Times New Roman;italic;size=12;bold=false;strike=true);circle");
     auto begin = test_str.begin();
     phrase_parse(begin, test_str.end(), parsers::font_parser,
                  boost::spirit::x3::space, afont);
     std::cout << std::boolalpha << "face_name = " << afont.face_name << '\n'
               << "point     = " << afont.point << '\n'
               << "bold      = " << afont.bold << '\n'
               << "italic    = " << afont.italic << '\n'
               << "strike    = " << afont.strike << '\n';
     return 0;
}

On 11/20/19 9:56 AM, Dan Bloomquist wrote:
> Henri Menke wrote:
> ...
> Hi Henri,
> Thank you very much for setting me on the way. I'm now much more
> comfortable with 'as_parser' and putting a parser in a lambda. Note that
> 'true_parser' just parses. And yes, Seth is brilliant, I've learned very
> much from his posts.
> 
> //https://stackoverflow.com/a/49269239
> It seems I had already voted that one up in the past. I just missed the treasure there on this endeavor. I just don't use spirit often enough to keep it all in my head. At that, I'm 68 so don't know how much mind is left. :)
> 
> The objective is that the user, as not a programmer, should get as much latitude as possible. Like in the case of fonts, the defaults are false and they can:
> bold;
> bold=true;
> bold=false; //this is redundant as is the case for no bold defined.
> size=12; //a value is required.
> 
> And, of course, they can put them in any order they would like. They just must lead with a font face or 'Default' for the globally used font.
> This:
> https://github.com/lakeweb/stuff/blob/master/void_method2.cpp
> 
> I've already plugged into my app and it is working nicely. This kind of parser is part of the support so a user has control over how their reports look without my hard-coding.
> 
> Best, Dan.
> 
> New code: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> #include <iostream>
> #include <boost/fusion/adapted/struct.hpp>
> #include <boost/spirit/home/x3.hpp>
> 
> struct font {
> 	std::string face_name;
> 	size_t point = 0;
> 	bool bold = false;
> 	bool italic = false;
> 	bool strike = false;
> 	size_t id = 0;
> };
> 
> namespace parsers {
> 	using namespace boost::spirit::x3;
> 
> 	auto true_parser = [](auto at) {
> 		auto propagate = [at](auto ctx) {
> 			traits::move_to(_attr(ctx), _val(ctx).*(at)); };
> 		return (('=' >> bool_[propagate]) | attr(true)[propagate]);
> 	};
> 	auto set_val = [](auto member, auto p) {
> 		auto propagate = [member](auto& ctx) {
> 			traits::move_to(_attr(ctx), _val(ctx).*(member));
> 		};
> 		return as_parser('=' >> p)[propagate];
> 	};
> 	auto const words = lexeme[+char_("a-zA-Z ")];
> 	auto const face = [](auto& attr) { _val(attr).face_name = _attr(attr); };
> 
> 	auto const font_parser = boost::spirit::x3::rule<class _, font>{ "font" } = [] {
> 		return lit("font(") >> words[face]
> 			>> *(
> 				';'
> 				>> (
> 					lit("size") >> set_val(&font::point, int_)
> 					| lit("bold") >> true_parser(&font::bold)
> 					| lit("italic") >> true_parser(&font::italic)
> 					| lit("strike") >> true_parser(&font::strike)
> 					)
> 				);
> 	}();
> }
> 
> int main() {
> 	font afont;
> 	std::string target;
> 	std::string test_str("font(Times New Roman;italic;size=12;bold=false;strike=true);circle");
> 	auto begin = test_str.begin();
> 	phrase_parse(begin, test_str.end(), parsers::font_parser, boost::spirit::x3::space, afont);
> 	std::cout << std::boolalpha
> 		<< "face_name = " << afont.face_name << '\n'
> 		<< "point     = " << afont.point << '\n'
> 		<< "bold      = " << afont.bold << '\n'
> 		<< "italic    = " << afont.italic << '\n'
> 		<< "strike    = " << afont.strike << '\n';
> 	return 0;
> }
> 
> 
> 
> _______________________________________________
> Spirit-general mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/spirit-general
>