0% found this document useful (0 votes)
32 views9 pages

CD Solutions

Uploaded by

Souvik Gon
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views9 pages

CD Solutions

Uploaded by

Souvik Gon
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 9

Q1) To translate the given expression a = -

(a+b)*(c+d)+(a+b+c) into the different intermediate code


forms, we'll break it down step by step. Here are the
representations:
i) Quadruple Representation
A quadruple consists of 4 fields: (Operator, Argument1,
Argument2, Result).
1. t1 = a + b → (`+`, `a`, `b`, `t1`)
2. t2 = c + d → (`+`, `c`, `d`, `t2`)
3. t3 = - t1 → (`-`, `t1`, ‘ ’, `t3`)
4. t4 = t3 * t2 → (`*`, `t3`, `t2`, `t4`)
5. t5 = a + b + c → (`+`, `t1`, `c`, `t5`)
6. a = t4 + t5 → (`+`, `t4`, `t5`, `a`)

ii) Triple Representation


In triples, there are 3 fields: (Operator, Argument1,
Argument2). The result is implicit and referenced by index.
1. (0) + a b → t1 = a + b
2. (1) + c d → t2 = c + d
3. (2) - (0) → t3 = - t1
4. (3) * (2) (1) → t4 = t3 * t2
5. (4) + (0) c → t5 = t1 + c
6. (5) + (3) (4) → a = t4 + t5
iii) Indirect Triple Representation
In indirect triples, we reference statements using pointers
instead of line numbers. Here’s how it would look:
Pointer Statement
p0 + a b
p1 + c d
p2 - p0
p3 * p2 p1
p4 + p0 c
p5 + p3 p4

iv) Three Address Code


Three-address code uses a maximum of three addresses
(operands).
1. `t1 = a + b`
2. `t2 = c + d`
3. `t3 = - t1`
4. `t4 = t3 * t2`
5. `t5 = t1 + c`
6. `a = t4 + t5`

Each format provides a structured way to represent the


expression using intermediate code concepts commonly
applied in compiler design.
Q2)
Features Quadruple Triplet Indirect Triplet

Number of Fields 4 fields: (Operator, 3 fields: (Operator, 3 fields, but


Arg1, Arg2, Arg1, Arg2) results are
Result) referenced
indirectly using
pointers
Result Explicit in a Implicit; result Uses a table of
Representation separate field stored in a pointers to triples
numbered index for referencing
results
Referencing Results are Results are Results are
referenced by referenced by the referenced
variable names index of the through pointers
operation to triple indices
Memory Requires more More compact as Similar to triples
Efficiency space as results results are but adds
need to be stored implicitly stored indirection
in a separate field through pointers
Ease of Use Easier to modify May require more Adds complexity
and maintain as the effort to track due to the
result field is index references indirection,
explicitly stored making code
harder to follow
Modification Easy to modify as More difficult to Indirection
results have modify, as any makes
distinct names change affects modification
subsequent index more complex,
references but isolated to
the pointer table
Execution Faster to retrieve Slightly slower Slightly slower
Efficiency the result using due to index look- due to indirect
explicit names up look-up of
references
Q3) (First Part)

LR Parsing:

LR parsing (Left-to-right, Rightmost derivation) is a bottom-up


method of syntax analysis, which reads the input string from left to
right and produces a rightmost derivation in reverse. LR parsers can
handle a larger class of grammars than predictive parsers. They are
used to construct parsers for programming languages.

Block Diagram for LR Parsing:

Key Components:

1. Input Buffer: Stores the input string being parsed.


2. Stack: Holds grammar symbols and states.
3. Parsing Table: Guides the parser by defining actions (shift,
reduce, accept, or error) based on the current input and state.
Main Advantages of LR Parsing:

1. Handles Larger Class of Grammars: LR parsers can handle most


programming language constructs, unlike simpler parsers like
LL.
2. Efficient Parsing: It efficiently handles deterministic context-
free languages, providing linear-time parsing in practice.
3. Detects Errors Early: LR parsers detect syntax errors as soon as
they are possible.
4. Automatic Construction: LR parsers can be automatically
constructed from context-free grammars.

(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

reduce A → aA reduce A → aA reduce A → aA


I6

Q4)

Criteria Predictive Parser Shift Reduce Parser

Approach Top-down, constructing Bottom-up, constructing


parse tree from root to parse tree from leaves to root
leaves
Direction of I/P Left-to-right (L) Left-to-right (L)
scanning
Derivation Leftmost derivation Rightmost derivation (in
reverse)

Backtracking Typically does not No backtracking,


backtrack if LL(1) grammar deterministic action (for
is used LR(0), SLR, LALR, etc.)
Grammar Handles LL(1) grammars Handles larger class of
types handled (subset of CFGs) grammars like LR and SLR
grammars
Stack Usage Uses an explicit recursive Uses an explicit parsing stack
call stack for shift and reduce actions

Lookahead Typically uses one symbol Can use lookahead (e.g., in


of lookahead (LL(1)) SLR or LALR parsers)

Example Recursive Descent Parser, LR(0), SLR(1), LALR(1), or


LL(1) Parser Canonical LR(1) Parsers
Q5) In the context of parsing, particularly in shift-reduce parsing, a handle is a specific part
of the input string that corresponds to the right-hand side of a production rule in the grammar.
A handle can be thought of as the segment of the input that can be reduced to a non-terminal
in the grammar.

Key Points about Handle:

• Reduction: When a handle is identified, it can be replaced (reduced) by the corresponding


non-terminal in the parsing process.
• Uniqueness: Each handle corresponds to a unique production in the grammar, and there can
be multiple handles in a string.
• Stack-based Parsing: In shift-reduce parsing, a handle is typically at the top of the parser's
stack, which is used to guide the reduction process.
• Example: In the string id + id, if we have a production rule like E → E + T, then the
handle could be the substring E + T when E is on the stack.

Basic Data Structures Used in Symbol Table

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:

• First(F) = { '(', 'id' }


• First(T) = First(F) = { '(', 'id' }
• First(E) = First(T) = { '(', 'id' }

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:

• Follow(E) = { '+', ')', ‘$’ }


• Follow(T) = { '+', '*', ')', ‘$’ }
• Follow(F) = { '+', '*', ')' , ‘$’}

+ * ( ) 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.

The key characteristics of recursive descent parsing include:

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.

The provided grammar is:

S → aBc

B → bc/b

To generate the string "abc", the parser would proceed as follows:

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.

3. Complete the Parse:

- If B to b is chosen, it will parse S as a b c , yielding the desired string "abc".

Drawbacks of Recursive Descent Parsing for This Grammar

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.

You might also like