1

my parser is nearly working :) (still amazed by Spirit feature set (and compiletimes) and the very welcoming community here on stack overflow)

small sample for online try: http://coliru.stacked-crooked.com/a/1c1bf88909dce7e3

so i've learned to use more lexeme-rules and try to prevent no_skip - my rules are smaller and better to read as a result but now i stuck with combining lexeme-rules and skipping-rules what seems to be not possible (compiletime error with warning about not castable to Skipper)

my problem is the comma seperated list in subscriptions which does not skip spaces around expressions

parses:

"a.b[a,b]"

fails:

"a.b[ a , b ]"

these are my rules:

qi::rule<std::string::const_iterator, std::string()> identifier_chain;

qi::rule<std::string::const_iterator, std::string()>
    expression_list = identifier_chain >> *(qi::char_(',') >> identifier_chain);

qi::rule < std::string::const_iterator, std::string() >
    subscription = qi::char_('[') >> expression_list >> qi::char_(']');

qi::rule<std::string::const_iterator, std::string()>
    identifier = qi::ascii::alpha >> *(qi::ascii::alnum | '_');

identifier_chain = identifier >> *(('.' >> identifier) | subscription);

as you can see all rules are "lexeme" and i think the subscription rule should be a ascii::space_type skipper but that does not compile

should i add space eaters in the front and back of identifier_chains in the expression_list?

feels like writing an regex :(

expression_list = *qi::blank >> identifier_chain >> *(*qi::blank >> qi::char_(',') >> *qi::blank >> identifier_chain >> *qi::blank);

it works but i've read that this will get me to an much bigger parser in the end (handling all the space skipping by myself)

thx for any advice

btw: any idea why i can't compile if surrounding the '.' in the indentifier_chain with qi::char_('.')

identifier_chain = identifier >> *(('.' >> identifier) | subscription);

UPDATE:

i've updated my expression list as suggested by sehe

qi::rule<std::string::const_iterator, spirit::ascii::blank_type, std::string()>
expression_list = identifier_chain >> *(qi::char_(',') >> identifier_chain);

qi::rule < std::string::const_iterator, std::string() >
subscription = qi::char_('[') >> qi::skip(qi::blank)[expression_list] >> qi::char_(']');

but still get compile error due to non castable Skipper: http://coliru.stacked-crooked.com/a/adcf665742b055dd

i also tried changed the identifer_chain to

identifier_chain = identifier >> *(('.' >> identifier) | qi::skip(qi::blank)[subscription]);

but i still can't compile the example

llm
  • 557
  • 3
  • 15
  • Using `char_()` exposes the character as element in the synthesized attribute sequence, breaking attribute propagation because it expects strings, not tuples/sequences of strings and chars. `lit()` doesn't expose an attribute at all, just matching required input. – sehe Mar 25 '20 at 09:38

1 Answers1

1

The answer I linked to earlier describes all the combinations (if I remember correctly): Boost spirit skipper issues

In short:

  • any rule that declares a skipper (so rule<It, Skipper[, Attr()]> or rule<It, Attr(), Skipper>) MUST be invoked with a compatible skipper (an expression that can be assigned to the type of Skipper).

  • any rule that does NOT declare a skipper (so of the form rule<It[, Attr()]>) will implicitly behave like a lexeme, meaning no input characters are skipped.

That's it. The slightly subtler ramifications are that given two rules:

rule<It, blank_type> a;
rule<It> b; // b is implicitly lexeme

You can invoke b from a:

a = "test" >> b;

But when you wish to invoke a from b you will find that you have to provide the skipper:

b = "oops" >> a; // DOES NOT COMPILE
b = "okay" >> qi::skip(qi::blank) [ a ];

That's almost all there is to it. There are a few more directives around skippers and lexemes in Qi, see again the answer linked above.

Side Question:

should i add space eaters in the front and back of identifier_chains in the expression_list?

If you look closely at the answer example here Parse a '.' chained identifier list, with qi::lexeme and prevent space skipping, you can see that it already does pre- and post skipping correctly, because I used phrase_parse:

" a.b " OK: ( "a" "b" ) 
----
"a . b" Failed
Remaining unparsed: "a . b"
----

You COULD also wrap the whole thing in an "outer" rule:

rule<std::string::const_iterator> main_rule = 
     qi::skip(qi::blank) [ identifier_chain ];

That's just the same but allows users to call parse without specifying the skipper.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Answered a side-question as well – sehe Mar 25 '20 at 09:35
  • thanks again - the flexibility of Spirit is so hight that i just don't see all the equaly attachable strategies – llm Mar 25 '20 at 09:48
  • Yup. I like spirit because it allows you to be very productive _when you know the sweet spot of convenience features_. Until then, it's a swamp that will slow you down at least as often as it would save you time (like on testing the building blocks) – sehe Mar 25 '20 at 09:51
  • i started to write my expression parser manualy - a friend forced me (without decent spirit expirience) to use Spirit - so now im knee deep in the swamp, btw: your example doesn't work for me - see Update – llm Mar 25 '20 at 09:58
  • @ilm if you can share the code (even if it doesn't currently compile) it would help me spot the problem more quickly - right now I'd have to make many assumptions (I do note that you have `char_(",")` again, on purpose?) – sehe Mar 25 '20 at 10:38
  • sorry, updated the question with coliru-link: http://coliru.stacked-crooked.com/a/adcf665742b055dd , i used the qi::char_ for getting the chars also in the result - maybe i should replace that with qi::lit – llm Mar 25 '20 at 10:43
  • even removing all the char_ and lit_ downto simple chars will give the same not castable to Skipper error – llm Mar 25 '20 at 10:52
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/210289/discussion-between-sehe-and-llm). – sehe Mar 25 '20 at 10:55