简体   繁体   中英

How do semantic actions (using _val and _attr) influence rule definition with %= and x3::rule's force_attribute=true?

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?), ie

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.

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 ).

PS 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 ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM