A couple of days ago I promised to get back to this topic (if you want to refresh your memory, here is the discussion of those operators in Qi). Today we will discuss Karma’s unary operators ‘!’ and ‘~’. These have very similar semantics as their counterparts in Qi, but as usual, we have to turn things inside out in order to make them fit to output generation.
The one commonality of the two operators is the same as in Qi: both negate whether the component they are being used with succeeds generating. If the component ‘c’ succeeds, both compound constructs, ‘!c’ and ‘~c’ will fail, and similarly, if ‘c’ fails, the execution of the components ‘!c’ and ‘~c’ will succeed.
Similar to its counterpart in Qi, the unary Karma operator ‘~’ is applicable to character and character class generators only. It negates the set of characters a generator will be allowed to emit. Let me explain. The Karma character set generators, such as char_(“a-z”) or digit will emit their attribute only if the attribute value belongs to the character set described. Consequently, applying the operator ‘~’ will negate the character set the generator it is attached to. Here are some examples:
|~char_(“a-z”)||will emit character values outside the character range spanned by ‘a’ and ‘z’|
|~digit||will emit non-digits only|
|~char_(‘a’)||will emit everything but an ‘a’|
If a generator can’t emit its attribute it will fail. This is very similar to a failing parser component. The possibility for a generator component to fail is very useful. This can be utilized for alternatives, predicates and other constructs. But this is beyond today’s topic and will be discussed in a different installment of the ‘Tip of the Day’.
The generators created by the operator ‘~’ do not wrap the underlying generator. The operator rather modifies the behavior of the component it is attached to. This means there is no performance difference if compared to the plain character generators.
The unary operator ‘!’ creates a not-predicate generator. Again, similar to its counterpart in Qi, it can be attached to any (arbitrarily complex) generator. The not-predicate generator will succeed if the associated generator fails, and it will fail if its generator succeeds. It invokes the generator it is attached to but the emitted output will not show up in the overall output stream. So effectively, the not-predicate generator will never emit any output. The following example will succeed emitting a floating point number if the first attribute is false (which will make the true_ generator fail), otherwise it will not emit anything and will fail all together:
namespace karma = boost::spirit::karma; std::string output; std::back_insert_iterator<std::string> sink(output); karma::generate(sink, !true_ << double_, false, 1.0); // will emit: 1.0
This example highlights another difference if compared to Qi’s not-predicate. The Karma not-predicate will always consume an attribute. More accurately, it will expose the attribute of the generator it is associated with (while in Qi the not-predicate never exposes any attribute). As mentioned earlier, we utilize the true_ generator’s ability to fail to control what output is generated (or if any output is generated at all as in our case).