An attribute is a characteristic that determines the value of a grammatical symbol. Semantic functions, also referred to as attribute computation functions, are functions connected to grammar productions that compute the values of attributes. Predicate functions are functions that state a specific grammar's static semantic rules as well as portions of its syntax.

Some of the basis of attributes are the following:

| Basis of Attributes | Description |
|---|---|
| Type | These link data objects to the range of valid values |
| Location | This represents the operating system's memory. |
| Value | These are what an assignment operation produces. |
| Name | The name of these variables can be altered whenever a sub-program is called. |
| Components | Data objects from other data items are called components. A pointer is used to represent this binding, which is then modified. |
Also see, Cross Compiler
Types of Attributes
When parsing values from their domain, attributes can be applied to them and evaluated when conditions are met. The attributes can be roughly separated into two groups:
- Synthesized Attributes
- Inherited Attributes
depending on how they receive their values.

1. Synthesized Attributes
An attribute that lies on the left-hand side of a production is called a synthesized attribute. These attributes derive their values from the child nodes. Let's use the example of the language P->QR. If an attribute P depends on attribute Q or R, it will be a synthesized attribute.
Let us see some examples of the synthesized attributes in compiler design to understand it in a better way.
Example 1
G → PQRSHere, G is referred to be a synthesized attribute if it receives values from its sibling nodes which are P, Q, R, and S.
Example 2
G → G * P + QIn this example, the parent node G takes its values from all the child nodes, which are P and Q.
We have come to the end of synthesized attributes in compiler design. Now let us discuss the other types of attributes.
2. Inherited Attributes
An inherited attribute belongs to a nonterminal on the right side of a production. These characteristics borrow values from their siblings or parents. As long as the production at the parent has the non-terminal in its body, a semantic rule connected to the production at the parent defines them.
Inherited attributes are useful when the parse tree's structure differs from the source program's abstract syntax tree. Since they depend on both left and right siblings, they cannot be evaluated by a pre-order traversal of the parse tree.
Let us see some examples of the inherited attributes in compiler design to understand it in a better way.
Example 1
G → PQRSHere, G, Q, R, and S can provide values for P. G, P, R, and S can all provide values for Q. G, P, Q, and S are sources of values for R. In the end, G, P, Q, and R are input sources for S.
Example 2
E → E + S + TIn the above example, the value of S can be determined by E and T. Similarly, the value of T can be determined with the help of E and S.
Now as we are now aware of the types of attributes. Let us know how these attributes are used in grammar production. These attributes are helpful in the generation of intermediate code directly. This is done with the help of syntax-directed translation. Let's proceed to know how.
Syntax-Directed Translation
Let us continue with our blog and discuss syntax-directed transmission in compiler design. This topic needs to be known to understand S-attributed SDT and L-attributed SDT better.
Grammar rules enhanced by Syntax Directed Translation make it easier to perform semantic analysis. SDT entails feeding the parse tree data attached to the nodes as attributes from the bottom up or the top down.
The primary method for Syntax-Directed Translation is to build a parse tree or syntax tree, visit the tree's nodes in some sequence, and compute the values of the attributes at each node. Constructing an explicit tree is not always necessary when translating during parsing.
Now, let us discuss S-attributed and L-attributed SDT.
S-Attributed SDT
The S-attributed definition is a type of syntax-directed attributes in compiler design that solely uses synthesized attributes. The symbol attribute values in the production's body are used to calculate the attribute values for the non-terminal at the head.
The nodes of the parse tree can be ranked from the bottom up when evaluating an S-attributed SDD's attributes. i.e., by conducting a post-order traverse of the parse tree and evaluating the characteristics at a node once the traversal finally leaves that node.
Let us see an example of S-attributed SDT.
Example
The grammar is given below:
S → E
E → E1+T
E → T
T → T1*F
T → F
F → digitThe S-attributed SDT of the above grammar can be written in the following way.
Production | Semantic Rules |
|---|---|
| S → E | S.val = E.val |
| E → E1 + T | E.val = E1.val + T.val |
| E → T | E.val = T.val |
| T → T1 * F | T.val = T1.val * F.val |
| T → F | T.val = F.val |
| F → digit | F.val = digit. lexval |
L-Attributed SDT
L-attributed definitions are syntax-directed attributes in compiler design in which the edges of the dependency graph for the attributes in the production body can go from left to right and not from right to left. L-attributed definitions can inherit or synthesize their attributes.
If the traits are inherited, the calculation must come from the following:
- A quality that the production head inherited.
- By a production-related attribute, either inherited or synthesized, situated to the left of the attribute being computed.
- An inherited or synthesized attribute is linked to the attribute in question in a way that prevents cycles from forming in the dependency network.
Let us see an example of L-attributed SDT.
Example
The grammar is given below:
G → TL
T → int
T → float
T → double
L → Ll, id
L → id.The L-attributed SDT of the above grammar can be written in the following way.
Production | Semantic Rules |
|---|---|
| G → T L | L.in = T.type |
| T → int | T.type = int |
| T → float | T.type = float |
| T → double | T.type = double |
| L → L1, id | L1.in = L.in Enter_type(id. entry, L.in) |
| L → id | Entry_type(id. entry, L.in) |



