0% found this document useful (0 votes)
34 views10 pages

Atcd - Unit 3

AI
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)
34 views10 pages

Atcd - Unit 3

AI
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/ 10

ATCD

UNIT –3

1. Explain the Intermediate Code forms. Give one example of each.

Intermediate Code is an abstract, machine-independent code that lies between the high-
level source code and the machine-level code generated by a compiler. It simplifies the
process of code optimization and enables portability across different machine
architectures, as the intermediate code can be converted to machine code specific to
each target architecture.

Types of Intermediate Code Forms

There are several types of intermediate code forms, each serving different purposes in
simplifying translation or optimization. Here’s a breakdown of common forms with
examples for each:

1. Abstract Syntax Tree (AST)

 Definition: An Abstract Syntax Tree is a tree structure representing the hierarchical


syntactic structure of the source code. Each node corresponds to an operator or
operand, showing the order of operations and the relationships between them.
 Use: Useful for representing the program's structure and performing syntax-based
optimizations.

Example: For the expression a + b * c, the AST would look like:

+
/ \
a *
/ \
b c

Here, * has a higher precedence than +, so it appears deeper in the tree, ensuring the
correct order of operations.
2. Three-Address Code (TAC)

 Definition: Three-Address Code is a sequence of instructions, each with at most


three operands. Each instruction typically consists of one operator and two
operands (e.g., x = y op z). Temporary variables are often introduced to hold
intermediate results.
 Use: TAC is simple and easy to optimize, making it one of the most commonly used
intermediate code forms.

Example: For the expression a + b * c, the TAC would be:

t1 = b * c
t2 = a + t1

Here, t1 and t2 are temporary variables holding intermediate values. This sequence
allows the code to be executed in the correct order and enables straightforward
optimizations like common subexpression elimination.

3. Quadruples

 Definition: Quadruples are a type of intermediate code form where each instruction
is represented as a four-part tuple (operator, arg1, arg2, result). Quadruples
explicitly list the operation and its operands, making them easy to read and
manipulate.
 Use: Useful for tracking temporary results and transformations during optimization.

Example: For the expression a + b * c, the quadruple representation would be:

( *, b, c, t1 )
( +, a, t1, t2 )

Each tuple represents an operation. For instance, ( *, b, c, t1 ) means t1 = b * c.

4. Triples

 Definition: Similar to quadruples, but triples use an index instead of a separate


temporary variable to refer to the results of previous operations. Triples have three
parts: (operator, arg1, arg2).
 Use: Triples save space by omitting explicit names for intermediate values, but they
can be less clear when referring back to prior results.

Example: For the expression a + b * c, the triple representation would be

(0) *, b, c
(1) +, a, (0)

Here, (0) refers to the result of the first operation (b * c), which is then used in the next
operation.

5. Polish Notation (Postfix or Prefix)

 Definition: Polish notation represents expressions without the need for


parentheses, using prefix (operators before operands) or postfix (operators after
operands) notation.
 Use: Reduces ambiguity and simplifies parsing, especially in compiler
implementations.

Example:

o Prefix: + a * b c
o Postfix: a b c * +

For the expression a + b * c, the prefix notation is + a * b c, and postfix notation is a


b c * +.

Summary of Uses and Importance

 AST is helpful for syntax-based optimizations.


 TAC and Quadruples are easy to optimize and widely used for representing
intermediate expressions.
 Triples are space-efficient but less readable.
 Polish Notation simplifies expression evaluation without parentheses.

2. Define Type Checking and Type Conversion. Give one example of each.
Type Checking

 Definition: Type checking is the process by which a compiler or


interpreter verifies that the data types used in expressions, statements,
and functions in a program are valid. This helps prevent type errors,
which occur when operations are applied to incompatible data types
(like adding an integer and a string).
 Purpose: Type checking ensures that the operations in a program make
sense based on the data types involved. By enforcing rules about data
types, type checking prevents potential runtime errors and improves
code reliability and readability.

Types of Type Checking:

Procedure of Type Checking:

1. Static Type Checking:


a. The compiler examines the types of variables and expressions at
compile time.
b. If the types are incompatible (e.g., adding an integer to a string),
an error is flagged before running the program.
c. This is common in strongly-typed languages like Java, C++, and
Rust.

Example:

int a = 5;
string b = "Hello";
a = a + b; // Error: incompatible types

In this example, the compiler detects a type mismatch (int + string) and
produces a compilation error.

2. Dynamic Type Checking:


a. The type of a variable is checked at runtime.
b. If there is an operation between incompatible types, an error will
occur during execution.
c. This is common in dynamically-typed languages like Python,
JavaScript, and Ruby.

Example:

a = 5
b = "Hello"
c = a + b # Runtime error: TypeError: unsupported operand type(s)

The error is caught only when the program is executed.

Type Conversion

 Definition: Type conversion, or type casting, is the process of converting a value


from one data type to another. This can occur either implicitly (automatic
conversion by the compiler) or explicitly (manual conversion by the programmer).
 Purpose: Type conversion allows operations between different types by adjusting
the values to compatible types. This is essential in cases where values of different
types need to be used together (e.g., combining an integer and a floating-point
number).

Types of Type Conversion:

1. Implicit Type Conversion (Type Promotion):


o The compiler automatically converts one data type to another when it is safe
to do so, such as converting an integer to a float in an arithmetic expression.
o This is typically performed from a smaller to a larger data type (e.g., int to
float, char to int).

Example:

int a = 5;
float b = 3.2;
float result = a + b; // Implicit conversion: a is converted to float
before the addition
here, a is automatically promoted to float before the addition.

2. Explicit Type Conversion (Type Casting):


o The programmer manually converts one data type to another using casting
operators or functions.
o It is required when implicit conversion is not available or could result in loss
of data or precision.

Example:

x = 5.7
y = int(x) # Explicit conversion: float to int, results in y = 5

Here, x is explicitly cast from a float to an int, truncating the


decimal part.

3.Write three addresses code for the following segment


a<b or c<d or e>f

Three-Address Code (TAC)

Three-Address Code (TAC) represents statements in the form of instructions with at most
three operands. Each instruction typically has:

 An operator (such as +, -, <, or, etc.)


 Two operands (variables or constants)
 A result (often stored in a temporary variable)

TAC is useful for breaking complex expressions down into simpler, manageable steps,
allowing for better optimization and machine code generation.
Generating TAC for a < b or c < d or e > f

This expression involves logical comparisons (<, >) and logical OR (or) operators. We'll
break it down into intermediate steps using temporary variables to hold intermediate
results.

1. Step 1: Start by evaluating each comparison separately.


2. Step 2: Use temporary variables to store each result.
3. Step 3: Combine the results with logical or operators.

Expression: a < b or c < d or e > f

TAC Instructions:

t1 = a < b // Evaluate if a is less than b, store in t1


t2 = c < d // Evaluate if c is less than d, store in t2
t3 = e > f // Evaluate if e is greater than f, store in t3
t4 = t1 or t2 // Combine t1 and t2 with OR, store in t4
t5 = t4 or t3 // Combine t4 and t3 with OR, store final result in t5

Here’s the breakdown:

 t1 stores the result of a < b.


 t2 stores the result of c < d.
 t3 stores the result of e > f.
 t4 stores the intermediate result of t1 or t2.
 t5 stores the final result of t4 or t3.

Final TAC Output:

t1 = a < b
t2 = c < d
t3 = e > f
t4 = t1 or t2
t5 = t4 or t3

Explanation:

 Each line is a simple three-address instruction with at most


three elements (operator, operands, and result).
 This format is easy to analyze and optimize, allowing for better
understanding and transformation by the compiler.

4. Explain Chomsky Hierarchy of Languages and Recognizers

The Chomsky Hierarchy is a theoretical framework for classifying languages based on


their complexity and the types of grammars that generate them. It was developed by
linguist and cognitive scientist Noam Chomsky and is fundamental to understanding
formal languages, automata, and computation theory. This hierarchy categorizes
languages into four types, each with increasingly restrictive grammar forms and different
types of machines (recognizers) capable of processing them.

Overview of the Chomsky Hierarchy

The Chomsky Hierarchy consists of four types of grammars:

1. Type 0: Unrestricted Grammar (Recursively Enumerable Languages)


2. Type 1: Context-Sensitive Grammar (Context-Sensitive Languages)
3. Type 2: Context-Free Grammar (Context-Free Languages)
4. Type 3: Regular Grammar (Regular Languages)

1. Type 0: Unrestricted Grammar (Recursively Enumerable Languages)

 Definition: Most general form; no restrictions on production rules.


 Recognizers: Turing Machines.
 Example: Any computable problem or language.
 Use: Models complex problems, but not all are decidable.

2. Type 1: Context-Sensitive Grammar (Context-Sensitive Languages)

 Definition: Rules maintain or increase string length (e.g., αAβ→αγβ\alpha A \beta


\rightarrow \alpha \gamma \betaαAβ→αγβ).
 Recognizers: Linear Bounded Automaton (LBA).
 Example: Language {anbncn∣n≥1}\{ a^n b^n c^n \mid n \geq 1 \} {anbncn∣n≥1}
(equal counts of a, b, and c).
 Use: Models structures with contextual dependencies (e.g., some natural
languages).
3. Type 2: Context-Free Grammar (Context-Free Languages)

 Definition: Each rule has a single non-terminal on the left (e.g., A→γA \rightarrow
\gammaA→γ).
 Recognizers: Pushdown Automaton (PDA).
 Example: Balanced parentheses or nested structures.
 Use: Widely used in programming languages and parsers for nested constructs.

4. Type 3: Regular Grammar (Regular Languages)

 Definition: Rules are of the form A→aBA \rightarrow aBA→aB or A→aA \rightarrow
aA→a.
 Recognizers: Finite Automaton (FA).
 Example: Binary strings with an even number of 0s.
 Use: Simple patterns, used in lexical analysis and regular expressions.

5. What is Overloading?

Overloading refers to the ability to define multiple functions or operators with the same
name but with different parameters or behaviors. This allows the programmer to use the
same function or operator name to perform different tasks based on the context, improving
code readability and reducing the need for creating unique names for similar operations.

There are two main types of overloading:

1. Function Overloading: Defining multiple functions with the same name but
different signatures (number or types of arguments).
2. Operator Overloading: Giving custom behavior to operators when they are applied
to user-defined data types (classes/structures).

1. Function Overloading

Definition: Function overloading allows a programmer to define multiple functions with


the same name, but with different parameter lists. The compiler distinguishes between the
overloaded functions based on the number or types of arguments passed to them.

How It Works:

 Functions with the same name are differentiated by their signatures (the number
and types of parameters).
 The function called depends on the number and types of arguments passed during
the function call.

2. Operator Overloading

Definition: Operator overloading allows a programmer to redefine or extend the


functionality of operators (like +, -, *, etc.) to work with user-defined data types (such as
classes or structures). This makes objects of these types behave more like built-in data
types when used with operators.

How It Works:

 You define how an operator should behave when applied to objects of your custom
data types.
 Operator overloading is often used for objects that represent mathematical
concepts, like complex numbers, matrices, or custom strings.
OVERVIEW :
Function Overloading allows defining functions with the same name but different
parameters to perform similar but distinct tasks.
Operator Overloading lets you define custom behavior for operators when working with
objects of user-defined types, making them more intuitive to use with operators like +, -, *,
etc.

You might also like