1

Given semantic actor

template<typename ValueType>
class divide
{
public:
  divide(ValueType value) : divisor{value} {}

  template<typename ContextType>
  void operator()(ContextType& context) const
  {
    _val(context) /= divisor;
  }
private:
  const ValueType divisor;
};

it seems I'm hitting the fact that the presence of semantic actions inhibit attribute synthesis (or propagation?), i.e.

const auto norm = x3::rule<struct _, double>{"norm"}
                = x3::double_[normalize{100.}];

gives me the value 0.

So I tried forcing attribute propagation by using %=

const auto norm_rule = x3::rule<struct _, double>{"norm"}
                    %= x3::double_[normalize{100.}];

This gives me the expected parsed value divided by 100.

Then I discovered x3::rule has a third template argument, bool force_attribute, and noticed that

const auto norm_rule = x3::rule<struct _, double, true>{"norm"}
                     = x3::double_[divide{100.}];

Has the wanted result of the value divided by 100.

Experimenting further, I also discovered I could instead define divide::operator() as follows:

void operator()(ContextType& context)
{
  _attr(context) = _val(context) / divisor;
}

This last one seems to strongly couple/condemn the semantic actor to a rule's top level, as it acts on the attribute _attr of the first rule instead of the value _val of a parser to which it is attached.

Am I correct in my deduction that

  1. %= is the same as setting the third x3::rule template parameter force_attribute to true?
  2. This type of value-processing semantic actions should exclusively work on _val so they work on the attached parser instead of the first rule encountered in the hierarchy?

I understand these may seem lie unrelated questions, but they really are connected as I'm trying to process a parsed number (float) and transform it into an uint8 in several ways. To be totally complete: I have working code (numeric stuff here and the actual thing I'm constructing/parsing here), but it seems needlessly complex (due to the above it seems I need a rule per type/value transformation, which seems silly.

rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • Hmm it seems I swapped the meaning of `_attr` and `_val`. The former should be the attached parser's result, and the latter should be the rule's attribute. At least I can understand my own confusion. I'll leave the question as is for now, until I get grips on this I fear changing it will make it worse than it is now. – rubenvb Dec 29 '18 at 12:57

1 Answers1

1

it seems I'm hitting the fact that the presence of semantic actions inhibit attribute synthesis (or propagation?)

It is so, the behavior was copied from Qi (qi::rule docs, How Do Rules Propagate Their Attributes?).

Relevant code parts: call site, handling.

%= is the same as setting the third x3::rule template parameter force_attribute to true?

Yes, it is not documented, see the code boost/spirit/home/x3/nonterminal/rule.hpp.

Experimenting further, I also discovered I could instead define divide::operator() as follows:

void operator()(ContextType& context)
{
  _attr(context) = _val(context) / divisor;
}

This last one seems to strongly couple/condemn the semantic actor to a rule's top level, as it acts on the attribute _attr of the first rule instead of the value _val of a parser to which it is attached.

You got it almost right, but swapped them. It should be _val(context) = _attr(context) / divisor, details are below.

This type of value-processing semantic actions should exclusively work on _val so they work on the attached parser instead of the first rule encountered in the hierarchy?

The semantic actions documentation describes what _val and _attr is:

Function  Description                                         Example
--------  --------------------------------------------------  -----------------------
_val      A reference to the attribute of the innermost rule  _val(ctx) = "Gotya!"
          that directly or indirectly invokes the parser p    
_attr     A reference to the attribute of the parser p        _val(ctx) += _attr(ctx)

What they will be in a particular situation depends on make_attribute/transform_attribute traits. By default they will reference to the same value until you have nested rules with different attribute types (relevant code).

P.S. I cannot say anything about the reason why it is so. I see that many Spirit users just use %= all over the place because intuitively it should be a default, and you can manually disable propagation with omit directive. Moreover, Qi has some bugs because of this mechanism when you use lazy values on something like repeat directive (ticket 13313).

Nikita Kniazev
  • 3,728
  • 2
  • 16
  • 30