Professional Documents
Culture Documents
Program: -
def extract_macros(source_code):
macros = []
lines = source_code.split('\n')
index = 0
for line in lines:
if line.strip().startswith("MACRO"):
macro_name = lines[index+1].strip().split()[0]
macros.append((macro_name, index+1))
index += 1
return macros
def display_macro_name_table(macros):
print("Macro Name Table:")
print("Index\tMacro Name\tMDT Index")
for i, (macro_name, index) in enumerate(macros, start=1):
print(f"{i}\t{macro_name}\t\t{index}")
prog.txt
MACRO
INCR1 &ARG1,&ARG2
A 1,&ARG1
L 2,&ARG2
MEND
MACRO
INCR2 &ARG1,&ARG2
M 1,&ARG1
S 2,&ARG2
MEND
MACRO
COMPLEX &X
INCR1 &X,DATA1
INCR2 &X,DATA2
MEND
OUTPUT: -
Source Code:
MACRO
INCR1 &ARG1,&ARG2
A 1,&ARG1
L 2,&ARG2
MEND
MACRO
INCR2 &ARG1,&ARG2
M 1,&ARG1
S 2,&ARG2
MEND
MACRO
COMPLEX &X
INCR1 &X,DATA1
INCR2 &X,DATA2
MEND
1 INCR1 1
2 INCR2 6
3 COMPLEX 11
EXP NO 3
Program: -
return tokens
# Main program
if __name__ == "__main__":
code = """int main() {
int x = 10 ;
float y = 3.14 ;
if ( x == 10 ) {
printf ( " Hello , World ! " ) ;
}
}"""
print("Input Code:")
print(code)
print("\nLexical Analysis:")
tokens = lexical_analyzer(code)
for token in tokens:
print(token[0], "-", token[1])
OUTPUT: -
Input Code:
int main() {
int x = 10 ;
float y = 3.14 ;
if ( x == 10 ) {
printf ( " Hello , World ! " ) ;
}
}
Lexical Analysis:
int - Keyword
main() - Unknown
{ - Delimiter
int - Keyword
x - Identifier
= - Operator
10 - Integer Literal
; - Delimiter
float - Keyword
y - Identifier
= - Operator
3.14 - Float Literal
; - Delimiter
if - Keyword
( - Delimiter
x - Identifier
== - Operator
10 - Integer Literal
) - Delimiter
{ - Delimiter
printf - Identifier
( - Delimiter
" - Unknown
Hello - Identifier
, - Delimiter
World - Identifier
! - Unknown
" - Unknown
) - Delimiter
; - Delimiter
} - Delimiter
} – Delimiter
EXP NO 4
Program: -
def eliminate_left_recursion(grammar):
non_terminals = list(grammar.keys())
for A in non_terminals:
productions_A = grammar[A]
if not left_recursive_productions:
continue
return grammar
# Main program
if __name__ == "__main__":
print("Enter the grammar productions:")
user_grammar = parse_grammar()
gram = {
"E":["2E2","3E3","4"]
}
starting_terminal = "E"
inp="2324232$"
stack = "$"
print(f'{"Stack": <15}'+"/"+f'{"Input Buffer": <15}'+"|"+f'Parsing Action')
print(f'{"-":-<50}')
while True:
action = True
i=0
while i<len(gram[starting_terminal]):
if gram[starting_terminal][i] in stack:
stack = stack.replace(gram[starting_terminal][i],starting_terminal)
print(f'{stack: <15}'+"|"+f'{inp: <15}'+"|"+f'Reduce S-
>{gram[starting_terminal][i]}')
i=-1
action = False
i+=1
if len(inp)>1:
stack+=inp[0]
inp=inp[1:]
print(f'{stack: <15}'+"/"+f'{inp: <15}'+"|"+f'Shift')
action = False
if inp == "$" and stack ==("$"+starting_terminal):
print(f'{stack: <15}'+"/"+f'{inp: <15}'+"/"+f'Accepted')
break
if action:
print(f'{stack: <15}'+"|"+f'{inp: <15}'+"|"+f'Rejected')
break
OUTPUT: -
for i in pos:
if i not in OPERATORS:
exp_stack.append(i)
else:
print(f't{t} := {exp_stack[-2]} {i} {exp_stack[-1]}')
exp_stack=exp_stack[:-2]
exp_stack.append(f't{t}')
t+=1
OUTPUT: -
import re
expr_buffer = []
optm_code = []
with open(r"sample_code_unopt.txt") as source:
code = source.readlines()
source.close()
print("\nInput Expressions: \n")
for line in code:
print(line)
for line in code:
spline = line.replace(" ", "")
if "=" in spline:
expr = (re.findall(r"(\w)+=\S*", spline))
for y in expr_buffer:
for x in y:
if expr[0] == x:
expr_buffer.remove(y)
if "/" in spline:
expr = (re.findall(r"[a-zA-Z]+[\d]*/[a-zA-Z]+[\d]*", spline))
for x in expr:
if x not in expr_buffer:
expr_buffer.append(x)
s = "t"+ str(expr_buffer.index(x))
optm_code.append(s + " = " + x + "\n")
spline = spline.replace(x, s)
else:
s = "t" + str(expr_buffer.index(x))
spline = spline.replace(x, s)
if "*" in spline:
expr = (re.findall(r"[a-zA-Z]+[\d]*\*[a-zA-Z]+[\d]*", spline))
for x in expr:
if x not in expr_buffer:
expr_buffer.append(x)
s = "t"+ str(expr_buffer.index(x))
optm_code.append(s + " = " + x + "\n")
spline = spline.replace(x, s)
else:
s = "t" + str(expr_buffer.index(x))
spline = spline.replace(x, s)
if "+" in spline:
expr = (re.findall(r"[a-zA-Z]+[\d]*\+[a-zA-Z]+[\d]*", spline))
for x in expr:
if x not in expr_buffer:
expr_buffer.append(x)
s = "t"+ str(expr_buffer.index(x))
optm_code.append(s + " = " + x + "\n")
spline = spline.replace(x, s)
else:
s = "t" + str(expr_buffer.index(x))
spline = spline.replace(x, s)
if "-" in spline:
expr = (re.findall(r"[a-zA-Z]+[\d]*-[a-zA-Z]+[\d]*", spline))
for x in expr:
if x not in expr_buffer:
expr_buffer.append(x)
s = "t"+ str(expr_buffer.index(x))
optm_code.append(s + " = " + x + "\n")
spline = spline.replace(x, s)
else:
s = "t" + str(expr_buffer.index(x))
spline = spline.replace(x, s)
optm_code.append(spline)
print("\nOptimized Expressions with computed compiler variable assignments: \n")
for line in optm_code:
print(line)
OUTPUT: -
Input Expressions:
c = a/b * d
a = c * d
f = a/b * c/d + e
g = f * a/b - c/d
t0 = a/b
t1 = t0*d
c=t1
t1 = c*d
a=t1
t2 = a/b
t3 = c/d
t4 = t2*t3
t5 = t4+e
f=t5
t6 = f*t2
t7 = t6-t3
g=t7
EXP NO 8
Program: -
import re
f = open("IntermediateCode.txt", 'r')
lines = f.readlines()
f.close()
fifo_return_reg = 'R0'
reg = [0] * 13
var = {}
store_seq = []
fifo_reg = 0
operator_list = {'+': 'ADD', '-': 'SUB', '*': 'MUL', '/': 'DIV', '=': 'MOV', '==': 'NE',
'>': 'G', '>=': 'GE', '<': 'L', '<=': 'LE', 'and': 'AND', 'or': 'OR'}
def fifo():
global fifo_reg
global fifo_return_reg
for k, v in var.copy().items():
if v == 'R' + str(fifo_reg):
fifo_return_reg = v
var.pop(k)
if k in store_seq:
store_seq.remove(k)
print("ST", k, ',', v, sep='')
fifo_reg = int(fifo_return_reg[1:]) + 1
return fifo_return_reg
def getreg():
for i in range(0, 13):
if reg[i] == 0:
reg[i] = 1
return 'R' + str(i)
register = fifo()
return register
line = line.split()
length = len(line)
if length == 0:
continue
if length == 1:
print(line[0])
continue
if re.match(r'^t[0-9]+$', line[0]):
continue
if length == 3:
lhs = line[0]
operand = line[2]
if operand not in var:
var[operand] = getreg()
if operand.isalpha():
print("LD", var[operand], ', ', operand, sep="")
else:
print("MOV", var[operand], ', #', operand, sep="")
if lhs in store_seq:
old_reg = var[lhs]
store_seq.remove(lhs)
print("ST", lhs, ',', old_reg, sep='')
var[lhs] = var[operand]
store_seq.append(lhs)
else:
if len(line) >= 5:
oper = line[3]
operand1 = line[2]
operand2 = line[4]
lhs = line[0]
if operand1 not in var:
var[operand1] = getreg()
if operand1.isalpha():
print("LD", var[operand1], ', ', operand1, sep="")
else:
print("MOV", var[operand1], ', #', operand1, sep="")
if operand2 not in var:
var[operand2] = getreg()
if operand2.isalpha():
print("LD", var[operand2], ', ', operand2, sep="")
else:
print("MOV", var[operand2], ', #', operand2, sep="")
operator_print = operator_list.get(oper)
if operator_print:
if lhs in store_seq:
old_reg = var[lhs]
store_seq.remove(lhs)
print("ST", lhs, ',', old_reg, sep='')
var[lhs] = getreg()
store_seq.append(lhs)
print(operator_print, var[lhs], ',', var[operand1], ',', var[operand2],
sep=' ')
else:
operand = line[3]
lhs = line[0]
if operand not in var:
var[operand] = getreg()
if operand.isalpha():
print("LD", var[operand], ', ', operand, sep="")
else:
print("MOV", var[operand], ', #', operand, sep="")
if lhs in store_seq:
old_reg = var[lhs]
store_seq.remove(lhs)
print("ST", lhs, ',', old_reg, sep='')
var[lhs] = getreg()
store_seq.append(lhs)
print("NOT", var[lhs], ',', var[operand], sep='')
for i in store_seq:
print("ST", i, ',', var[i], sep='')
IntermediateCode.txt
a = 10
b = 9
t0= 10 + 9
tl = 19 + 100
c = 119
e = 10
f = 8
t2 = 10 * 8
d = 80
|0:
t3 = 10 >= 9
t4 = not 1
if 0 goto |1
a = 19
15 = 80 * 100
g = 8000
|1:
u = 10
j = 99
OUTPUT: -
MOVR0, #10
MOVR1, #9
NOTR2,R1
MOVR3, #19
MOVR4, #100
ADD R5 , R3 , R4
MOVR6, #119
MOVR7, #8
MOVR8, #80
|0:
MOVR9, #0
BNEZ R9 , |1
STa,R0
MUL R10 , R8 , R4
MOVR11, #8000
|1:
MOVR12, #99
STb,R1
STt0=,R2
STtl,R5
STc,R6
STe,R0
STf,R7
STd,R8
STa,R3
ST15,R10
STg,R11
STu,R0
STj,R
ASSIGNMENT 3
Q. 1 "What are the different types of system programs, and how do they contribute to the efficient
operation and management of computer systems?"
System programs are essential software components that facilitate the efficient operation and
management of computer systems. They perform various tasks ranging from controlling hardware devices
to managing system resources and providing essential utilities. Here are some different types of system
programs and their contributions to the efficient operation and management of computer systems:
1. Operating System (OS):
The operating system is the core system software that manages computer hardware resources and
provides essential services to user applications.
It includes components such as the kernel, which interacts directly with hardware, and system services
such as process management, memory management, file system management, and input/output (I/O)
management.
The OS ensures efficient allocation and utilization of system resources, facilitates multitasking and
multiprocessing, and provides a platform for running applications.
2. Device Drivers:
Device drivers are software components that enable communication between the operating system and
hardware devices such as printers, network adapters, graphics cards, and storage devices.
They provide an interface for the OS to control and manage hardware devices, including handling device
initialization, data transfer, and error handling.
Device drivers ensure compatibility between hardware devices and the operating system, allowing users
to access and utilize peripheral devices effectively.
3. System Utilities:
System utilities are software tools provided by the operating system or third-party vendors to perform
various system management tasks.
Examples include disk utilities for managing storage devices (e.g., disk formatting, partitioning, and
defragmentation), network utilities for configuring network settings and troubleshooting connectivity
issues, and performance monitoring tools for analyzing system performance metrics (e.g., CPU usage,
memory usage, disk activity).
System utilities help users and administrators manage system resources, diagnose and troubleshoot
problems, and optimize system performance.
4. File Management Programs:
File management programs facilitate the creation, organization, modification, and deletion of files and
directories on storage devices.
They provide features such as file browsing, file copying and moving, file searching, file compression,
and file permission management.
File management programs ensure efficient storage and retrieval of data, maintain file system integrity,
and enforce security policies related to file access and permissions.
5. Security Software:
Q.2 "What are some key data structures used in the design of an assembler, and how do they
contribute to the efficient translation of assembly language code into machine code?"
In the design of an assembler, several key data structures play essential roles in efficiently translating
assembly language code into machine code. Here are some key data structures commonly used in
assembler design and their contributions to the translation process:
1. Symbol Table:
A symbol table is a data structure used to store information about symbols (labels, variables, and
constants) defined in the assembly code, along with their associated memory addresses or values.
Symbol tables help in resolving symbolic references and associating memory addresses with symbols
during the assembly process.
They facilitate efficient translation by providing quick access to symbol information and supporting
symbol resolution and memory allocation.
2. Parsing Trees:
Parsing trees (or syntax trees) are hierarchical data structures used to represent the syntactic structure of
assembly language instructions.
During the parsing phase of the assembly process, parsing trees are constructed by parsing and analyzing
the assembly code.
Parsing trees facilitate semantic analysis and code generation by organizing assembly language
instructions into a structured format, making it easier to analyze and manipulate them during translation.
3. Opcode Table:
An opcode table is a data structure that maps mnemonic instructions to their corresponding binary
machine code representations.
Opcode tables contain entries for each assembly language instruction, along with their opcode values and
associated instruction formats.
They enable the assembler to look up opcode information quickly and efficiently translate mnemonic
instructions into their binary equivalents during code generation.
4. Instruction Queue:
An instruction queue is a data structure used to buffer assembly language instructions during the
translation process.
As assembly code is parsed and analyzed, instructions are placed in the instruction queue for further
processing.
Instruction queues help in organizing instructions for sequential translation, ensuring proper ordering and
handling of dependencies between instructions.
5. Symbol Resolution Table:
A symbol resolution table is a data structure used to track unresolved symbols and their references in the
assembly code.
During the assembly process, unresolved symbols are recorded in the symbol resolution table until they
are resolved by associating them with memory addresses or values from the symbol table.
Symbol resolution tables help in detecting and handling unresolved symbols, ensuring that all symbols
are resolved before generating machine code.
These key data structures contribute to the efficient translation of assembly language code into machine
code by organizing and managing symbol information, representing the syntactic structure of instructions,
mapping mnemonics to opcode values, buffering instructions for translation, and resolving symbolic
references. By leveraging these data structures, assemblers can perform translation tasks accurately,
quickly, and reliably, resulting in optimized machine code generation and enhanced overall performance.
Q.3 Write a assembly language program for finding a factorial of a number using macro
Explanation:
● The macro CALC_FACTORIAL takes two arguments: the address where the result will be
stored (%1), and the number whose factorial needs to be calculated (%2).
● Within the macro, it initializes ecx with the value of the second argument (%2) and initializes
%1 with 1.
● It then enters a loop where it multiplies %1 by the current value of ecx (the counter) and
decrements ecx until it becomes zero.
● The main program first initializes the number whose factorial needs to be calculated (number),
and a variable to store the result (result).
● It then calls the CALC_FACTORIAL macro, passing the address of result and the value stored
in number.
Finally, it displays the result and exits the program.
This program calculates the factorial of the number 5 (hardcoded) and displays the result. You can modify
the number variable to calculate the factorial of any other number.
Q.4 What are the various loaders and linkers schemes used in software development, and how do they
contribute to the efficient development of user applications?
Various loaders and linkers schemes are employed in software development to facilitate the efficient
execution of user applications. These schemes include static linking, dynamic linking, and various
relocation techniques. Here's an overview of each and their contributions to the efficient development of
user applications:
1. Static Linking:
● Definition: Static linking involves combining all necessary library code and application code into
a single executable file before the program is run. This means that all library functions are
resolved and linked at compile time.
● Performance: Static linking can improve the performance of the application by reducing the
overhead associated with dynamic loading and linking during runtime.
● Portability: Static linking ensures that all necessary dependencies are included within the
executable, making the application more portable as it can be run without relying on external
libraries.
● Simplicity: Static linking simplifies deployment and distribution, as it eliminates the need to
manage and distribute separate library files along with the executable.
● Security: Static linking can enhance security by reducing the risk of dependency-related
vulnerabilities, as the application is self-contained and does not rely on external resources.
2. Dynamic Linking:
● Definition: Dynamic linking involves linking libraries at runtime, where the necessary library
functions are loaded into memory only when they are needed by the application.
● Memory Efficiency: Dynamic linking reduces memory overhead by allowing multiple
applications to share the same copy of a library loaded into memory.
● Flexibility: Dynamic linking provides flexibility in updating and managing libraries, as changes
to shared libraries can be applied without recompiling or relinking the application.
● Resource Utilization: Dynamic linking optimizes resource utilization by loading libraries into
memory on demand, reducing startup time and memory footprint.
● Version Management: Dynamic linking facilitates version management, as different versions of
a library can coexist on the system, and applications can dynamically link to the appropriate
version at runtime.
3. Relocation Techniques:
Definition: Relocation techniques are used by loaders to adjust the memory addresses of program and
library code during the loading process, ensuring that the code can execute correctly regardless of its
location in memory.
● Memory Management: Relocation techniques enable efficient memory allocation and address
resolution, allowing programs to utilize available memory resources optimally.
● Address Space Layout Randomization (ASLR): ASLR is a relocation technique that enhances
security by randomizing the memory addresses of executable code and libraries, making it
difficult for attackers to predict the location of critical system components.
● Position-Independent Code (PIC): PIC is a compilation technique used to generate code that
can be relocated to any memory address, enhancing compatibility and flexibility in dynamic
linking environments.
Overall, loaders and linkers schemes such as static linking, dynamic linking, and relocation techniques
contribute to the efficient development of user applications by optimizing performance, memory
utilization, portability, security, and flexibility. Understanding these schemes is essential for software
developers to make informed decisions regarding application design, deployment, and maintenance.
Q.5 Construct a CLR parsing table for following Grammar
S-->AA
A-->aA|b
Expression 3 : d = T 0 + T1
Example : T1 = a + b T2 = T1 + c T3 = T1 x T2