6.1 Syntax-Directed Definitions (SDD) & Syntax-Directed Translation (SDT) Schemes
π‘ Core Intuition: The 3-Second Mental Modelβ
π³ The Everyday Analogy: The Declarative Blueprint vs The Construction Triggerβ
A parser alone only checks grammar. It verifies that salary = hours * 50; is a syntactically valid sentence, but it has no understanding of what salary is, whether hours is an integer or string, or what machine instructions to generate.
Semantic Analysis attaches concrete meaning to syntax trees using two complementary formalisms:
- Syntax-Directed Definition (SDD) β The Declarative Blueprint:
- Associates mathematical quantities (Attributes) with grammar symbols.
- For every production rule, it specifies mathematical equations (Semantic Rules) defining how attributes relate to each other.
- It specifies what values to compute without specifying when or in what order to evaluate them.
- Syntax-Directed Translation Scheme (SDT) β The Imperative Trigger:
- Embeds explicit program code fragments (Semantic Actions inside
{ ... }) directly into the production bodies. - It specifies exact execution timing: "When the parser reaches this position during tree traversal or stack reduction, immediately execute this block of code!"
- Embeds explicit program code fragments (Semantic Actions inside
π» Bridging to Computer Science: Synthesized and Inherited Attribute Propagationβ
In compiler syntax-directed translation:
- Attributes decorate the parse tree nodes.
- Synthesized Attributes flow strictly upward (from children to parent). They represent bottom-up computations, such as evaluating the numeric value of an expression.
- Inherited Attributes flow downward or laterally (from parent or left siblings to a node). They represent contextual information, such as passing a declared variable type down to a list of identifier names.
πTable of Contents
- ποΈ Architectural Foundations: The SDD & SDT Pipeline
- π Formal Mathematical Foundations: Attributes & Rules
- π² The Annotated Parse Tree (Decorated Parse Tree)
- βοΈ Syntax-Directed Translation Schemes (SDT)
- π Real-World Production Context: How Industry Builds This
- π Numerical Applications & Worked Examples
- π― Exam & Interview Pitfall Check
ποΈ Architectural Foundations: The SDD & SDT Pipelineβ
The Syntax-Directed Translation Lifecycle
From declarative grammar equations to deterministic attribute evaluation over parse trees
Context-Free Syntax
Underlying CFG specifying valid syntactic sentences.
Semantic Rules
Declarative attribute definitions associated with productions.
Dependency Graph
Directed edges capture data dependencies between node attributes.
Annotated Tree
Evaluation order executes semantic actions and populates values.
βοΈ SDD vs SDT: Declarative vs Imperative Formalismsβ
SDD vs SDT: Declarative Specification vs Imperative Action
Contrasting pure attribute equations against parser-synchronized code execution
Syntax-Directed Definition (SDD)
- β’High-level declarative specification; zero order-of-execution constraints.
- β’Uses pure mathematical functions without arbitrary side effects.
- β’Evaluated over an Annotated Parse Tree using topological sorting.
- β’Ideal for formal reasoning and automated compiler generation.
Syntax-Directed Translation (SDT)
- β’Imperative execution scheme; embeds action code inside { }.
- β’Order of execution is explicitly determined by parser tree traversal.
- β’Can perform arbitrary side effects: emit assembly, modify symbol table.
- β’Directly implemented during Shift/Reduce bottom-up reductions.
π Formal Mathematical Foundations: Attributes & Rulesβ
π·οΈ What is an Attribute?β
An Attribute is any quantity associated with a grammar symbol . Examples include:
val: The numeric value of an expression.type: The data type of a variable (int,float,pointer).addr: The memory location or temporary register holding a value.name: The string lexeme of an identifier.
π³ Synthesized vs Inherited Attributesβ
Attributes are categorized into two fundamental classes based on how their values flow through the parse tree:
1. Synthesized Attributes (Bottom-Up Flow β¬οΈ)β
An attribute of non-terminal is Synthesized if its value at parse tree node is computed solely from:
- Attribute values of the children of .
- Other attributes of itself.
For production :
The Terminal Invariant: Terminals can ONLY possess synthesized attributes! Their values are computed by the lexical analyzer (e.g.
NUM.lexval = 42) and supplied directly to the parse tree. Terminals can NEVER have inherited attributes.
2. Inherited Attributes (Top-Down & Sibling Flow β¬οΈβ‘οΈ)β
An attribute of non-terminal is Inherited if its value at parse tree node is computed from:
- Attribute values of the parent of .
- Attribute values of the siblings of .
- Other attributes of itself.
For production :
Inherited attributes pass contextual information downward, such as:
- Passing declared variable types from a declaration header down into a list of identifier names.
- Passing base register addresses down into array index offsets.
Attribute Data Flow Topology
Comparing the directional propagation models of Synthesized vs Inherited attributes
Synthesized Attributes (Bottom-Up: β¬οΈ Children β Parent)
A.a = f(X1, X2, ... Xk). Children calculate sub-results and pass them upward to the parent node.
Inherited Attributes (Top-Down / Lateral: β¬οΈ Parent / β‘οΈ Siblings β Child)
B.b = f(Parent.a, LeftSiblings.c). Contextual information passed down from enclosing scope or left siblings.
π² The Annotated Parse Tree (Decorated Parse Tree)β
An Annotated Parse Tree (or Decorated Parse Tree) is a standard parse tree where each node displays the concrete values of its associated attributes.
Canonical Example: The Desk Calculator SDDβ
Consider an SDD that computes the numeric values of arithmetic expressions:
| Production | Semantic Rule | Attribute Nature |
|---|---|---|
| Synthesized | ||
| Synthesized | ||
| Synthesized | ||
| Synthesized | ||
| Synthesized | ||
| Synthesized | ||
| Synthesized |
(Subscripts and are used solely to distinguish production occurrences).
βοΈ Syntax-Directed Translation Schemes (SDT)β
An SDT embeds semantic action blocks within production right-hand sides:
E -> E '+' T { printf("+"); }
| T
T -> T '*' F { printf("*"); }
| F
F -> ID { printf("%s", $1); }
Infix to Postfix Translation via SDTβ
When parsing input a + b * c with the SDT above:
F -> IDmatchesaprintsa.F -> IDmatchesbprintsb.F -> IDmatchescprintsc.T -> T * Freduces prints*.E -> E + Treduces prints+.
Output Stream: a b c * + (Standard Reverse Polish / Postfix Notation!).
π Real-World Production Context: How Industry Builds Thisβ
In industrial bottom-up compiler generators (such as Yacc, GNU Bison, and Tree-sitter):
Industrial bottom-up parsers do not construct full parse trees in RAM to evaluate synthesized attributes!
- Alongside the state stack, the engine maintains a parallel Value Stack (
yyvsp). - In a semantic action:
$$represents the synthesized attribute of the left-hand side non-terminal ().$1represents the attribute of the first symbol on the RHS ().$2represents the attribute of the second symbol on the RHS (), and so on.
- During reduction by :
$$ = $1 + $3; /* Pops 3 values from value stack, computes sum, pushes result */ - This enables attribute evaluation on-the-fly during parsing with zero tree allocation!
In production parsers, semantic actions can emit intermediate code, insert records into the global symbol table, or print debug AST dumps directly during the reduction step.
π Numerical Applications & Worked Examplesβ
Example 1: Annotated Parse Tree Evaluationβ
Evaluate the annotated parse tree for the input expression:
Using the Desk Calculator SDD. Determine the computed val attribute at every node.
Step-by-Step Analytical Trace:
-
Leaf Lexical Values:
- :
- :
- :
-
Evaluate Subtree :
- .
- .
- .
- .
- .
-
Evaluate Subtree :
- .
- .
-
Evaluate Root Expression :
- .
-
Top-Level Goal:
- .
Final Result: . The semantic evaluation respects operator precedence automatically through tree depth!
Example 2: Type Distribution via Inherited Attributesβ
Consider the variable declaration grammar:
Design an SDD using an inherited attribute in_type to propagate the type from down to all identifiers in list , and insert each identifier with its type into the symbol table.
Step-by-Step Semantic Rules:
| Production | Semantic Rule | Attribute Classification |
|---|---|---|
| is Inherited (from sibling ) | ||
| is Synthesized | ||
| is Synthesized | ||
| | is Inherited (from parent ) | |
| Semantic Action |
For input int x, y, z;:
- is synthesized at .
- inherits from its left sibling .
- As expands downward, inherits from parent .
- Each identifier
x,y,zreceives type and records it in the symbol table!
π― Exam & Interview Pitfall Checkβ
Question 1: "Can terminal symbols ever possess inherited attributes in an SDD?"
Key Focus Points:
- Strict Rule: Never. Terminal symbols can ONLY possess synthesized attributes.
- Reason: Terminal symbols are generated and recognized by the lexical analyzer (scanner). Their attributes (such as
lexvalorlexeme) are supplied by the lexer before parsing begins and flow upward into the parse tree.
Question 2: "What is the primary difference between an SDD and an SDT?"
Key Focus Points:
- SDD (Syntax-Directed Definition): High-level declarative specification. Uses mathematical equations without evaluation order constraints or arbitrary side effects.
- SDT (Syntax-Directed Translation Scheme): Imperative implementation scheme. Embeds program fragments
{ actions }directly inside production right-hand sides with explicit execution timing.
Trap 1: Assuming circular dependencies can be evaluated by top-down parsers.
Answer: Impossible. If an SDD has a directed cycle in its dependency graph (e.g. and ), no topological sort exists. The definition is ill-formed, and no parser can evaluate it.
Trap 2: Believing inherited attributes can only flow from parent to child.
Answer: Incomplete. An inherited attribute at node can receive values from its parent AND from its sibling nodes (specifically its left siblings in L-attributed SDDs).