Compiler Design Practical Exam Questions
Compiler Design Practical Exam Questions
Transforming an infix expression to postfix typically uses a stack data structure following the Shunting Yard algorithm: Process input left to right, pushing operators onto the stack considering precedence and associativity, and popping to the output when higher-precedence is met. Parentheses pull expressions together shifting control, yielding postfix as operands are output first, before operators, resulting in: abc*de^f-/+ .
A code generator for x := y + z can include the following steps: 1) Use intermediate code representations, such as three-address code. 2) Map expression to specific instructions for the target architecture. For example: generate an instruction to load y into a register, add z to this register, and store the result in memory location of x .
Challenges include left-recursion removal since it’s unsuitable for recursive descent. Alternations and multiple derivation paths introduce complexity as backtracking or lookahead is needed to disambiguate. Implementing predictive parsing tables with sophisticated FIRST and FOLLOW sets supports eliminating ε-productions and distinguishing paths based on input handling .
To design a Predictive Parser, construct parser tables with entries corresponding to specific input tokens and stack symbols. Use FIRST and FOLLOW sets to guide table entries. For the input (a,a), trace: push starting symbol S. At '(', use rule S -> (L). For L, use L -> L, S | S with input iteratively splitting at ',' and matching terminal 'a', spanning nested structures in a top-down fashion, resulting in a valid parse .
To compute 'FIRST' sets, analyze grammar rules to find terminals that appear at the start of derivations: 'FIRST(E)' includes {(, id} from both 'E -> E+T' and 'E -> T'. 'FOLLOW' sets are found by determining which terminals can appear directly following each variable in derivations using specific rules such as: If a production X -> αYβ exists, everything in 'FIRST(β)' (except ε) is in 'FOLLOW(Y)'. Repeat until no more changes can be identified .
Generating three-address code involves breaking complex expressions into simpler instructions. Every sub-expression gets a temporary variable if needed, ensuring operations like load, perform arithmetic, and store are separated. Equality constraints allocate temp variables reflecting computational dependencies and intermediate values, making subsequent optimizations like line addressing, dead code elimination feasible .
The process involves several steps: 1) Convert the regular expression into an NFA using Thompson's construction. 2) Define a start state for the NFA. 3) Create a DFA whose states correspond to sets of NFA states (subset construction method). 4) Continue processing from initial NFA state, grouping transitions, and marking the DFA acceptance states corresponding to the NFA’s acceptance states .
An LR(0) parser constructs a parse stack based on states from a DFA and makes parsing decisions via shift/reduce actions by looking at the current state and input. It uses grammar specific tables for parsing directly. A shift-reduce parser utilizes a stack to hold grammar symbols and inputs are matched through shifts and reduces without pre-computed states, potentially backtracking without LR format optimizations .
For shift-reduce parsing, start with an empty stack and shift input symbols (a and +) one by one while continually checking the top of the stack. Reduce whenever a valid production rule (E -> a or E -> E+E) matches with sequence stack content ensuring stack eventually reduces to start symbol completely. Failed final reduction or incomplete inputs indicate an invalid case; successful reduction confirms validity .
Predictive parsers rely on top-down parsing strategies that inherently struggle with left recursion, which causes infinite loops on recursive calls. Eliminating left recursion transformation ensures a deterministic parsing path, compatible with lookahead mechanisms to decide actions based on FIRST sets. This facilitates immediate recursive descent decisions avoiding undecidable loops and enabling LL(1) transformations .