Syntax-Directed Translation: Infix to Prefix Notation
In compiler design, translating high-level program expressions into machine-friendly intermediate representations is a core task of the front-end parser. One common task is converting infix notation into prefix notation. This conversion is governed by rules of operator precedence and associativity .
To perform this translation systematically, we construct a Syntax-Directed Translation Scheme (SDTS) based on a context-free grammar (CFG) . By defining structural attributes for grammar symbols, we can perform translation in a single pass.
In this section, we will define a robust context-free grammar that handles basic arithmetic operators (, , ) while respecting operator precedence. We will establish a Syntax-Directed Definition (SDD) that uses synthesized attributes to construct the prefix string.
Footnotes
-
Aho, A. V., Lam, M. S., Sethi, R., & Ullman, J. D. (2006). Compilers: Principles, Techniques, and Tools (2nd Edition). Addison-Wesley. (Sections on Syntax-Directed Translation and Parser design). ↩
-
Cooper, K. D., & Torczon, L. (2011). Engineering a Compiler. Morgan Kaufmann. (Analysis of expression grammars, operator precedence, and syntax trees). ↩
Syntax Directed Translation (SDT) | Scheme & Implementation
The Grammar & Semantic Rules
To handle addition (), subtraction (), and multiplication (), we must design a grammar that naturally implements operator precedence (multiplication has higher precedence than addition and subtraction) and left-associativity (operations of equal precedence are evaluated from left to right) .
We use the standard unambiguous expression grammar:
To translate this grammar into prefix notation, we associate a synthesized string attribute, val, with each non-terminal (, , and ). The token has a lexical attribute which represents its character value.
The Syntax-Directed Translation Scheme (SDTS) for this conversion is defined below:
| Production | Semantic Rule / Action |
|---|---|
Parse Tree Construction and Evaluation
When a parser processes an input string, it builds a parse tree. In an S-attributed definition like ours, the synthesized attributes are evaluated bottom-up, starting from the leaf nodes () and propagating up to the root () .
Footnotes
-
Cooper, K. D., & Torczon, L. (2011). Engineering a Compiler. Morgan Kaufmann. (Analysis of expression grammars, operator precedence, and syntax trees). ↩
-
Grune, D., van Reeuwijk, K., Bal, H. E., Jacobs, C. J., & Langendoen, K. (2012). Modern Compiler Design. Springer. (Detailed explanation of S-attributed and L-attributed Definitions). ↩
Why Use Synthesized Attributes?
Synthesized attributes are perfect for bottom-up parsing algorithms (like LR parsers). Since the attributes of a parent node depend entirely on the attributes of its children, the parser can execute the semantic translation action immediately upon reducing a production rule.
Parsing & Translating 9 - 5 + 2
- 1Step 1
The input string
9 - 5 + 2is tokenized into three digits and two operators: , , , , and . - 2Step 2
First, reduce individual digits to factors () and then terms ():
- 3Step 3
Next, parse the digit :
Apply the reduction :
- E_2.\text{val} = \text{strcat}('- ', '9', ' ', '5') = '- 9 5'
- 4Step 4
Parse the final digit :
Apply the final reduction at the root :
- E.\text{val} = \text{strcat}('+ ', '- 9 5', ' ', '2') = '+ - 9 5 2'
Parsing & Translating 9 - 5 * 2
- 1Step 1
The input is
9 - 5 * 2. Because multiplication () has a higher precedence than subtraction (), the grammar forces to be grouped together under a term () before resolving the subtraction under an expression (). - 2Step 2
Reduce the first digit:
- 3Step 3
Reduce the remaining terms:
Apply the reduction :
- T_3.\text{val} = \text{strcat}('* ', '5', ' ', '2') = '* 5 2'
- 4Step 4
Apply the subtraction reduction at the root :
- E.\text{val} = \text{strcat}('- ', '9', ' ', '* 5 2') = '- 9 * 5 2'
Annotated Parse Trees
Below are the visual parse trees with annotated synthesized attributes showing exactly how the final prefix string is constructed from bottom-up attribute evaluation .
Annotated Parse Tree for
Annotated Parse Tree for
Footnotes
-
Grune, D., van Reeuwijk, K., Bal, H. E., Jacobs, C. J., & Langendoen, K. (2012). Modern Compiler Design. Springer. (Detailed explanation of S-attributed and L-attributed Definitions). ↩
Left-Recursion and Infix-to-Prefix Translation
While left-recursive grammars are perfectly suited for bottom-up parsers (like LALR/Yacc), they cannot be parsed directly by top-down LL(1) parsers. If you write a top-down translator, you must eliminate left recursion first, which changes the grammar structure and requires using inherited attributes to pass operands down the parse tree.
This tab displays the output generated by our synthesized SDTS:
-
Input:
9 - 5 + 2
Prefix Output:+ - 9 5 2 -
Input:
9 - 5 * 2
Prefix Output:- 9 * 5 2
In synthesized attributes, strings are accumulated and concatenated as operations are completed up the parse tree.
Parse Tree Nodes and Traversal Step Count
Comparison of node allocations and evaluation overhead between addition/subtraction and multiplication precedence levels.
Deep Dive into Grammar Mechanics
Knowledge Check
Which of the following is the correct prefix notation for the infix expression 7 * (3 + 2)?
Explore Related Topics
Ambiguity in the Grammar \(S \rightarrow ABA,\; A \rightarrow aA \mid \epsilon,\; B \rightarrow bB \mid \epsilon\)
The grammar (S\rightarrow ABA,;A\rightarrow aA\mid\epsilon,;B\rightarrow bB\mid\epsilon) is ambiguous because the two (A) nonterminals can distribute the same (a)-string in multiple ways, especially when (B) derives (\epsilon).
- It generates the language (L(S)={a^i b^j a^k\mid i,j,k\ge0}=a^*b^a^).
- The shortest string (a) has two distinct left‑most derivations (or parse trees), proving ambiguity.
- Every string (a^n) with (n\ge1) can be split between the two (A) symbols in (n+1) ways, yielding multiple parse trees.
- The ambiguity stems from both (A) producing (a^*) and (B) being able to vanish via (\epsilon).
- An equivalent unambiguous grammar can be constructed, showing the ambiguity is a property of this grammar, not necessarily of the language.
Lexical Analyzer Output in Compiler Design
In compiler design, the lexical analyzer’s sole output is a stream of tokens derived from the source code character stream.
- It scans characters left‑to‑right, grouping them into lexemes that match language patterns.
- Each lexeme is classified into a token category (e.g., ID, NUM, PLUS) possibly with attributes.
- The token stream is handed to the parser, which builds the parse tree or AST.
- Machine code, intermediate code, and parse trees are produced in later compilation phases, not by the lexer.
Matrix Chain Multiplication with Dynamic Programming: Optimal Parenthesization for $\{4,10,3,12,20,7\}$
The lesson shows how dynamic programming determines the cheapest way to multiply the matrix chain with dimensions .
- The recurrence with computes optimal sub‑costs.
- Filling the cost table yields , and the split table gives the top‑level split .
- The optimal parenthesization is , requiring scalar multiplications.
- The bottom‑up algorithm runs in time and uses space.