Attributes of Compound Components

| Home | Libraries | People | FAQ | More

#### Attributes of Compound Components

Spirit.Qi and Spirit.Karma implement well defined attribute type propagation rules for all compound parsers and generators, such as sequences, alternatives, Kleene star, etc. The main attribute propagation rule for a sequences is for instance:

Library | Sequence attribute propagation rule
--- | ---
Qi | `a: A, b: B --> (a >> b): tuple<A, B>`
Karma | `a: A, b: B --> (a << b): tuple<A, B>`

which reads as:

> Given `a` and `b` are parsers (generators), and `A` is the attribute type of `a`, and `B` is the attribute type of `b`, then the attribute type of `a >> b` (`a << b`) will be `tuple<A, B>`.

| Note
The notation `tuple<A, B>` is used as a placeholder expression for any fusion sequence holding the types A and B, such as `boost::fusion::tuple<A, B>` or `std::pair<A, B>` (for more information see Boost.Fusion).

As you can see, in order for a type to be compatible with the attribute type of a compound expression it has to

- either be convertible to the attribute type,
- or it has to expose certain functionalities, i.e. it needs to conform to a concept compatible with the component.

Each compound component implements its own set of attribute propagation rules. For a full list of how the different compound generators consume attributes see the sections Parser Compound Attribute Rules and Generator Compound Attribute Rules.

###### The Attribute of Sequence Parsers and Generators

Sequences require an attribute type to expose the concept of a fusion sequence, where all elements of that fusion sequence have to be compatible with the corresponding element of the component sequence. For example, the expression:

Library | Sequence expression
--- | ---
Qi | `double_ >> double_`
Karma | `double_ << double_`

is compatible with any fusion sequence holding two types, where both types have to be compatible with `double`. The first element of the fusion sequence has to be compatible with the attribute of the first `double_`, and the second element of the fusion sequence has to be compatible with the attribute of the second `double_`. If we assume to have an instance of a `std::pair<double, double>`, we can directly use the expressions above to do both, parse input to fill the attribute:

    // the following parses "1.0 2.0" into a pair of double
    std::string input("1.0 2.0");
    std::pair<double, double> p;
    qi::phrase_parse(input.begin(), input.end(),
        qi::double_ >> qi::double_,       // parser grammar
        qi::space,                        // delimiter grammar
        p);                               // attribute to fill while parsing

and generate output for it:

    // the following generates: "1.0 2.0" from the pair filled above
    std::string str;
    std::back_insert_iterator<std::string> out(str);
    karma::generate_delimited(out,
        karma::double_ << karma::double_, // generator grammar (format description)
        karma::space,                     // delimiter grammar
        p);                               // data to use as the attribute

(where the `karma::space` generator is used as the delimiter, allowing to automatically skip/insert delimiting spaces in between all primitives).

| Tip

For sequences only: Spirit.Qi and Spirit.Karma expose a set of API functions usable mainly with sequences. Very much like the functions of the `scanf` and `printf` families these functions allow to pass the attributes for each of the elements of the sequence separately. Using the corresponding overload of Qi's parse or Karma's `generate()` the expression above could be rewritten as:

    double d1 = 0.0, d2 = 0.0;
    qi::phrase_parse(begin, end, qi::double_ >> qi::double_, qi::space, d1, d2);
    karma::generate_delimited(out, karma::double_ << karma::double_, karma::space, d1, d2);

where the first attribute is used for the first `double_`, and the second attribute is used for the second `double_`.

###### The Attribute of Alternative Parsers and Generators

Alternative parsers and generators are all about - well - alternatives. In order to store possibly different result (attribute) types from the different alternatives we use the data type Boost.Variant. The main attribute propagation rule of these components is:

    a: A, b: B --> (a | b): variant<A, B>

Alternatives have a second very important attribute propagation rule:

    a: A, b: A --> (a | b): A

often allowing to simplify things significantly. If all sub expressions of an alternative expose the same attribute type, the overall alternative will expose exactly the same attribute type as well.