0% found this document useful (0 votes)
4 views13 pages

2. Question Bank With Answers of Compiler Design (CS-603(C ) ).Doc-1

The document discusses various concepts in compiler design, focusing on type checking, type expressions, coercions, polymorphic functions, heap allocation, parameter passing techniques, and symbol tables. It explains the differences between static and dynamic type checking, the structure and features of symbol tables, and how they are utilized in different phases of a compiler. Additionally, it outlines methods for implementing symbol tables, including lists, linked lists, binary search trees, and hash tables.

Uploaded by

I Know
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)
4 views13 pages

2. Question Bank With Answers of Compiler Design (CS-603(C ) ).Doc-1

The document discusses various concepts in compiler design, focusing on type checking, type expressions, coercions, polymorphic functions, heap allocation, parameter passing techniques, and symbol tables. It explains the differences between static and dynamic type checking, the structure and features of symbol tables, and how they are utilized in different phases of a compiler. Additionally, it outlines methods for implementing symbol tables, including lists, linked lists, binary search trees, and hash tables.

Uploaded by

I Know
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/ 13

Compiler Design (CS-603(C ))

Question Bank

Unit 3

Q.1 What is Type Checking? Explain with its types?

Ans= Type checking is the process of verifying and enforcing constraints of types in values. A
compiler must check that the source program should follow the syntactic and semantic
conventions of the source language and it should also check the type rules of the language.

It checks the type of objects and reports a type error in the case of a violation, and incorrect
types are corrected. Whatever the compiler we use, while it is compiling the program, it has
to follow the type rules of the language. Every language has its own set of type rules for the
language. We know that the information about data types is maintained and computed by the
compiler.

There are two kinds of type checking:


1.​ Static Type Checking.
2.​ Dynamic Type Checking.

Static type checking is defined as type checking performed at compile time. It checks the type
variables at compile-time, which means the type of the variable is known at the compile time.
It generally examines the program text during the translation of the program. Using the type
rules of a system, a compiler can infer from the source text that a function (fun) will be
applied to an operand (a) of the right type each time the expression fun(a) is evaluated.

Dynamic Type Checking:

Dynamic Type Checking is defined as the type checking being done at run time. In Dynamic
Type Checking, types are associated with values, not variables. Implementations of
dynamically type-checked languages runtime objects are generally associated with each other
through a type tag, which is a reference to a type containing its type information. Dynamic
typing results in more compact programs since it is more flexible and does not require types
to be spelled out.

Q.2 What is Type Expression? Explain in detail?

Ans= The type expressions are used to represent the type of a programming language
construct. Type expression can be a basic type or formed by recursively applying an operator
called a type constructor to other type expressions. The basic types and constructors depend
on the source language to be verified. Let us define type expression as follows:

●​ A basic type is a type expression


●​ Boolean, char, integer, real, void, type_error
●​ A type constructor applied to type expressions is a type expression
●​ Array: array(I, T)
●​ Array (I,T) is a type expression denoting the type of an array with elements of type T
and index set I, where T is a type expression. Index set I often represents a range of
integers. For example, the Pascal declaration var C: array[1..20] of integer;
associates the type expression array(1..20, integer) with C.
●​ Product: T1 × T2
●​ If T1 and T2 are two type expressions, then their Cartesian product T1 × T2 is a type
expression. We assume that × associates to the left.
●​ Record: record((N1 × T1) × (N2 × T2))

A record differs from a product. The fields of a record have names. The record type
constructor will be applied to a tuple formed from field types and field names. For example,
the Pascal program fragment

type node = record

address: integer ;

data : array [1..15] of char

end;

var node table : array [1..10] of node ;​

declares the type name “node” representing the type expression

record((address×integer) × (data × array(1..15,char)))

and the variable “node_table” to be an array of records of this type.

●​ Pointer: Pointer(T) is a type expression denoting the type “pointer to an object of type
T where T is a type expression. For example, in Pascal, the declaration var ptr: *row
declares variable “ptr” to have type pointer(row).

●​ Function: D → R , Mathematically, a function is a mapping from elements of one set


called domain to another set called range. We may treat functions in programming
languages as mapping a domain type “Dom” to a range type “Rg.”. The type of such
a function will be denoted by the type expression Dom → Rg. For example, the
built-in function mod, i.e. modulus of Pascal has type expression int × int → int.

Q.3 What is Coercions? Explain in detail?

Ans= The programming languages that enable mixed-mode expressions should describe
conventions for implicit operand type conversions. Coercion is defined as an automatic
conversion between types. For example in Pascal, if the operands for the addition operation
are of integer type and other real types, one of then the integer data object is implicitly
changed to type real earlier the addition is implemented.
For example, Pascal supports a built-in function round that changes a real-number data object
to an integer data object with a value similar to the rounded value of the real.

Q.4 What is Polymorphic function? Explain in detail?

Ans= A piece of code is said to be polymorphic if the statements in the body can be executed
with different types. A function that takes the arguments of different types and executes the
same code is a polymorphic function. The type checker designed for a language like Ada that
supports polymorphic functions, the type expressions are extended to include the expressions
that vary with type variables. The same operation performed on different types is called
overloading and are often found in object-oriented programming. For example, let us
consider the function that takes two arguments and returns the result.

int add(int, int)


int add(real, real)
real add(real, real)

The type expression for the function add is given as -

int × int → int


real × real → int
real × real → real
Q.5 Explain Heap Allocation in detail?
Ans= Heap allocation is used where the Stack allocation lacks if we want to retain the values
of the local variable after the activation record ends, which we cannot do in stack allocation,
here LIFO scheme does not work for the allocation and de-allocation of the activation record.
Heap is the most flexible storage allocation strategy we can dynamically allocate and
de-allocate local variables whenever the user wants according to the user needs at run-time.
The variables in heap allocation can be changed according to the user’s requirement. C, C++,
Python, and Java all of these support Heap Allocation.
For example:
int* ans = new int[5];

Advantages of Heap Allocation:


●​ Heap allocation is useful when we have data whose size is not fixed and can change
during the run time.
●​ We can retain the values of variables even if the activation records end.
●​ Heap allocation is the most flexible allocation scheme.

Disadvantages of Heap Allocation:


●​ Not Heap allocation is slower as compared to stack allocation.
●​ There is a chance of memory leaks.
Q.6 Explain Call by value and Call by Reference technique of parameter passing with
examples?
Ans= Call by Values: When using call by value, the compiler adds the r-value of the actual
parameters that were passed to the calling procedure to the called procedure's activation
record. Any modifications made to the formal parameters do not affect the actual parameters
because they include the values given by the calling procedure.

Let us see an example of call-by-values.

#include <bits/stdc++.h>

using namespace std;

// Formal parameter

void swap(int x, int y)

​ int temp = x;

​ x = y;

​ y = temp;

int main()

​ // Actual parameter

​ int x = 9;

​ int y = 12;

​ cout << "Values before swap: x= " << x << " y= " << y << endl;

​ swap(x, y);

​ cout << "Values after swap: x= " << x << " y= " << y << endl;

As we can see, even when we modify the contents of variables x and y defined in the scope of
the swap function, the modifications do not affect the variables' definitions in the scope of the
main. This is because main() will not be affected by changes performed in the swap() because
we call swap() by value, which will receive separate memory for x and y.

Call by Reference: The formal and actual parameters in a call by reference relate to the same
memory address. The activation record of the called function receives a copy of the L-value
of the actual arguments. As a result, the address of the actual parameters is passed to the
called function.

If the actual parameters do not contain an L-value, they are evaluated in a new temporary
location, and the location's address is passed. Since changes are made at the address, any
modifications to the formal parameter are reflected in the actual parameters. Let us see an
example of a call by reference.

#include <stdio.h>

// Call by reference

// Formal parameter

void swap(int *x, int *y) {

​ int temp = *x;

​ *x = *y;

​ *y = temp;

int main() {

​ // Actual parameter

​ int x = 9;

​ int y = 12;

​ printf("Values before swap: x = %d, y = %d\n", x, y);

​ swap(&x, &y);

​ printf("Values after swap: x = %d, y = %d", x, y);

As we can see, instead of int x, int y, we used int *x, int y, and instead of giving x,y, we gave
&x,&y in the function call. Due to the usage of pointers as function parameters, which return
the original parameters' address rather than their value, this practice is called by reference.

The variables' addresses are given using the & operator, and the memory location to which
the pointer is pointing is accessed using the * operator. The modifications done in
the swap() reflect in main(), as shown in the output above because the variable function
points to the same memory address as the original arguments.

Q.7 Explain Call by copy restore and Call by name technique of parameter passing with
examples?
Ans= Call by Copy Restore: In contrast to call-by-reference, changes to actual parameters
are made when the called procedure completes. The actual parameter values are stored in the
called procedure's activation record during the function call.

The manipulation of formal parameters has no immediate impact on the actual parameters;
however, the L-value of the formal parameters is copied to the actual parameters when the
called procedure completes.

#include <bits/stdc++.h>

using namespace std;

int a;

void func(int x) {

​ //a is still 10

​ x = 5;

​ //a is now 5

​ a = x;

//Function ends so the value of x is now stored in a and value of a is now 5

int main()

​ a = 10;

​ // When this ends the value of a will be 5

​ func(a);

​ cout << "The value of a is: " << a; //prints 5

Value is passed into the function in the example above, but until the function is complete, it
has no impact on the value of the passed-in variable. At that time, the function variable's final
value is saved in the passed-in variable.
Call by Name: A new form of preprocessor-like argument parsing mechanism is offered by
languages like Algol. Here, the procedure's body is called instead of the procedure's name.

#include <bits/stdc++.h>

using namespace std;

int a, b, c;

// Formal parameter

void swap(int a, int b) {

​ int t;

​ // These three swapping is done by compiler

​ t = a;

​ a = b;

​ b = t;

​ cout << a << " " << b << endl;

int main() {

​ // Actual parameter

​ int a = 9;

​ int b = 12;

​ cout << a << " " << b << endl;

​ //cout<<"Values before swap: x= "<<x<<" y= "<<y<<endl;

​ swap(a, b);

​ cout << a << " " << b;

​ //cout<<"Values after swap: x= "<<x<<" y= "<<y<<endl;

Here, the evaluation is done based on parameters. In all cases where formal parameters are
used in the technique, actual parameters are used instead of formal ones.
Q.8 What is Symbol Table ? Explain its features .

Ans= A symbol table is an important Data Structure used by compilers to manage identifiers
in a program. An identifier is a name given to a variable, function, class or other
programming construct that is used to represent some data or functionality in a program.
Identifiers must be declared before they are used in a program, and their properties (such as
data type, scope, and memory location) must be known by the compiler to generate the
correct code. A symbol table provides a way for the compiler to store this information and
access it as needed during the compilation process.

A symbol table typically consists of a set of entries, each of which represents an identifier
used in the program. Each entry contains information such as the identifier's name, data type,
scope, memory location, and any other attributes that may be needed by the compiler. The
symbol table is created and populated during the compilation phase, as the compiler scans the
program for identifiers and their declarations. Once the symbol table is populated, it can be
used by the compiler in several ways. For example, the compiler can use the symbol table to
check for errors in the program, such as undeclared variables or type mismatches.

Features Of Symbol Table :


This symbol table is used for the following purposes:
●​ It is used to keep all of the names of all entities in one place in a structured format.
●​ It is used to check whether or not a variable has been declared.
●​ It is used to identify the scope of a name.
It is used to implement type checking in source code by ensuring if assignments and
expressions are semantically accurate or not.​

Q.9 How Symbol Table is used by different phases of a compiler ? Explain .

Ans= It is utilized in the compiler's different phases, as shown below:

Lexical Analysis: New table entries are created in the table, For example, entries about
tokens.

Syntax Analysis: Adds the information about attribute type,, dimension, scope line of
reference, use, etc in the table.

Semantic Analysis: Checks for semantics in the table, i.e., verifies that expressions and
assignments are semantically accurate (type checking) and updates the table appropriately.

Intermediate Code generation: The symbol table is used to determine how much and what
type of run-time is allocated, as well as to add temporary variable data.

Code Optimization: Uses information from the symbol table for machine-dependent
optimization.

Target Code generation: Uses the address information of the identifier in the table to
generate code.
Q.10 How Symbol Table is implemented ? Explain .

Ans= If a compiler only needs to process a small quantity of data, the symbol table can be
implemented as an unordered list, which is simple to code but only works for small tables.
The following methods can be used to create a symbol table:

1.​ List: A List is a collection of elements in which each element has a position or an
index. This is one of the way to create a symbol table is to use a List to store the
entries for each identifier. This method is simple and easy to implement, but it may
not be efficient for large symbol tables because searching for a specific identifier
requires iterating through the entire List.

2.​ Linked List: A Linked List is a data structure in which each element contains a
reference to the next element in the list. One way to create a symbol table is to use a
Linked List to store the entries for each identifier. This method can be more efficient
than a List for searching because it allows for faster traversal of the entries. However,
it can be slower than other methods for inserting or deleting entries because it requires
modifying the references between elements.

3.​ Binary Search Tree: A Binary Search Tree is a data structure in which each node has
at most two children, and the values in the left subtree are less than the value in the
node, and the values in the right subtree are greater than the value in the node. One
way to create a symbol table is to use a Binary Search Tree to store the entries for
each identifier. This method is efficient for searching because it allows for a binary
search to quickly find a specific identifier. However, it can be slower than other
methods for inserting or deleting entries because it may require rebalancing the tree.

4.​ Hash Table: A Hash Table is a data structure that uses a hash function to map keys to
values. One way to create a symbol table is to use a Hash Table to store the entries for
each identifier. This method is efficient for searching, inserting, and deleting entries
because it allows for constant-time access to entries. However, it may require more
memory than other methods because it needs to store the hash table and handle
collisions between entries.

Q.11 Give examples for Static Type checking ?

Ans= Following are the examples for Static checking-

Type-checks: A compiler should report an error if an operator is applied to an incompatible


operand. For example, if an array variable and function variable are added together.

The flow of control checks: Statements that cause the flow of control to leave a construct
must have someplace to which to transfer the flow of control. For example, a break statement
in C causes control to leave the smallest enclosing while, for, or switch statement, an error
occurs if such an enclosing statement does not exist.

Uniqueness checks: There are situations in which an object must be defined only once. For
example, in Pascal an identifier must be declared uniquely, labels in a case statement must be
distinct, and else a statement in a scalar type may not be represented.
Name-related checks: Sometimes the same name may appear two or more times. For
example in Ada, a loop may have a name that appears at the beginning and end of the
construct. The compiler must check that the same name is used at both places.

Q.12 Explain advantages of Type conversion ?

Ans= There are the following advantages of type conversion which are as follows −
●​ If during type checking, a mismatch appears between the actual type of an argument
and the expected type for that operation, then type conversion easily converts the data
object implicitly and prevents the error.

●​ In some languages such as C, type conversion is a built-in function, which implicitly


casts an expression to convert it to the correct type.

●​ Type conversion is automatically invoked in certain cases of mismatch. For example,


in Pascal, if the arguments for an arithmetic operation including ‘+’ are of mixed real
and integer type, the integer data object is implicitly converted to type real earlier the
addition is implemented.

●​ There is no data is hidden. Because each short integer can be defined as a long
integer, no data is hidden by necessarily invoking a short int→long int.

●​ With dynamic type checking, conversions or coercions are built at the point that the
type mismatch is recognized during execution. For such languages narrowing
conversions can be enabled. For static type checking, more code is added to the
compiled program.

●​ Type conversion is a subtitle need when there is a large number of data types in a
programming language.

Q.13 Explain Overloading Of Functions And Operators ?


Ans= An operator is overloaded if the same operator performs different operations. For
example, in arithmetic expression a + b, the addition operator “+” is overloaded because it
performs different operations, when a and b are of different types like integer, real, complex,
and so on.

Another example of operator overloading is overloaded parenthesis in ada, that i, the


expression A(i) has different meanings. It can be the i th element of an array, or a call to
function A with argument I, and so on. Operator overloading is resolved when the unique
definition for an overloaded operator is determined. The process of resolving overloading is
called operator identification because it specifies what operation an operator performs. The
overloading of arithmetic operators can be easily resolved by processing only the operands of
an operator.

Like operator overloading, the function can also be overloaded. In function overloading, the
functions have the same name but different numbers and arguments of different types. In
Ada, the operator “*” has the standard meaning that it takes a pair of integers and returns an
integer. The function of “*” can be overloaded by adding the following declarations:
Function “*”(a,b: integer) return integer. Function “*”(a,b: complex) return integer. Function
“*”(a,b: complex) return complex.
By addition of the above declarations, now the operator “*” can take the following possible
types:

●​ It takes a pair of integers and returns an integer


●​ It takes a pair of integers and returns a complex number
●​ It takes a pair of complex numbers and returns a complex number
Function overloading can be resolved by the type checker based on the number and types of
arguments. The type checking rule for function by assuming that each expression has a
unique type is given as

E → E1(E2)
{
E.type : = t
E2.type : = t → u then
E.type : = u
else E.type : = type_error

E′ → E {E′.type := E.type}

E → id {E.type := lookup(id.entry)}

E → E1(E2) {E.type := { u | there exists an s in E2.type


Such that s → u is in E1.type }

Table : Overloading Of Functions and Operators

Q.14 Explain Panic Mode Recovery and Statement Mode Recovery ?


Ans=
Panic Mode Recovery :In this method, successive characters from the input are removed one
at a time until a designated set of synchronizing tokens is found. Synchronizing tokens are
deli-meters such as; or }. The advantage is that it’s easy to implement and guarantees not to
go into an infinite loop. The disadvantage is that a considerable amount of input is skipped
without checking it for additional errors.

Statement Mode recovery :In this method, when a parser encounters an error, it performs
the necessary correction on the remaining input so that the rest of the input statement allows
the parser to parse ahead. The correction can be deletion of extra semicolons, replacing the
comma with semicolons, or inserting a missing semicolon.While performing correction,
utmost care should be taken for not going in an infinite loop. A disadvantage is that it finds it
difficult to handle situations where the actual error occurred before pointing of detection.

Q.15 Explain advantages and disadvantages of Error detection and Recovery ?


Ans=

Advantages:

Improved code quality: Error detection and recovery in a compiler can improve the overall
quality of the code produced. This is because errors can be identified early in the compilation
process and addressed before they become bigger issues.

Increased productivity: Error recovery can also increase productivity by allowing the
compiler to continue processing the code after an error is detected. This means that
developers do not have to stop and fix every error manually, saving time and effort.

Better user experience: Error recovery can also improve the user experience of software
applications. When errors are handled gracefully, users are less likely to become frustrated
and are more likely to continue using the application.

Better debugging: Error recovery in a compiler can help developers to identify and debug
errors more efficiently. By providing detailed error messages, the compiler can assist
developers in pinpointing the source of the error, saving time and effort.

Consistent error handling: Error recovery ensures that all errors are handled in a consistent
manner, which can help to maintain the quality and reliability of the software being
developed.

Reduced maintenance costs: By detecting and addressing errors early in the development
process, error recovery can help to reduce maintenance costs associated with fixing errors in
later stages of the software development lifecycle.

Improved software performance: Error recovery can help to identify and address code that
may cause performance issues, such as memory leaks or inefficient algorithms. By improving
the performance of the code, the overall performance of the software can be improved as
well.

Disadvantages:

Slower compilation time: Error detection and recovery can slow down the compilation
process, especially if the recovery mechanism is complex. This can be an issue in large
software projects where the compilation time can be a bottleneck.

Increased complexity: Error recovery can also increase the complexity of the compiler,
making it harder to maintain and debug. This can lead to additional development costs and
longer development times.

Risk of silent errors: Error recovery can sometimes mask errors in the code, leading to silent
errors that go unnoticed. This can be particularly problematic if the error affects the behavior
of the software application in subtle ways.

Potential for incorrect recovery: If the error recovery mechanism is not implemented
correctly, it can potentially introduce new errors or cause the code to behave unexpectedly.
Dependency on the recovery mechanism: If developers rely too heavily on the error
recovery mechanism, they may become complacent and not thoroughly check their code for
errors. This can lead to errors being missed or not addressed properly.

Difficulty in diagnosing errors: Error recovery can make it more difficult to diagnose and
debug errors since the error message may not accurately reflect the root cause of the issue.
This can make it harder to fix errors and may lead to longer development times.

Compatibility issues: Error recovery mechanisms may not be compatible with certain
programming languages or platforms, leading to issues with portability and cross-platform
development.

You might also like