Introduction
Parsing tells the compiler how a program is structured; it does not compute anything about the program. Syntax-directed translation attaches information to grammar symbols, called attributes, and attaches rules to productions that compute those attributes. The value of an expression, the type of a declared name, the code for a statement and the syntax tree of an expression are all attributes.
A syntax-directed definition (SDD) is a context-free grammar together with attributes and semantic rules. It says what must be computed but not when; the order of evaluation follows from the dependencies between attributes. Two restricted classes, S-attributed and L-attributed definitions, can always be evaluated in a fixed order during parsing, which is why examination questions concentrate on recognising them.
Prerequisites
- Top-Down Parsing and LL(1): parse trees, the left-recursion-free expression grammar, and the order in which a top-down parser visits nodes.
- Bottom-up parsing order (see the note Bottom-Up Parsing and Shift-Reduce): children are reduced before their parent.
Core concepts
| Term | Definition |
|---|---|
| Attribute | A value attached to a grammar symbol, written (a number, a type, a string of code, a pointer) |
| Semantic rule | An equation attached to a production that defines one attribute in terms of others in the same production |
| Synthesized attribute | Attribute of the head of production , defined from attributes of the children and already available inherited attributes of the head (or a leaf constant), with an acyclic local schedule. An S-attributed definition has only synthesized attributes and uses the child-only restriction |
| Inherited attribute | Attribute of a body symbol , defined from attributes of the head , of itself, and of its siblings |
| Annotated parse tree | A parse tree with the value of every attribute shown at its node |
| Dependency graph | One node per attribute instance in the tree; an edge when the rule for uses |
| S-attributed SDD | Every attribute is synthesized |
| L-attributed SDD | Every inherited attribute of in depends only on inherited attributes of , on attributes (of either kind) of , and on attributes of itself without creating a cycle |
Synthesized versus inherited: where the rule lives. A synthesized attribute is computed in the production where its symbol is the head, so its information flows up the tree. An inherited attribute is computed in the production where its symbol is in the body, so its information flows down or sideways. Terminals have synthesized attributes only, supplied by the lexical analyser (for example digit.lexval); the start symbol normally has no inherited attributes.
Side effects. A rule such as addType(id.entry, L.inh) or print(E.val) does not define an attribute; it is treated as defining a dummy attribute of the head so that it gets a place in the dependency graph.
How it works
Evaluation order
Any order consistent with the dependency graph, that is any topological sort, evaluates every attribute after the attributes it uses. If the graph has a cycle, no order exists and the SDD is circular for that tree. Testing whether an arbitrary SDD can be circular for some parse tree is decidable but takes exponential time, which is the practical reason for restricting attention to S- and L-attributed definitions.
| Class | Order that always works | Fits naturally with |
|---|---|---|
| S-attributed | Any bottom-up order, for example a postorder traversal | LR parsing: compute the head's attributes at each reduction |
| L-attributed | Depth-first, left to right: inherited attributes of a node before visiting it, synthesized ones after its children | LL parsing and recursive descent |
Every S-attributed SDD is L-attributed (there are no inherited attributes to restrict).
Example 1: an S-attributed desk calculator
| Production | Semantic rule |
|---|---|
( is an end-of-line token; subscripts distinguish two occurrences of the same nonterminal.) For the input 3 * 4 + 5 n the annotated parse tree has, bottom-up: , , , , , then , , , and at the root. Every value is computed from the children, so a postorder walk, or an LR parser at each reduction, evaluates the whole tree.
Example 2: an inherited attribute for declarations
| Production | Semantic rule |
|---|---|
| ; | |
The type is known at , on the left, but must reach every identifier in , on the right and further down: an inherited attribute carries it. For float x, y, z the parse tree has three nodes. The dependency graph has 10 nodes: , three , three , and three dummy nodes for the addType calls. It has 9 edges: at the top, two edges passing down to , and two edges into each addType node (from that 's and from its ). The SDD is L-attributed: depends on its left sibling , and on the head's inherited attribute.

Example 3: inherited attributes after removing left recursion
Left-recursion removal moves the left operand of an operator into a different subtree. An inherited attribute carries it across:
| Production | Semantic rules |
|---|---|
| ; | |
| ; | |
For 3 * 5 * 2: the first inherits 3; the next inherits ; the last inherits and, being , copies it to ; the 30 is passed back up to . The running product is the left-associative value , even though the tree leans to the right. The SDD is L-attributed: each uses the head's inherited attribute and the left sibling .
Example 4: an S-attributed definition with positional weights
For binary numerals, , , , the rules , , or , and are all synthesized. The input 1101 gives : the partial values are 1, 3, 6, 13.
Example 5: unfamiliar operators, familiar method
| Production | Semantic rule |
|---|---|
The symbol # means multiplication and & means addition, and & is on the lower level , so it binds tighter. For 2 # 3 & 5 # 6 & 4 the tree groups the & pieces first: -values , and , and then . Reading the string with the usual "multiplication first" habit gives , the trap answer.
Example 6: a synthesized attribute that is not a value
| Production | Semantic rule |
|---|---|
is the nesting depth of a balanced string of parentheses. For (()())(), the outer pair encloses ()(), whose depth is 1, so it contributes ; the trailing () contributes 1. Hence at the root. The definition is S-attributed.
Example 7: a circular definition
| Production | Semantic rules |
|---|---|
| ; | |
| none |
For the only tree, over over b, the dependency graph has 2 nodes, and , and 2 edges, and : a cycle, so no evaluation order exists. The definition is not L-attributed either, because reads the head's synthesized attribute. L-attributed definitions can never be circular: their inherited attributes use only the head's inherited attributes, attributes from the left, or acyclic same-symbol dependencies. Respecting that local dependency order during a left-to-right depth-first visit makes each required source available.
Recognising the class
For each rule, ask: which symbol's attribute does it define, and from which positions does it read?
| Production and rule | Kind of the defined attribute | S-attributed? | L-attributed? |
|---|---|---|---|
| : | synthesized | yes | yes |
| : | inherited | no | yes (head's inherited, left sibling) |
| : | inherited | no | no (depends on a right sibling) |
| : | inherited | no | no (depends on the head's synthesized attribute) |
| : | synthesized | no ( is inherited) | yes, if itself obeys the rule |
A whole SDD is S-attributed only if every rule is of the first kind, and L-attributed only if every inherited attribute satisfies the left-to-right restriction.
Important properties and observations
- S implies L. Every S-attributed SDD is L-attributed; the converse is false (Example 2).
- Postorder suffices for S-attributed SDDs; L-attributed ones need one left-to-right depth-first pass with actions on the way down (inherited) and on the way up (synthesized).
- Inherited attributes in LR parsing. Bottom-up parsers compute synthesized attributes at reductions easily. Inherited attributes need marker nonterminals or a separate pass (see the note Syntax-Directed Translation Schemes).
- The grammar decides the shape. The same computation can be S-attributed on a left-recursive grammar (Example 1) and need inherited attributes on its left-recursion-free version (Example 3).
- Syntax trees are attributes too. Rules like build abstract syntax trees, the starting point of intermediate code generation.
Common misconceptions.
- "An attribute of a body symbol is inherited." Only if it is defined in this production. used in is synthesized: it was defined in a production where is the head.
- "L-attributed means only left recursion." It is about the direction of attribute flow (from the left), not about the grammar.
- "Terminals can have inherited attributes." In the standard definition they have only synthesized ones from the lexer.
- "Circular means the grammar is recursive." Circularity is about attribute dependencies in a parse tree, not about recursive productions; Example 6 is recursive and not circular, Example 7 is not recursive and circular.
GATE examination perspective
- Classify an SDD. For each inherited attribute, check that it reads only the head's inherited attributes, attributes of symbols to its left, or attributes of the same symbol without creating a dependency cycle. A rule reading a right sibling or the head's synthesized attribute breaks L-attribution.
- Evaluate an annotated parse tree. Build the tree for the given string and grammar, then apply the rules bottom-up (synthesized) or top-down and left to right (inherited). Watch for unusual rules (for example on a
+production): apply what is written. - Dependency graphs. Count nodes (attribute instances, including dummy nodes for side effects if the question says so) and edges; find a topological order or detect a cycle.
- Which parser suits which SDD. S-attributed with LR; L-attributed with LL or recursive descent.
- Type propagation in declarations. This is the standard inherited-attribute example.
- Operators with invented symbols. Map each symbol to its rule, find its level in the grammar, and group by levels; Example 5 gives 160, not 40.
- Circularity. A dependency cycle in some parse tree makes the SDD circular. S-attributed and L-attributed definitions are never circular.
Summary
Sources
- A. V. Aho, M. S. Lam, R. Sethi, J. D. Ullman, Compilers, Principles, Techniques, and Tools, 2nd ed., Pearson Addison-Wesley, 2007: Section 5.1 (syntax-directed definitions; inherited and synthesized attributes; evaluating an SDD at the nodes of a parse tree), Section 5.2 (evaluation orders: dependency graphs, ordering the evaluation, S-attributed and L-attributed definitions, semantic rules with controlled side effects), Section 5.3.1 (construction of syntax trees).