IAT - 1 Set2 Answers
IAT - 1 Set2 Answers
Part – A
(Each Question carries 2 Marks)
1. What is a compiler, and why is it essential in program execution?
A compiler is a software tool that translates high-level source code written in a programming language
into machine code, intermediate code, or another low-level format understandable by a computer. This
translation typically occurs in multiple stages, such as lexical analysis, syntax analysis, semantic analysis,
optimization, and code generation.
2. Differentiate between compilation and interpretation with examples
Aspect Compilation Interpretation
Translates the entire source code into Executes the source code line-by-line or
Definition
machine code before execution. statement-by-statement.
Produces an independent executable file
Output Does not produce a separate executable file.
(machine code).
Faster since the translation is done Slower because each instruction is translated
Execution Speed
beforehand. and executed on the fly.
Errors are detected at compile time before
Error Handling Errors are detected during runtime.
execution.
C, C++, Java (with JVM for bytecode
Examples Python, Ruby, JavaScript.
execution).
3. Explain ambiguous grammar with a suitable example.
An ambiguous grammar is a grammar in which a single string (or sentence) can have more than one valid parse tree
or derivation. In other words, the grammar allows multiple interpretations of the same input, making it difficult to determine the
intended structure of the string.
4. Enumerate the types of errors that can occur during compilation and describe various recovery modes
Types of Errors
1. Lexical Errors: Errors due to invalid tokens, such as illegal characters or identifiers.
2. Syntax Errors: Errors in the grammar or structure of the program (e.g., missing semicolon or brackets).
3. Semantic Errors: Errors due to incorrect meaning or usage of constructs (e.g., type mismatches).
4. Runtime Errors: Errors that occur during program execution (e.g., division by zero, null pointer access).
5. Logical Errors: Errors in the logic of the program that lead to incorrect outputs.
Recovery Modes
1. Panic Mode Recovery: The parser skips to a predefined set of synchronizing tokens to resume parsing.
2. Phrase-Level Recovery: The parser corrects the error by inserting, deleting, or modifying tokens locally.
3. Error Productions: Specific error-handling rules are added to the grammar to detect and recover from common
errors.
4. Global Correction: Analyzes and modifies the entire source code to correct errors with minimal changes.
Aspec
Synthesized Attribute Inherited Attribute
t
An attribute whose value is computed from the An attribute whose value is determined using the
Definition
attributes of child nodes. attributes of parent or sibling nodes.
Direction Information flows top-down or laterally in the
Information flows bottom-up in the parse tree.
of Flow parse tree.
Often used for evaluating expressions or computing Often used for passing contextual information, such
Usage
results. as variable scope.
Example In E → E1 + T, the value of E.val = E1.val + T.val. In S → id = E, the type of E is inherited from S.
Part – B
(Answer for each question carries 10 Marks)
1) a) Illustrate the structure of LEX with a detailed explanation and a sample program.
Lexical Analysis
It is the first step of compiler design, it takes the input as a stream of characters and gives the output as tokens also known
as tokenization. The tokens can be classified into identifiers, Sperators, Keywords, Operators, Constants and Special
Characters.
It has three phases:
Tokenization: It takes the stream of characters and converts it into tokens.
Error Messages: It gives errors related to lexical analysis such as exceeding length, unmatched string, etc.
Eliminate Comments: Eliminates all the spaces, blank spaces, new lines, and indentations.
What is Lex in Compiler Design?
Lex is a tool or a computer program that generates Lexical Analyzers (converts the stream of characters into tokens).
The Lex tool itself is a compiler. The Lex compiler takes the input and transforms that input into input patterns. It is
commonly used with YACC(Yet Another Compiler Compiler). It was written by Mike Lesk and Eric Schmidt.
Function of Lex
1. In the first step the source code which is in the Lex language
having the file name ‘File.l’ gives as input to the Lex Compiler
commonly known as Lex to get the output as lex.yy.c.
2. After that, the output lex.yy.c will be used as input to the C
compiler which gives the output in the form of an ‘a.out’ file, and
finally, the output file a.out will take the stream of character and
generates tokens as output.
lex.yy.c: It is a C program.
File.l: It is a Lex source program
a.out: It is a Lexical analyzer
(b) Describe the structure and working of a compiler with a detailed diagram.
Phases of a Compiler
We basically have two phases of compilers, namely the Analysis phase and Synthesis phase. The analysis phase creates an
intermediate representation from the given source code. The synthesis phase creates an equivalent target program from the
intermediate representation.
A compiler is a software program that converts the high-level source code written in a programming language into low-
level machine code that can be executed by the computer hardware. The process of converting the source code into
machine code involves several phases or stages, which are collectively known as the phases of a compiler. The typical
phases of a compiler are:
QP Code : 16727
1. Lexical Analysis: The first phase of a compiler is lexical analysis, also known as scanning. This phase reads the
source code and breaks it into a stream of tokens, which are the basic units of the programming language. The tokens
are then passed on to the next phase for further processing.
2. Syntax Analysis: The second phase of a compiler is syntax analysis, also known as parsing. This phase takes the
stream of tokens generated by the lexical analysis phase and checks whether they conform to the grammar of the
programming language. The output of this phase is usually an Abstract Syntax Tree (AST).
3. Semantic Analysis: The third phase of a compiler is semantic analysis. This phase checks whether the code is
semantically correct, i.e., whether it conforms to the language’s type
system and other semantic rules. In this stage, the compiler checks the
meaning of the source code to ensure that it makes sense. The compiler
performs type checking, which ensures that variables are used correctly
and that operations are performed on compatible data types. The
compiler also checks for other semantic errors, such as undeclared
variables and incorrect function calls.
4. Intermediate Code Generation: The fourth phase of a compiler is
intermediate code generation. This phase generates an intermediate
representation of the source code that can be easily translated into
machine code.
5. Optimization: The fifth phase of a compiler is optimization. This phase
applies various optimization techniques to the intermediate code to
improve the performance of the generated machine code.
Code Generation: The final phase of a compiler is code generation. This phase
takes the optimized intermediate code and generates the actual machine code
that can be executed by the target hardware
7) a) Construct and minimize the DFA for the given regular expression (a+b).(b+c)
Step 1: Breakdown of the Regular Expression
The given regular expression is:
(a + b): Matches either “a” or “b”.
(b + c): Matches either “b” or “c”.
Concatenation: The sequence matches a string where the first component “a + b” is followed by the second
component “b + c”.
T { +, ), $ }
T′ { +, ), $ }
F { *, +, ), $ }
∗FT′* F
T FT′F T' FT′F T'
T' ε εε
T'
F idid (E)(E)
#include <stdio.h>
#include <stdlib.h>
%}
%token NUMBER
%%
expr: expr '+' term { printf("Sum: %d\n", $1 + $3); $$ = $1 + $3; }
| term { $$ = $1; }
;
term: term '*' factor { printf("Product: %d\n", $1 * $3); $$ = $1 * $3; }
| factor { $$ = $1; }
;
factor: '(' expr ')' { $$ = $2; }
| NUMBER { $$ = $1; }
;
%%
int main() {
printf("Enter an expression: ");
yyparse();
return 0;
}
int yyerror(char *s) {
fprintf(stderr, "Error: %s\n", s);
return 0;
}
Code Explanation
1. Declarations Section:
o %{ and %} enclose C code to include in the output file.
o #include statements add required libraries.
o %token NUMBER declares tokens used in the grammar.
2. Rules Section:
o Contains grammar rules (e.g., expr: expr '+' term).
o Actions in {} specify what happens when a rule is matched (e.g., $1 and $3 refer to rule components, and $$
stores results).
3. Auxiliary Code Section:
o main() runs the parser with yyparse().
o yyerror() handles syntax errors.
Input and Output Example
Input:
3+4*5
Output:
Product: 20
Sum: 23
9) a) YACC Tool Explanation
YACC (Yet Another Compiler-Compiler) is a tool used to generate parsers. It is commonly used in compiler design
to handle syntax analysis. YACC takes a formal grammar as input, typically in Backus-Naur Form (BNF), and
generates a parser in the C programming language. The parser evaluates whether an input string conforms to the
specified grammar and performs specified actions.
How It Works
1. Lexer: The yylex() function processes the input to generate tokens (NUMBER, +, -).
2. Parser: The parser generated by YACC uses the grammar to parse the input.
3. Action Execution: When rules are reduced, the corresponding actions (e.g., arithmetic operations) are executed.
Simpler YACC Program: Adding Two Numbers
QP Code : 16727
%{
#include <stdio.h>
#include <stdlib.h>
int yylex();
void yyerror(const char *s);
%}
%token NUMBER
%%
sum:
NUMBER '+' NUMBER {
printf("Sum: %d\n", $1 + $3);
}
;
%%
int main() {
printf("Enter two numbers to add (e.g., 3 + 5): ");
yyparse();
return 0;
}
int yylex() {
int c = getchar();
if (c >= '0' && c <= '9') {
ungetc(c, stdin);
scanf("%d", &yylval);
return NUMBER;
}
return c;
}
Example Input/Output
Input:
4+5
Output:
Sum: 9
Sum: 23
b) Illustrate
recursive descent parser shift reduce parser for the following grammar and checking
the input string :”id+id-id”
E->E+E / E- E/ id
Shift-Reduce Parser
The Shift-Reduce Parser uses a stack to shift input symbols and reduce them using grammar
rules.
Parsing Table (Stack + Input + Action):
QP Code : 16727
Introduction
An intermediate language (IL) is a representation of a program between the source code and machine code
during the compilation process. It is used to improve portability, simplify optimization, and ease the generation of
machine-specific code.
Compilers typically translate source code into an intermediate language before generating machine code. This
approach divides the compilation process into manageable stages, enhancing the modularity and reusability of the
compiler components.
Applications
1. Java Bytecode: Java programs are compiled into bytecode, an intermediate language executed by the Java
Virtual Machine (JVM).
2. .NET Common Intermediate Language (CIL): C#, VB.NET, and other languages are compiled into CIL
for execution by the .NET runtime.
3. LLVM Intermediate Representation (LLVM IR): A low-level, strongly-typed IL used for program
analysis and optimization in the LLVM framework.
b) Analyze about Syntax Directed Definitions and annotated parse tree for simple desk calculator.
Evaluation Steps:
1. NUM(3).val = 3, NUM(4).val = 4, NUM(5).val = 5.
2. F.val = NUM.val: F(3).val = 3, F(4).val = 4, F(5).val = 5.
3. T(4 * 5).val = F(4).val * F(5).val = 4 * 5 = 20.
E(3 + 20).val = T(3).val + T(20).val = 3 + 20 = 23.