Go to the first, previous, next, last section, table of contents.


Inherited attributes

Inherited attributes are attributes that are passed to a rule, as opposed to synthesized attributes, which are returned from a rule.

As an example, we rewrite example 5 to handle the operator priorities within a single rule: the priority of the expression surrounding the subexpression being handled is passed as an attribute. For this purpose, we define some constants to give names to the various priorities.

%class ex6

%instance
{
  redeclare Lexer lex;

  const PRIO_EXPR = 0;
  const PRIO_ADDSUB = 1;
  const PRIO_MULDIV = 2;
  const PRIO_UNARY = 3;
}

%token NUMBER

The expr rule is modified to accept a the priority of the expression that it is to parse. When, for example, an subexpression must be parsed at priority PRIO_ADDSUB, a multiplication must be parsed before the subexpression is completed. If, however, another addition is encountered, the subexpression is finished (since parsing the addition before returning would imply right-associativity, which is not the desired semantics).

The expr rule becomes:

expr (> int priority, < int value)
                {
                  int right;
                }
        :
          atom (value)
          [
                        { if (priority >= PRIO_ADDSUB) return; }
            '+' expr (PRIO_ADDSUB, right)
                        { value += right; }
          |
                        { if (priority >= PRIO_ADDSUB) return; }
            '-' expr (PRIO_ADDSUB, right)
                        { value -= right; }
          |
                        { if (priority >= PRIO_MULDIV) return; }
            '*' expr (PRIO_MULDIV, right)
                        { value *= right; }
          |
                        { if (priority >= PRIO_MULDIV) return; }
            '/' expr (PRIO_MULDIV, right)
                        { value /= right; }
          ]*
        ;

A top-level invocation of the expr rule becomes

expr (PRIO_EXPR, result)

A closer look at the branches in the expr reveals a very common structure:

            { if (priority >= PRIO_ADDSUB) return; }
'+' expr (PRIO_ADDSUB, right)

A particular branch will be selected when the current token matches (in this case, it would be a `+'). However, when the priority of the next operator is not higher than that of the expression already being parsed (passed as an attribute in priority), the return is executed and the operator token still remains to be taken from the input, for the invoking rule to digest it and correctly accept an expression.


Go to the first, previous, next, last section, table of contents.