The Freenet #boost IRC channel amazes me every day with the amount of interest Spirit is getting from a lot of people. Thanks to everyone over there! But the best is those people are asking many interesting questions allowing me to come up with yet another Tip of the Day.
Today’s question has been asked by @psicode: “What is the difference between the components created by the unary operators ‘!’ and ‘~’?”. As the semantics of those operators are slightly dissimilar in Qi and Karma, I will talk about them separately. I will write about the Qi operators today and about the corresponding Karma operators in one of the next installments.
Let us start with the commonality between the two operators: both negate whether the component they are being used with succeeds parsing. If the component ‘c’ succeeds, both compound constructs, ‘!c’ and ‘~c’ will fail, and similarly, if ‘c’ fails, the execution of components ‘!c’ and ‘~c’ will succeed.
The differences are more interesting. The unary Qi operator ‘~’ is applicable to character and character class parsers only. It negates the set of characters matched by the parser component it is attached to. Here are some examples:
|~char_||does not match anything|
|~digit||matches everything except digits|
|~char_(“a-z”)||matches every character outside the character range spanned by ‘a’ and ‘z’|
The parsers created by the operator ‘~’ do not wrap the underlying parser. It rather modifies the behavior of the component it is attached to. This means there is no performance difference if compared to the plain character parsers.
The unary operator ‘!’ is creating a not-predicate parser. It can be attached to any (arbitrarily complex) parser expression. The not-predicate is a look-ahead matching parser trying to match the expression it is attached to. This is done without moving the current input position forward. In other words, the not-predicate does not consume any input . If the attached parser fails matching the overall not-predicate will succeed. In this case the parsing resumes at the same point where the not-predicate started matching. The following (slightly artificial) example will succeed matching a floating point number after making sure it is not a Boolean expression:
namespace qi = boost::spirit::qi; std::string input("1.0"); std::string::const_iterator b = input.begin(); double result = 0; qi::parse(b, input.end(), !qi::bool_ >> qi::double_, result);
Any parser created by the not-predicate is neutral in terms of attribute handling because it exposes unused_type as its attribute. As a consequence, parser components augmented with ‘!’ will never expose their attribute, and never will participate in any attribute propagation.