100% FREE Updated: Mar 2026 Compiler Design Front-End Design

Syntax-Directed Translation

Comprehensive study notes on Syntax-Directed Translation for GATE CS preparation. This chapter covers key concepts, formulas, and examples needed for your exam.

Syntax-Directed Translation

Overview

Having established the grammatical structure of a source program through parsing, we now proceed to the crucial phase of discerning its meaning. This process is systematically guided by the program's syntax, and the formal framework for achieving this is known as Syntax-Directed Translation (SDT). This powerful method involves associating semantic rules, or actions, with the productions of a context-free grammar. By executing these actions during parsing, we can translate a string from the source language into a target representation, which may be an abstract syntax tree, intermediate code, or even executable code itself.

The principles of syntax-directed translation form the theoretical and practical foundation for the semantic analysis phase of a compiler. Through this framework, we can perform a multitude of essential tasks, including the enforcement of type-compatibility rules, the collection of information for symbol tables, and the generation of intermediate representations such as three-address code. In essence, syntax-directed translation serves as the bridge between the structural analysis performed by the front end and the code synthesis conducted by the back end, ensuring that the compiler understands what the program is supposed to do, not just how it is structured.

For the Graduate Aptitude Test in Engineering (GATE), a thorough understanding of this topic is paramount. Questions frequently assess one's ability to work with attribute grammars, differentiate between S-attributed and L-attributed definitions, and trace the evaluation of attributes on a parse tree. Mastery of these concepts is indispensable for solving complex problems involving semantic checking and intermediate code generation, which are recurring and high-value themes in the Compiler Design syllabus.

---

Chapter Contents

| # | Topic | What You'll Learn |
|---|-------|-------------------|
| 1 | Semantic Analysis | Verifying program meaning and language rules. |

---

Learning Objectives

❗ By the End of This Chapter

After completing this chapter, you will be able to:

  • Define Syntax-Directed Definitions (SDDs) and differentiate between synthesized and inherited attributes.

  • Distinguish between S-attributed and L-attributed definitions and their respective evaluation orders.

  • Construct dependency graphs and annotated parse trees for given grammar productions and input strings.

  • Develop translation schemes to perform type checking and generate intermediate code.

---

We now turn our attention to Semantic Analysis, which constitutes the first and most fundamental application of the syntax-directed framework.
## Part 1: Semantic Analysis

Introduction

Following the parsing phase, a compiler has successfully verified that a source program is syntactically correct according to the rules of a context-free grammar. However, syntactic correctness does not guarantee that the program is meaningful or logically sound. For instance, a program might attempt to add an integer to a string, or use a variable before it has been declared. The detection of such errors falls under the purview of semantic analysis. This phase serves as a critical bridge between the structural analysis of the parser and the generation of machine-level code, ensuring that the program adheres to the language's semantic rules.

The primary framework for performing semantic analysis is Syntax-Directed Translation (SDT). This powerful technique systematically associates semantic rules, or actions, with the productions of a grammar. As the parser constructs the parse tree, these semantic actions are executed to compute attribute values, perform checks, and generate intermediate representations. We can view this process as annotating the parse tree with semantic information that flows up, down, and across its nodes, ultimately determining the overall meaning of the source program. The successful completion of this phase confirms that the program is not only syntactically well-formed but also semantically coherent.

πŸ“– Syntax-Directed Definition (SDD)

A Syntax-Directed Definition (SDD) is a context-free grammar where each grammar symbol is associated with a set of attributes, and each production is associated with a set of semantic rules for computing the values of the attributes.

---

Key Concepts

#
## 1. Attributes: Synthesized and Inherited

Attributes are values associated with grammar symbols that carry semantic information. They can be of any type, such as numbers, strings, types, memory locations, or even data structures. We classify attributes into two distinct categories based on how their values are computed.

a) Synthesized Attributes

A synthesized attribute for a non-terminal is computed from the attribute values of its children in the parse tree. This corresponds to a flow of information upwards from the leaves of the tree towards the root. For a production A→αA \rightarrow \alpha, a semantic rule computing a synthesized attribute for AA will take the form A.s=f(attributes of symbols in α)A.s = f(\text{attributes of symbols in } \alpha).

Consider the parse tree for a production A→BCDA \rightarrow BCD. A synthesized attribute A.sA.s is computed using attributes from its children BB, CC, and DD.



Synthesized Attribute Flow



A


B


C


D














A.s = f(B.attr, C.attr, D.attr)

b) Inherited Attributes

An inherited attribute for a non-terminal is computed from the attribute values of its parent and/or its siblings in the parse tree. This corresponds to a flow of information downwards or sideways across the tree. For a production A→BCDA \rightarrow BCD, a semantic rule computing an inherited attribute for child CC might take the form C.i=f(A.i,B.attr)C.i = f(A.i, B.attr).



Inherited Attribute Flow



A


B


C


D













C.i = f(A.i, B.attr)

Inherited attributes are particularly useful for propagating contextual information, such as the data type in a declaration, down to the individual identifiers.

---

#
## 2. Classes of Syntax-Directed Definitions

Based on the types of attributes and the structure of semantic rules, we can classify SDDs into two important categories, which have significant implications for parsing and implementation.

a) S-Attributed Definitions

An SDD that uses only synthesized attributes is called an S-attributed definition. These are the simplest form of SDDs and are highly compatible with bottom-up parsing methods, such as LR parsing.

πŸ“– S-Attributed Definition

An SDD is S-attributed if every attribute it uses is a synthesized attribute. Semantic rules for a production A→αA \rightarrow \alpha can only compute attributes for the head non-terminal AA.

The evaluation of attributes in an S-attributed definition naturally follows a post-order traversal of the parse tree. Since a node's attribute value depends only on its children, we can compute it as soon as the attributes for all its children have been computed. This aligns perfectly with the reduction step in a bottom-up parser.

Worked Example: S-Attributed Expression Evaluator

Problem: Consider the following S-attributed grammar for binary number evaluation. Compute the value of the input string `1011`.

| Production | Semantic Rule |
|-----------------------|---------------------------------|
| N→BN \rightarrow B | {N.val=B.val;}\{ N.val = B.val; \} |
| N→N1BN \rightarrow N_1 B | {N.val=2×N1.val+B.val;}\{ N.val = 2 \times N_1.val + B.val; \} |
| B→0B \rightarrow \bf{0} | {B.val=0;}\{ B.val = 0; \} |
| B→1B \rightarrow \bf{1} | {B.val=1;}\{ B.val = 1; \} |

Solution:

First, we construct the parse tree for the input string `1011`. The grammar is left-recursive, so the parse tree will be left-leaning.

Step 1: Parse the input and build the tree. The string `1011` is parsed as (((1)0)1)1(( (1) 0 ) 1 ) 1.

Step 2: Evaluate attributes in a post-order traversal (bottom-up).

  • The first `1` is reduced by Nβ†’Bβ†’1N \rightarrow B \rightarrow \bf{1}. Let's call this NaN_a.

Na.val=1N_a.val = 1

  • Next, `10` is parsed. This corresponds to the production Nbβ†’NaBN_b \rightarrow N_a B.
Nb.val=2Γ—Na.val+B.val=2Γ—1+0=2N_b.val = 2 \times N_a.val + B.val = 2 \times 1 + 0 = 2
  • Next, `101` is parsed. This corresponds to Ncβ†’NbBN_c \rightarrow N_b B.
Nc.val=2Γ—Nb.val+B.val=2Γ—2+1=5N_c.val = 2 \times N_b.val + B.val = 2 \times 2 + 1 = 5
  • Finally, `1011` is parsed. This corresponds to Nβ†’NcBN \rightarrow N_c B.
N.val=2Γ—Nc.val+B.val=2Γ—5+1=11N.val = 2 \times N_c.val + B.val = 2 \times 5 + 1 = 11

Answer: The computed value for the string `1011` is 1111.

b) L-Attributed Definitions

L-attributed definitions are a more general class that allows both synthesized and inherited attributes, but with a key restriction on how inherited attributes are computed. This class is designed to permit attribute evaluation in a single left-to-right, depth-first traversal of the parse tree.

πŸ“– L-Attributed Definition

An SDD is L-attributed if for each production Aβ†’X1X2…XnA \rightarrow X_1 X_2 \dots X_n, each inherited attribute of XjX_j (for 1≀j≀n1 \le j \le n) depends only on:

  • The inherited attributes of the parent, AA.

  • The attributes (synthesized or inherited) of its left siblings, X1,X2,…,Xjβˆ’1X_1, X_2, \dots, X_{j-1}.

Furthermore, any rule for a synthesized attribute of AA can depend on any attributes of its children.

Crucially, an inherited attribute for a symbol XjX_j cannot depend on any attributes from its right siblings (Xj+1,…,XnX_{j+1}, \dots, X_n). This "left-to-right" information flow makes L-attributed definitions suitable for top-down parsing methods.

❗ Must Remember

Every S-attributed definition is, by definition, also an L-attributed definition, as it satisfies the conditions vacuously (there are no inherited attributes to violate the rules). However, not all L-attributed definitions are S-attributed.

Example Analysis: S-Attributed vs. L-Attributed

Let us consider three different semantic rules.

  • Rule A: Pβ†’QR{P.s=Q.s+R.s;}P \rightarrow QR \quad \{P.s = Q.s + R.s;\}

  • This rule only defines a synthesized attribute (P.sP.s) based on its children. Thus, it is S-attributed (and therefore also L-attributed).

  • Rule B: Dβ†’typeΒ L{L.in_type=type.lexval;}D \rightarrow \text{type } L \quad \{L.in\_type = \text{type.lexval};\}

  • Here, the attribute L.in_typeL.in\_type is an inherited attribute of LL. It depends on an attribute of its left sibling, `type`. This follows the L-attributed rule. Since it uses an inherited attribute, it is not S-attributed but is L-attributed.

  • Rule C: Aβ†’BC{B.i=C.s;C.i=A.i;}A \rightarrow BC \quad \{B.i = C.s; \quad C.i = A.i;\}

  • The rule for B.iB.i requires the synthesized attribute C.sC.s from its right sibling CC. This violates the L-attributed condition. Therefore, this definition is neither S-attributed nor L-attributed.

    ---

    #
    ## 3. Application: Type Checking

    One of the most important roles of semantic analysis is type checking. SDDs provide a formal mechanism to implement this. We associate a `type` attribute with non-terminals representing expressions and use semantic rules to enforce type compatibility.

    Consider a simple language with integer and boolean types.

    | Production | Semantic Rule |
    |-----------------------------|----------------------------------------------------------------------------------------------------------------|
    | D→int idD \rightarrow \text{int id} | {add_type(id.lexeme, INT);}\{ \text{add\_type(id.lexeme, INT)}; \} |
    | D→bool idD \rightarrow \text{bool id} | {add_type(id.lexeme, BOOL);}\{ \text{add\_type(id.lexeme, BOOL)}; \} |
    | E→E1+E2E \rightarrow E_1 + E_2 | {if E1.type==INT and E2.type==INT then E.type=INT else error();}\{ \text{if } E_1.type == \text{INT and } E_2.type == \text{INT then } E.type = \text{INT else error()}; \} |
    | E→E1 and E2E \rightarrow E_1 \text{ and } E_2 | {if E1.type==BOOL and E2.type==BOOL then E.type=BOOL else error();}\{ \text{if } E_1.type == \text{BOOL and } E_2.type == \text{BOOL then } E.type = \text{BOOL else error()}; \} |
    | E→idE \rightarrow \text{id} | {E.type=lookup_type(id.lexeme);}\{ E.type = \text{lookup\_type(id.lexeme)}; \} |

    Here, the `add_type` function would update the symbol table during a declaration, and `lookup_type` would retrieve the type from the symbol table when an identifier is used in an expression. The rules for expressions check if the operands have the correct types for the given operator. Notice the rule for E→idE \rightarrow \text{id}, which correctly looks up the type, avoiding the flaw seen in a similar PYQ where the type was hardcoded.

    ---

    Problem-Solving Strategies

    When faced with SDT problems in GATE, a systematic approach is essential.

    πŸ’‘ GATE Strategy

    For Attribute Evaluation Problems:

    • Construct the Parse Tree: First, correctly parse the input string according to the given grammar. Pay close attention to operator precedence and associativity, which are dictated by the grammar's structure (e.g., left-recursion implies left-associativity).

    • Identify Attribute Dependencies: Look at the semantic rules. Are the attributes synthesized or inherited? This determines the evaluation order.

    • Perform Traversal and Evaluate: For S-attributed grammars, perform a post-order traversal of the parse tree. Compute the attributes for a node only after all its children's attributes are known. For L-attributed grammars, a depth-first, left-to-right traversal is needed.

    • Annotate the Tree: As you compute values, write them next to the nodes on your rough parse tree sketch. This helps keep track of intermediate values and prevents calculation errors.

    For S/L-Attributed Classification Problems:

    • Scan for Inherited Attributes: Quickly scan all semantic rules. If you find even one inherited attribute (an attribute of a symbol on the RHS of a production), the SDD is not S-attributed.

    • Check L-Attributed Conditions: If it is not S-attributed, examine each rule with inherited attributes. For a production Aβ†’X1…XnA \rightarrow X_1 \dots X_n, verify that any inherited attribute for XjX_j only depends on attributes of AA and/or X1,…,Xjβˆ’1X_1, \dots, X_{j-1}. If any rule violates this by depending on a right sibling (XkX_{k} where k>jk > j), the SDD is not L-attributed.

    • Conclusion: If it passes the L-attributed check, it's L-attributed. If it has no inherited attributes, it's S-attributed (and also L-attributed).

    ---

    Common Mistakes

    ⚠️ Avoid These Errors
      • ❌ Incorrect Parse Tree Construction: Misinterpreting operator precedence or associativity from the grammar leads to an incorrect parse tree, which makes any subsequent attribute calculation wrong.
    βœ… Correct Approach: Analyze the grammar first. Left-recursive productions like Eβ†’E+TE \rightarrow E+T imply left-associativity. Productions at different "levels" (e.g., Eβ†’T,Tβ†’FE \rightarrow T, T \rightarrow F) define precedence. Always draw the tree carefully.
      • ❌ Confusing S- and L-Attributed Definitions: Assuming any SDD with inherited attributes is not L-attributed.
    βœ… Correct Approach: Remember the specific restriction for L-attributed definitions. Inherited attributes are allowed, but their dependencies are strictly limited to the parent and left siblings.
      • ❌ Right-to-Left Dependency Violation: In an L-attributed check, seeing a rule like Aβ†’BC,{B.i=C.s}A \rightarrow BC, \{B.i = C.s\} and failing to recognize it as a violation.
    βœ… Correct Approach: The attribute for the left sibling (BB) cannot depend on the right sibling (CC). This is a fundamental violation of the L-attributed property.
      • ❌ Ignoring Terminal Attributes: Forgetting that terminals can also have synthesized attributes (e.g., `id.lexeme`, `num.val`) which are typically provided by the lexical analyzer.
    βœ… Correct Approach: Treat attributes of terminals as given values (leaves of the dependency graph).

    ---

    Practice Questions

    :::question type="MCQ" question="Consider the following syntax-directed definition:
    S \rightarrow L = R \quad \{ \text{check_type}(L.type, R.type); \}
    R→L{R.type=L.type;}R \rightarrow L \quad \{ R.type = L.type; \}
    L→id{L.type=lookup(id.entry);}L \rightarrow \text{id} \quad \{ L.type = \text{lookup(id.entry)}; \}
    L \rightarrow L_1 [E] \quad \{ L.type = \text{element_type}(L_1.type); \quad E.type = \text{INT}; \}
    Which of the following statements is true?" options=["The SDD is S-attributed.","The SDD is L-attributed but not S-attributed.","The SDD is neither S-attributed nor L-attributed.","The SDD contains a syntax error."] answer="The SDD is S-attributed." hint="Analyze the dependencies for each attribute. Does any attribute for a symbol on the RHS depend on its parent or siblings?" solution="Let's analyze the attributes.

    • In Sβ†’L=RS \rightarrow L=R, the semantic action is a procedure call, not an attribute assignment. We consider the attributes used. L.typeL.type and R.typeR.type are synthesized from the children.

    • In Rβ†’LR \rightarrow L, R.typeR.type is a synthesized attribute depending on its child LL.

    • In Lβ†’idL \rightarrow \text{id}, L.typeL.type is a synthesized attribute.

    • In Lβ†’L1[E]L \rightarrow L_1 [E], L.typeL.type is synthesized from its child L1L_1. The rule also checks that E.typeE.type is INT, but it does not define any inherited attributes. All information flows up the parse tree.

    Since all attributes are synthesized, the SDD is S-attributed."
    :::

    :::question type="NAT" question="Consider the following SDD, where `val` is a synthesized attribute.

    S→ES \rightarrow E {S.val=E.val;}\{ S.val = E.val; \}
    E→E1+TE \rightarrow E_1 + T {E.val=E1.val+T.val;}\{ E.val = E_1.val + T.val; \}
    E→TE \rightarrow T {E.val=T.val;}\{ E.val = T.val; \}
    Tβ†’T1βˆ—FT \rightarrow T_1 * F {T.val=T1.valΓ—T.val;}\{ T.val = T_1.val \times T.val; \} -- Note the typo in the rule
    T→FT \rightarrow F {T.val=F.val;}\{ T.val = F.val; \}
    F→(E)F \rightarrow (E) {F.val=E.val;}\{ F.val = E.val; \}
    F→digitF \rightarrow \text{digit} {F.val=digit.lexval;}\{ F.val = \text{digit.lexval}; \}

    There is a deliberate error in one semantic rule. Assuming the parser uses this SDD to evaluate the expression `3 (4 + 2)`. What is the value computed for S.valS.val?" answer="18" hint="The rule Tβ†’T1βˆ—FT \rightarrow T_1 F has a typo: T.valT.val depends on itself. This creates an infinite recursion for the attribute calculation. However, for the given expression, this production is used as Tβ†’Fβ†’(E)β†’(E+T)β†’...T \to F \to (E) \to (E+T) \to ... and Tβ†’T1βˆ—FT \to T_1 F is not used at the top level for the multiplication. Let's re-read the question. Ah, the typo is T.val=T1.valΓ—T.valT.val = T_1.val \times T.val. This is a circular definition. Let's assume the intended rule was T.val=T1.valΓ—F.valT.val = T_1.val \times F.val. Let's solve with the intended rule. Wait, the question asks what is computed with the given SDD*. Let's trace it carefully.
    The expression is `3 * (4 + 2)`.
    The parse tree structure is Sβ†’Eβ†’Tβ†’T1βˆ—FS \to E \to T \to T_1 * F.
    T1T_1 derives `3`. FF derives `(4 + 2)`.
    Let's compute F.valF.val for `(4 + 2)` first.
    F→(E)F \to (E). We need E.valE.val for `4+2`.
    E→E1+TE \to E_1 + T. E1E_1 derives `4`, TT derives `2`.
    E1→T→F→digitE_1 \to T \to F \to \text{digit}. E1.val=4E_1.val = 4.
    T→F→digitT \to F \to \text{digit}. T.val=2T.val = 2.
    So, E.val=4+2=6E.val = 4 + 2 = 6.
    This means F.valF.val for `(4+2)` is 66.
    Now for the top-level production Tβ†’T1βˆ—FT \rightarrow T_1 * F.
    T1→F→digitT_1 \to F \to \text{digit}. T1.val=3T_1.val = 3.
    F.val=6F.val = 6.
    The rule is T.val=T1.valΓ—T.valT.val = T_1.val \times T.val. This is a circular definition. T.val=3Γ—T.valT.val = 3 \times T.val. This equation only holds if T.val=0T.val = 0. However, compilers would typically get stuck in a loop or use an uninitialized value. Let's reconsider the question's intent. It's more likely a typo in the question itself and should be T.val=T1.valΓ—F.valT.val = T_1.val \times F.val. Let's assume this was the intended rule, as this is a common GATE pattern.
    With the corrected rule T.val=T1.valΓ—F.valT.val = T_1.val \times F.val:
    T.val=3Γ—6=18T.val = 3 \times 6 = 18.
    S.val=E.val=T.val=18S.val = E.val = T.val = 18.
    Let's assume the question is malicious. If T.val=3Γ—T.valT.val = 3 \times T.val, then 2Γ—T.val=02 \times T.val = 0, which implies T.val=0T.val=0. If the value is 0, the answer is 0. But this is semantically nonsensical. Competitive exam questions usually test understanding, not obscure language-lawyer issues with circular definitions. The most logical interpretation is that the `T.val` on the RHS was a typo for `F.val`.
    Let's stick to the most reasonable interpretation.
    " solution="
    Step 1: Assume the semantic rule for multiplication has a typo and the intended rule is {T.val=T1.valΓ—F.val;}\{ T.val = T_1.val \times F.val; \}. A circular definition like {T.val=T1.valΓ—T.val;}\{ T.val = T_1.val \times T.val; \} is not evaluable and would likely be flagged as an error or result in undefined behavior, which is not typically tested via a NAT question seeking a specific value.

    Step 2: Construct the parse tree for `3 (4 + 2)`. The top-level production for the expression will be Eβ†’TE \rightarrow T, and for the term, it will be Tβ†’T1βˆ—FT \rightarrow T_1 F.

    • T1T_1 will correspond to the sub-expression `3`.

    • FF will correspond to the sub-expression `(4 + 2)`.


    Step 3: Evaluate the attribute for the sub-expression `3`.
    T1→F→digitT_1 \rightarrow F \rightarrow \text{digit}.
    T1.val=3T_1.val = 3

    Step 4: Evaluate the attribute for the sub-expression `(4 + 2)`.
    This corresponds to the production F→(E)F \rightarrow (E). We must find the value of EE for `4 + 2`.
    The production for this is E→E1+TE \rightarrow E_1 + T.

    • E1E_1 corresponds to `4`. E1β†’Tβ†’Fβ†’digitE_1 \rightarrow T \rightarrow F \rightarrow \text{digit}. So, E1.val=4E_1.val = 4.

    • TT corresponds to `2`. Tβ†’Fβ†’digitT \rightarrow F \rightarrow \text{digit}. So, T.val=2T.val = 2.

    Applying the rule for addition:
    E.val=E1.val+T.val=4+2=6E.val = E_1.val + T.val = 4 + 2 = 6

    Therefore, for the sub-expression `(4 + 2)`, we have:
    F.val=E.val=6F.val = E.val = 6

    Step 5: Compute the final value using the top-level production Tβ†’T1βˆ—FT \rightarrow T_1 * F.

    T.val=T1.valΓ—F.val=3Γ—6=18T.val = T_1.val \times F.val = 3 \times 6 = 18

    Result:
    The final value computed for the expression is 1818.
    "
    :::

    :::question type="MSQ" question="Which of the following statements about Syntax-Directed Definitions (SDDs) and their evaluation are correct?" options=["All S-attributed definitions can be evaluated by a bottom-up parser during parsing.","An L-attributed definition can have inherited attributes that depend on attributes of right siblings.","A top-down parser, like a recursive-descent parser, can evaluate any L-attributed definition.","Type checking is an application of synthesized attributes only."] answer="A,C" hint="Evaluate each statement based on the core definitions of S-attributed and L-attributed grammars and their relationship with parsing methods." solution="

    • A. All S-attributed definitions can be evaluated by a bottom-up parser during parsing. This is correct. S-attributed definitions use only synthesized attributes. In a bottom-up parser, a reduction occurs only after the entire right-hand side of a production (and its subtrees) have been processed. At this point, the attributes of all children are available on the stack, and the parent's synthesized attribute can be computed.


    • B. An L-attributed definition can have inherited attributes that depend on attributes of right siblings. This is incorrect. This is the primary restriction of L-attributed definitions. An inherited attribute of a symbol XjX_j can only depend on its parent and its left siblings (X1,…,Xjβˆ’1X_1, \dots, X_{j-1}).


    • C. A top-down parser, like a recursive-descent parser, can evaluate any L-attributed definition. This is correct. The left-to-right dependency flow in L-attributed definitions is designed to work with the depth-first, left-to-right traversal naturally performed by top-down parsers. Inherited attributes can be passed down as arguments to the parsing functions for non-terminals.


    • D. Type checking is an application of synthesized attributes only. This is incorrect. While synthesized attributes are used to pass the type of an expression up the tree (e.g., E.type=INTE.type = \text{INT}), inherited attributes are very useful for passing contextual type information down the tree, for example, in declarations like Dβ†’typeΒ LD \rightarrow \text{type } L, the type is passed to LL via an inherited attribute.

    "
    :::

    ---

    Summary

    ❗ Key Takeaways for GATE

    • Semantic Analysis bridges Syntax and Code Generation. Its primary role is to enforce context-sensitive rules, such as type checking and scope resolution, which cannot be handled by CFGs alone.

    • Attributes Define Meaning. SDDs augment grammars with attributes (synthesized for upward information flow, inherited for downward/sideways flow) and semantic rules that define how these attributes are computed.

    • S-Attributed vs. L-Attributed is a Core Distinction.

    • - S-Attributed: Only synthesized attributes. Evaluated naturally during bottom-up parsing.
      - L-Attributed: Synthesized attributes plus inherited attributes with a strict left-to-right dependency. Evaluated naturally during top-down parsing. Every S-attributed SDD is also L-attributed.
    • Evaluation Requires a Systematic Approach. For any SDT evaluation problem, the first and most critical step is to correctly build the parse tree based on the grammar's implicit precedence and associativity.

    ---

    What's Next?

    πŸ’‘ Continue Learning

    This topic is fundamental to understanding the compiler's front-end. It connects directly to:

      • Intermediate Code Generation: The semantic actions in an SDT are often used to produce an intermediate representation like Three-Address Code. The attributes computed here become the operands and results in that code.

      • Symbol Table Management: The `lookup` and `add_type` actions we've seen are interfaces to the symbol table. A deeper study of how symbol tables are implemented (e.g., hash tables, scope stacks) is essential.


    Mastering how information flows through the parse tree via attributes is key to solving a wide range of compiler design problems in GATE.

    ---

    Chapter Summary

    πŸ“– Syntax-Directed Translation - Key Takeaways

    In our study of Syntax-Directed Translation, we have established the formal mechanisms for bridging the gap between parsing and code generation. The semantic analysis phase, guided by the syntactic structure of the source program, appends meaning to the constructs recognized by the parser. For success in the GATE examination, a firm grasp of the following core concepts is essential.

    • Syntax-Directed Definitions (SDD) vs. Translation Schemes (SDT): We must distinguish between these two concepts. An SDD is a high-level specification that associates semantic rules with grammar productions, independent of implementation. An SDT, in contrast, is an implementation-focused mechanism that embeds semantic action code within the productions themselves, explicitly defining the order of evaluation.

    • Synthesized and Inherited Attributes: Attributes are the primary carriers of semantic information. A synthesized attribute at a node is computed from the attributes of its children, passing information up the parse tree. An inherited attribute is computed from the attributes of its parent and/or siblings, passing information down or across the tree.

    • S-Attributed and L-Attributed Definitions: These classifications are critical as they dictate the evaluation strategy. An S-Attributed Definition uses only synthesized attributes and is naturally suited for evaluation during a bottom-up parse (e.g., by an LR parser). An L-Attributed Definition is a more general form where an inherited attribute of a symbol on the right-hand side of a production can only depend on attributes of symbols to its left. This class is well-suited for evaluation by a top-down parser. Every S-Attributed SDD is also L-Attributed, but not vice-versa.

    • Dependency Graphs: For a given parse tree, the dependency graph is a directed graph that visualizes the flow of information among attribute instances. A directed edge from attribute XX to attribute YY signifies that YY's value depends on XX's value. A topological sort of this graph yields a valid evaluation order for the attributes.

    • Attribute Evaluation Order: The choice of evaluation order is tied to the parser. S-Attributed definitions can be evaluated by a post-order traversal of the parse tree. L-Attributed definitions can be evaluated by a depth-first, left-to-right traversal. Understanding this link between attribute type and parser capability is frequently tested.

    • Application in Type Checking: A principal application of SDT is static type checking. Semantic rules are used to determine the type of each expression from the types of its subexpressions. The type information, stored as attributes in the parse tree nodes, is checked for compatibility according to the language's type system.

    ---

    Chapter Review Questions

    :::question type="MCQ" question="Consider the following Syntax-Directed Definition (SDD), where `s` denotes a synthesized attribute and `i` denotes an inherited attribute.
    Grammar:
    A→BCA \rightarrow B C
    B→bB \rightarrow b
    C→cC \rightarrow c

    Semantic Rules:

  • B.i=A.i+2B.i = A.i + 2

  • C.i=B.s+3C.i = B.s + 3

  • A.s=C.sβˆ’1A.s = C.s - 1

  • B.s=B.iΓ—2B.s = B.i \times 2

  • C.s=C.iC.s = C.i
  • Which of the following statements is TRUE regarding this SDD?" options=["The SDD is both S-Attributed and L-Attributed.", "The SDD is L-Attributed but not S-Attributed.", "The SDD is S-Attributed but not L-Attributed.", "The SDD is neither S-Attributed nor L-Attributed."] answer="B" hint="Analyze the dependencies for each inherited attribute. Recall the specific constraint for an L-Attributed definition concerning dependencies on siblings." solution="
    Let us analyze the given SDD to classify it.

    1. Check for S-Attributed property:
    An SDD is S-Attributed if it uses only synthesized attributes. The given SDD uses both synthesized attributes (`s`) and inherited attributes (`i`). Therefore, it is not S-Attributed. This immediately eliminates options A and C.

    2. Check for L-Attributed property:
    An SDD is L-Attributed if, for any production Xβ†’Y1Y2…YnX \rightarrow Y_1 Y_2 \dots Y_n, the inherited attributes of YjY_j (for 1≀j≀n1 \le j \le n) depend only on:

    • The inherited attributes of XX.

    • The attributes (synthesized or inherited) of the symbols Y1,Y2,…,Yjβˆ’1Y_1, Y_2, \dots, Y_{j-1} (i.e., symbols to the left of YjY_j).


    Let's examine the rules for the production A→BCA \rightarrow B C:
    • Rule for B.iB.i: B.i=A.i+2B.i = A.i + 2. The inherited attribute of BB depends on the inherited attribute of the parent AA. This is valid under the L-Attributed definition.

    • Rule for C.iC.i: C.i=B.s+3C.i = B.s + 3. The inherited attribute of CC depends on a synthesized attribute of its left sibling, BB. This is also valid under the L-Attributed definition.


    Since all rules adhere to the constraints, the SDD is L-Attributed.

    Conclusion:
    The SDD is L-Attributed but not S-Attributed. Thus, option B is the correct answer.
    "
    :::

    :::question type="NAT" question="Consider the S-Attributed Definition for evaluating binary numbers, given by the following productions and semantic rules, where `val` is a synthesized attribute.

    N→SN \rightarrow S { N.val=S.valN.val = S.val }
    S→S1BS \rightarrow S_1 B { S.val=2×S1.val+B.valS.val = 2 \times S_1.val + B.val }
    S→BS \rightarrow B { S.val=B.valS.val = B.val }
    B→0B \rightarrow 0 { B.val=0B.val = 0 }
    B→1B \rightarrow 1 { B.val=1B.val = 1 }

    Using this SDD, compute the value for the input string `1101`." answer="13" hint="Trace the evaluation of the `val` attribute up the parse tree in a bottom-up fashion, corresponding to an LR parse of the string." solution="
    We need to evaluate the `val` attribute for the input string `1101`. The evaluation follows the structure of a bottom-up parse. Let's trace the reductions and the corresponding semantic rule applications.

    The input string is `1101`.

  • The first `1` is parsed.

  • - Reduction: Bβ†’1B \rightarrow 1. Rule: B.val=1B.val = 1.
    - Reduction: S→BS \rightarrow B. Rule: S.val=B.val=1S.val = B.val = 1.
    - The value of the prefix `1` is 1.

  • The next character `1` is parsed. The current prefix is `11`.

  • - Reduction: Bβ†’1B \rightarrow 1. Rule: B.val=1B.val = 1.
    - We now have S1S_1 (from prefix `1`) and BB (from current `1`).
    - Reduction: S→S1BS \rightarrow S_1 B. Rule: S.val=2×S1.val+B.val=2×1+1=3S.val = 2 \times S_1.val + B.val = 2 \times 1 + 1 = 3.
    - The value of the prefix `11` is 3.

  • The next character `0` is parsed. The current prefix is `110`.

  • - Reduction: Bβ†’0B \rightarrow 0. Rule: B.val=0B.val = 0.
    - We now have S1S_1 (from prefix `11`) and BB (from current `0`).
    - Reduction: S→S1BS \rightarrow S_1 B. Rule: S.val=2×S1.val+B.val=2×3+0=6S.val = 2 \times S_1.val + B.val = 2 \times 3 + 0 = 6.
    - The value of the prefix `110` is 6.

  • The final character `1` is parsed. The full string is `1101`.

  • - Reduction: Bβ†’1B \rightarrow 1. Rule: B.val=1B.val = 1.
    - We now have S1S_1 (from prefix `110`) and BB (from current `1`).
    - Reduction: S→S1BS \rightarrow S_1 B. Rule: S.val=2×S1.val+B.val=2×6+1=13S.val = 2 \times S_1.val + B.val = 2 \times 6 + 1 = 13.
    - The value of the string `1101` is 13.

    Finally, the start symbol reduction N→SN \rightarrow S sets N.val=S.val=13N.val = S.val = 13.

    The final computed value is 13.
    "
    :::

    :::question type="MCQ" question="A syntax-directed translation scheme is given for type checking expressions:

    E→E1+E2{if E1.type=int and E2.type=int then E.type=int else E.type=error}E \rightarrow E_1 + E_2 \quad \{ \text{if } E_1.type = \text{int and } E_2.type = \text{int then } E.type = \text{int else } E.type = \text{error} \}
    E→id{E.type=lookup(id.name)}E \rightarrow \text{id} \quad\quad\quad \{ E.type = \text{lookup(id.name)} \}
    E→num{E.type=int}E \rightarrow \text{num} \quad\quad \{ E.type = \text{int} \}
    E→real{E.type=float}E \rightarrow \text{real} \quad\quad \{ E.type = \text{float} \}

    The `lookup` function consults a symbol table with the following entries: `p: int`, `q: float`, `r: int`. What is the synthesized type attribute of the root of the parse tree for the expression `p + r + q`?" options=["int", "float", "error", "The type cannot be determined"] answer="C" hint="Evaluate the type attributes from left to right, following the left-associativity of the `+` operator. Check if the type rules are satisfied at each step." solution="
    Let us trace the evaluation of the `type` attribute for the expression `p + r + q`. The `+` operator is left-associative, so the expression is parsed as `(p + r) + q`.

    The parse tree structure for this expression is:
    ```
    E
    /|\
    E + q
    /|\
    p + r
    ```

    We evaluate the `type` attributes in a bottom-up (post-order) fashion.

    Step 1: Evaluate the types of the leaves.

    • For `p`: The rule is Eβ†’idE \rightarrow \text{id}. The action is E.type=lookup(’p’)E.type = \text{lookup('p')}. From the symbol table, the type of `p` is `int`. Let's call this node EpE_p. So, Ep.type=intE_p.type = \text{int}.

    • For `r`: The rule is Eβ†’idE \rightarrow \text{id}. The action is E.type=lookup(’r’)E.type = \text{lookup('r')}. From the symbol table, the type of `r` is `int`. Let's call this node ErE_r. So, Er.type=intE_r.type = \text{int}.

    • For `q`: The rule is Eβ†’idE \rightarrow \text{id}. The action is E.type=lookup(’q’)E.type = \text{lookup('q')}. From the symbol table, the type of `q` is `float`. Let's call this node EqE_q. So, Eq.type=floatE_q.type = \text{float}.


    Step 2: Evaluate the type of the subexpression `(p + r)`.
    • This corresponds to the production Eβ†’E1+E2E \rightarrow E_1 + E_2, where E1E_1 is EpE_p and E2E_2 is ErE_r.

    • We have E1.type=intE_1.type = \text{int} and E2.type=intE_2.type = \text{int}.

    • The semantic rule is: `{ if } E_1.type = \text{int and } E_2.type = \text{int then } E.type = \text{int else } E.type = \text{error} }`.

    • Since both conditions are met, the type of the resulting node (let's call it EprE_{pr}) is `int`. So, Epr.type=intE_{pr}.type = \text{int}.


    Step 3: Evaluate the type of the final expression `(p + r) + q`.
    • This corresponds to the production Eβ†’E1+E2E \rightarrow E_1 + E_2, where E1E_1 is the node for `(p + r)` (i.e., EprE_{pr}) and E2E_2 is the node for `q` (i.e., EqE_q).

    • We have E1.type=intE_1.type = \text{int} and E2.type=floatE_2.type = \text{float}.

    • We apply the same semantic rule: `{ if } E_1.type = \text{int and } E_2.type = \text{int then } E.type = \text{int else } E.type = \text{error} }`.

    • The condition `E_2.type = int` is false (since it is `float`).

    • Therefore, the `else` part of the rule is executed, and the type of the root node is set to `error`.


    The final synthesized type is `error`.
    "
    :::

    ---

    What's Next?

    πŸ’‘ Continue Your GATE Journey

    Having completed Syntax-Directed Translation, you have established a firm foundation for the subsequent, more pragmatic phases of compilation. The abstract concepts of attribute grammars and semantic rules now pave the way for concrete implementation tasks.

    Key connections to your learning path:

      • Relation to Previous Chapters: This chapter is a direct extension of Parsing. We have seen that the parse tree, the primary output of the parsing phase, is not merely a validation of syntax but the very scaffold upon which we build meaning. The distinctions between LL and LR parsing, which might have seemed purely algorithmic, now reveal their profound impact on attribute evaluation strategies. S-Attributed grammars align perfectly with bottom-up LR parsers, while L-Attributed grammars are the natural companion to top-down LL parsers.
      • Foundation for Future Chapters: The concepts mastered here are indispensable for the next stages of your study in Compiler Design:
    - Intermediate Code Generation: The "translation" in SDT culminates in the generation of an intermediate representation (IR). The semantic actions you have written abstractly will now be specified to produce concrete IR, such as Three-Address Code, quadruples, or triples. - Code Optimization: The semantic attributes we have computed, especially type information, are critical prerequisites for the code optimization phase. An optimizer relies on this information to perform valid transformations, such as replacing floating-point operations with more efficient integer arithmetic where permissible. - Symbol Table Management: We have used the symbol table as a given data structure. A deeper study will involve its implementation and how semantic actions for handling scopes (e.g., entering and leaving a block) interact with it.

    🎯 Key Points to Remember

    • βœ“ Master the core concepts in Syntax-Directed Translation before moving to advanced topics
    • βœ“ Practice with previous year questions to understand exam patterns
    • βœ“ Review short notes regularly for quick revision before exams

    Related Topics in Compiler Design

    More Resources

    Why Choose MastersUp?

    🎯

    AI-Powered Plans

    Personalized study schedules based on your exam date and learning pace

    πŸ“š

    15,000+ Questions

    Verified questions with detailed solutions from past papers

    πŸ“Š

    Smart Analytics

    Track your progress with subject-wise performance insights

    πŸ”–

    Bookmark & Revise

    Save important questions for quick revision before exams

    Start Your Free Preparation β†’

    No credit card required β€’ Free forever for basic features