CD Solutions
CD Solutions
LR Parsing:
Key Components:
(Second Part)
S → AA S’ → .S I0
S → .AA
A → aA
A → .aA
A→b S A → .b b
A a
S’ → S. I1 S → A.A I2 A → a.A I3 A → b. I4
A → .aA A → .b
A → .b
A A
S → AA. I5 A → aA. I6
State Action Goto
a b $ S A
I0 S3 S4 1 2
I1 accepted
I2 S3 S4 5
I3 S3 S4 6
reduce A → b reduce A → b reduce A → b
I4
I5 reduce S → AA reduce S → AA reduce S → AA
Q4)
A symbol table is a data structure used by a compiler to store information about identifiers
(variables, functions, classes, etc.) in the source code. Here are the basic data structures
commonly used in symbol tables:
1. Hash Table:
o Description: A hash table provides efficient insertion, deletion, and lookup
operations using a hash function to compute an index.
o Use Case: Frequently used due to its average-case constant time complexity for
lookups.
2. Binary Search Tree (BST):
o Description: A binary tree where each node has at most two children, and nodes are
arranged in a way that the left child is less than the parent, and the right child is
greater.
o Use Case: Used for maintaining ordered identifiers, allowing for efficient searching,
insertion, and deletion.
3. AVL Tree:
o Description: Balanced binary search trees that maintain height balance to ensure
O(log n) time complexity for operations.
o Use Case: Useful for dynamic symbol tables where the number of identifiers changes
frequently.
4. Linked List:
o Description: A simple linear list structure where each entry points to the next.
o Use Case: Used for simple implementations or where the number of identifiers is
small.
Q5) (i) First and Follow Sets:
First Set:
• First(E):
o From production E→E+T and E→T, we need to find First(T).
o First(E) will depend on First(T), so move to T.
• First(T):
o From T→T∗F and T→F, First(T) will depend on First(F).
• First(F):
o From F→(E), First(F) includes '('.
o From F→id, First(F) also includes 'id'.
Therefore:
Follow Set:
• Follow(E):
o By convention, since E is the start symbol, is included in Follow(E).
o From E→E+T, the symbol '+' follows E, so '+' is added to Follow(E).
• Follow(T):
o From E→E+T, '+' is included in Follow(T).
o From T→T∗F, '*' is included in Follow(T).
o Also, Follow(E) is passed to T due to E→T.
• Follow(F):
o From T→T∗F, Follow(F) includes '*' and Follow(T).
o From F→(E), Follow(F) includes ')' because ')' follows E.
Therefore:
+ * ( ) id $
E E → E+T E → E+T
F F → (F) F → id
T T → T*F T → T*E
(Note – This is a shit type of an example given by SLD. There is no null productions in the
grammar, so no meaningless calculation of Follows)
Q7) Recursive Descent Parsing
Recursive descent parsing is a top-down parsing technique that uses a set of recursive procedures to process
the input string based on a given grammar. Each non-terminal in the grammar corresponds to a procedure in
the parser, and these procedures call one another recursively to parse the input.
1. Backtracking: If a choice in a production fails, the parser can backtrack and try other alternatives.
2. Predictive Parsing: If the grammar is LL(1), the parser can decide which production to use based on the
next input symbol, making it more efficient.
S → aBc
B → bc/b
1. Start with S:
- Parse S as aBc
2. Process B:
- The parser must decide which production of B to use. It can either choose B to bc or B to b.
1. Ambiguity and Left Recursion: The grammar must be free of left recursion and ambiguity for recursive
descent parsing to work effectively. In this case, the grammar does not have left recursion, but if there were
multiple valid productions for B (e.g., if B could derive itself), the parser could enter an infinite loop.
2. Backtracking: If the parser initially chooses B to bc but the remaining input does not match, it must
backtrack to try B to b. This can lead to inefficiency, particularly in larger grammars.
3. LL(1) Limitation: Recursive descent parsers typically require the grammar to be LL(1), meaning they can only
make decisions based on the current input symbol. If the grammar has productions that require looking ahead
more than one symbol, it cannot be parsed using this method without additional lookahead mechanisms.
4. Complexity in Handling Alternatives: For more complex grammars, managing alternatives (like those in the
productions for B can become convoluted, leading to complicated parsing code that is hard to maintain.
5. Error Handling: Error recovery can be challenging in recursive descent parsers. If a mismatch occurs, it may
not be straightforward to determine how to recover and continue parsing the remaining input.