CD Lab Manual 2024 Ctech
CD Lab Manual 2024 Ctech
School of Computing
Department of Computing Technologies
Prepared by
Dr. K. Vijaya
Dr. D. Vinod
Dr. K. Anitha
SRM Institute of Science and Technology
College of Engineering and Technology
School of Computing
Department of Computing Technologies
B.Tech – Computer Science and Engineering
Regulations 2018
Department Vision
• To become a world-class department in imparting high-quality knowledge and providing
students with a unique learning and research experience in Computer Science and
Engineering.
Department Mission
• To impart knowledge in cutting edge technologies in part with industrial standards.
S. Topic(s) CO PO
No.
1 Write a simple calculator program in C/C++/JAVA 1 1
3
Ex. 1: Write a simple calculator program in C/C++/JAVA
4
Procedure for Balancing Symbols:
1. Traverse the expression from left to right.
a. If the current character is a starting bracket, then push it to stack
b. If the current character is a closing bracket, then pop from stack and if the popped character
is not matching starting bracket, return “not balanced”.
2. If there is some starting bracket left in stack then return “not balanced”
3. Return balanced
5
Ex. 2: Write a program Using FLEX
Flex is a scanner generator tool for lexical analysis, which is based on finite state
machine (FSM). The input is a set of regular expressions, and the output is the code to
implement the scanner according to the input rules.
To implement a scanner for calculator, we can write the file “cal1.l” as below:
/* this is only for scanner, not link with parser yet */
%{
int lineNum = 0;
%}
%%
"(" { printf("(\n"); }
")" { printf(")\n"); }
"+" { printf("+\n"); }
"*" { printf("*\n"); }
\n { lineNum++; }
[ \t]+ { }
[0-9]+ { printf("%s\n", yytext); }
%%
int yywrap() {
return 1;
}
int main () {
yylex();
return 0;
}
6
%start list
%union { int a; }
7
Ex. 3 : Implementation of scanner by specifying Regular Expressions
Procedure:
1. Place the following files in a folder minijava.html, P1.tab.h, P1.l and a sample
input file
(Factorial.java).
2. flex P1.l
3. gcc lex.yy.c -o P1
4. P1
5. Give = if as input
Output: = number (number is symbol table id of =)
if number (number is symbol table id of if)
6. Fill in the code that you have been asked to include in P1.l
7. flex P1.l
8. gcc lex.yy.c -o P1
9. P1 < Factorial.java
10. All tokens generated by the parser will be placed in the output
9
ArrayAllocationExpression ::= "new" "int" "[" Expression "]"
AllocationExpression ::= "new" Identifier "(" ")"
NotExpression ::= "!" Expression
BracketExpression ::= "(" Expression ")"
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
/* Put the tokens into the symbol table, so that GDB and other debuggers
know about them. */
enum yytokentype {
HASHDEFINE = 258,
NOT = 259,
CURLY_OPEN = 260,
CURLY_CLOSE = 261,
PAR_OPEN = 262,
PAR_CLOSE = 263,
10
SQR_CLOSE = 264,
IF = 265,
WHILE = 266,
CLASS = 267,
PUBLIC = 268,
STATIC = 269,
VOID = 270,
MAIN = 271,
STR = 272,
PRINTLN = 273,
EXTENDS = 274,
THIS = 275,
NEW = 276,
SEMI_COLON = 277,
COMMA = 278,
LENGTH = 279,
TRUE = 280,
FALSE = 281,
NUMBER = 282,
RET = 283,
BOOL = 285,
INT = 286,
IDENTIFIER = 287,
ADD = 288,
SUB = 289,
MUL = 290,
DIV = 291,
MOD = 292,
BIT_AND = 293,
LESSTHAN = 294,
SQR_OPEN = 295,
DOT = 296,
ASSIGNMENT = 297,
ELSE = 298,
lab1 = 299,
newlabel = 300,
label = 301
};
#endif
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
{
/* Line 2068 of yacc.c */
#line 25 "P1.y"
11
char* str;
/* Line 2068 of yacc.c */
#line 102 "P1.tab.h"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
#endif
extern YYSTYPE yylval;
Template of the lex (P1.l) file with code for one operator and one keyword.
%{
#include "P1.tab.h"
#include<stdio.h>
#include<stdlib.h>
%}
%%
/* Write code to ignore empty spaces and newlines. */
/* Write code to ignore comments (single line and multiline). */
/* Write code to scan all the operators, paranthesis etc. Example shown for
assignment. */
"=" {char* str=yytext;printf ("%s %d\n", str, ASSIGNMENT);}
/* Write code to scan all the keywords. Example shown for if */
"if" {char* str=yytext;printf ("%s %d\n", str, IF);}
13
Ex. 4: Write a program using BISON.Simple calculator program in bison
- The calculator should exit, when the user enters the Ctrl^D (eof) character.
Procedure:
1. Define Lexical Analysis Rules: Write lexical analysis rules using regular expressions to identify
tokens like identifiers, numbers, operators, etc. Use the flex tool to generate the corresponding lexer
code.
2. Define Syntax Rules: Write Bison grammar rules specifying the syntax of your language.
Bison uses BNF-like notation for specifying grammar rules. These rules define how
different parts of the language can be combined to form valid programs.
3. Handle Tokenization: In your Bison grammar file, specify how tokens generated by the
lexer should be handled. This involves associating token types with grammar symbols and
actions to take when certain combinations of tokens are recognized.
4. Implement Semantic Actions: Define semantic actions associated with grammar rules to
specify what should happen when a certain rule is recognized during parsing. Semantic
actions typically involve constructing abstract syntax trees (ASTs) or executing code.
5. Error Handling: Implement error handling mechanisms to detect and report syntax errors
during parsing. Bison provides facilities for error recovery and reporting.
6. Generate Parser Code: Use the bison tool to generate C code for the parser based on your
Bison grammar file.
7. Integrate Lexer and Parser: Integrate the lexer and parser code into a single program. The
lexer provides tokens to the parser, which uses the Bison-generated parser code to parse the
input according to the grammar rules.
8. Test the Parser: Test the parser with various inputs to ensure that it correctly recognizes
valid programs and detects syntax errors in invalid ones.
Bison program for a calculator:
15
{
$$.a = $1.a / $3.a;
}
|
expr '%' expr
{
$$.a = $1.a % $3.a;
}
|
expr '+' expr
{
$$.a = $1.a + $3.a;
}
|
expr '-' expr
{
$$.a = $1.a - $3.a;
}
|
expr '&' expr
{
$$.a = $1.a & $3.a;
}
|
expr '|' expr
{
$$.a = $1.a | $3.a;
}
|
'-' expr %prec UMINUS
{
$$.a = -$2.a;
}
|
LETTER
{
$$.a = regs[$1.a];
}
|
number
;
number: DIGIT
{
16
$$ = $1;
base = ($1.a==0) ? 8 : 10;
}|
number DIGIT
{
$$.a = base * $1.a + $2.a;
}
;
%%
main()
{
return(yyparse());
}
yyerror(s)
char *s;
{
fprintf(stderr, "%s\n",s);
}
yywrap()
{
return(1);
}
LEX FILE
#include <stdio.h>
#include "y.tab.h"
int c;
%}
%%
" " ;
[a-z] {
c = yytext[0];
yylval.a = c - 'a';
return(LETTER);
}
[0-9] {
c = yytext[0];
yylval.a = c - '0';
return(DIGIT);
}
[^a-z0-9\b] {
c = yytext[0];
return(c);
17
}
%%
OUTPUT:
5+4
9
18
Ex. 5. Write a program for Top-Down Parsing - predictive parsing table (Removalof Left
recursion/Left factoring and Compute FIRST & FOLLOW)
Procedure
Input: Grammar G
Output: Parsing table
2. Compute FIRST sets for each non-terminal and terminal symbol in the
grammarG.
a. Initialize FIRST set for each terminal as itself.
b. For each non-terminal A in G, initialize FIRST(A) as an empty set.
c. Repeat until no changes in FIRST sets:
i. For each production A -> α, do the following:
- If α is terminal or ε, add α to FIRST(A).
- If α is non-terminal, add all symbols from FIRST(α) to FIRST(A),
except ε.
- If ε is in FIRST(α), continue to the next symbol.
19
4. Construct the parsing table.
a. Initialize parsing table M with empty entries.
b. For each production A -> α in G, do the following:
i. For each terminal a in FIRST(α), add A -> α to M[A, a].
- If α derives ε, add A -> α to M[A, b] for each terminal b in FOLLOW(A).
ii. If ε is in FIRST(α), for each terminal b in FOLLOW(A),
add A -> α to M[A, b].
Example:
Remove left recursion from the grammar G
return newGrammar;
}
Parse table creation
public class TopDownParser {
public static void main(String[] args) {
// Example grammar: S -> A, A -> aA | ε
Map<String, Set<String>> firstSets = new HashMap<>();
Map<String, Set<String>> followSets = new HashMap<>();
firstSets.get("S").add("a");
firstSets.get("A").add("a");
firstSets.get("A").add("ε");
21
// Construct FOLLOW sets
followSets.put("S", new HashSet<>());
followSets.put("A", new HashSet<>());
followSets.get("S").add("$");
followSets.get("A").add("$");
followSets.get("A").add("a");
parseTable.get("S").put("a", "A");
parseTable.get("A").put("a", "aA");
parseTable.get("A").put("$", "ε");
22
EXPRESSION GRAMMAR
E->E+T
E->T
T->T*F
T->F
F->( E)
F->id
OUTPUT
PARSE TABLE
23
Ex. 6: Write a program for Bottom-Up Parsing - SLR Parsing
Procedure:
• SLR is simple LR. It is the smallest class of grammar having few number of states.
SLR is very easy to construct and is similar to LR parsing.
• The only difference between SLR parser and LR(0) parser is that in LR(0) parsing
table, there’s a chance of ‘shift reduced’ conflict because we are entering ‘reduce’
corresponding to all terminal states.
• We can solve this problem by entering ‘reduce’ corresponding to FOLLOW of LHS
of production in the terminating state. This is called SLR(1) collection of items
Sample Code
import java.util.*;
class SLRParser {
private final List<String> grammar;
private final Map<Integer, Map<String, String>> actionTable;
private final Map<Integer, Map<String, Integer>> goToTable;
private final Stack<Integer> stateStack;
private final Stack<String> symbolStack;
private final List<String> input;
24
String currentSymbol = input.get(inputIndex);
if (actionTable.containsKey(currentState) &&
actionTable.get(currentState).containsKey(currentSymbol)) {
String action = actionTable.get(currentState).get(currentSymbol);
if (action.startsWith("s")) {
int nextState = Integer.parseInt(action.substring(1));
stateStack.push(nextState);
symbolStack.push(currentSymbol);
inputIndex++;
} else if (action.startsWith("r")) {
int productionIndex = Integer.parseInt(action.substring(1));
String production = grammar.get(productionIndex);
String[] parts = production.split("->");
String leftPart = parts[0].trim();
String rightPart = parts[1].trim();
int len = rightPart.split(" ").length;
for (int i = 0; i < len; i++) {
stateStack.pop();
symbolStack.pop();
}
currentState = stateStack.peek();
String nonTerminal = leftPart;
stateStack.push(goToTable.get(currentState).get(nonTerminal));
symbolStack.push(nonTerminal);
} else if (action.equals("accept")) {
return true;
}
} else {
// No valid action in action table, parsing fails
return false;
}
}
OUTPUT:
Enter input word: id*id
26
Ex. 7: Introduction to basic Java - Programs in java
Introduction to JAVA-
• JAVA was developed by James Gosling at Sun Microsystems Inc in the year 1995 and
later acquired by Oracle Corporation.
• It is a simple programming language. Java makes writing, compiling, and debugging
programming easy.
• It helps to create reusable code and modular programs. Java is a class-based, object-
oriented programming language and is designed to have as few implementation
dependencies as possible.
• A general-purpose programming language made for developers to Write Once Run
Anywhere (WORA) that is compiled Java code can run on all platforms that support
Java.
• Java applications are compiled to byte code that can run on any Java Virtual Machine.
The syntax of Java is similar to c/c++.
• Java is known for its simplicity, robustness, and security features, making it a popular
choice for enterprise-level applications.
27
• This will compile your code. If there are no errors in the code, the command prompt will
take you to the next line. Now, type "java Main" to run the file:
C:\Users\Your Name>java Main
• The output should read:
Hello World
Sample Code
import java.util.Scanner;
public class SimpleJavaProgram {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
28
Ex. 8: Write a program to traverse syntax trees and perform action arithmetic
operations
Procedure:
1. Define a recursive function Traverse(node), where node is a node in the syntax tree:
a. If node is a leaf node containing a number:
i. Return the value of the number.
b. If node is an interior node representing an operator:
i. Let leftValue = Traverse(leftChild), where leftChild is the left child of node.
ii. Let rightValue = Traverse(rightChild), where rightChild is the right child of node.
iii. Perform the arithmetic operation indicated by the operator stored in node, using
leftValue and rightValue.
iv. Return the result of the operation.
2. Call Traverse(root), where root is the root node of the syntax tree.
Sample Code
class TreeNode {
String value;
TreeNode left;
TreeNode right;
TreeNode(String value) {
this.value = value;
this.left = null;
this.right = null;
}
}
29
if (node.left == null && node.right == null) {
return Integer.parseInt(node.value);
}
switch (node.value) {
case "+":
return leftValue + rightValue;
case "-":
return leftValue - rightValue;
case "*":
return leftValue * rightValue;
case "/":
if (rightValue == 0) {
throw new ArithmeticException("Division by zero");
}
return leftValue / rightValue;
default:
throw new IllegalArgumentException("Invalid operator: " + node.value);
}
}
Implemented a method traverse and calculate to recursively traverse the syntax tree and
perform arithmetic operations. In the main method, created an example syntax tree
representing the expression 3 + (5 * 2), reverse the tree, and print the result.
30
Ex. 9: Write an Intermediate code generation for If/While
Procedure
• Create a method takes two strings as input: condition represents the condition of the if
statement, and action represents the action to be performed if the condition is true.
• Inside the generate if Code method, a StringBuilder is used to construct the code.
• The condition and action strings are concatenated to form the if statement block.
• Finally, the generated code is returned as a string.
Can customize this code by providing different conditions and actions to generate code for
different if statements. Additionally, can extend this algorithm to handle more complex if- else
or nested if statements as needed.
Sample Code
import java.util.*;
31
codeBuilder.append("if (").append(condition).append(") {\n");
OUTPUT:
Generated code:
if (x > 5) {
y = x * 2;
}
32
Ex. 10: Code Generation Introduction to MIPS Assembly language
Introduction:
• MIPS (Million Instructions Per Second) is a RISC (Reduced Instruction Set
Computer) instruction set architecture developed by MIPS Technologies in the late
1980s.
• It is a loadstore architecture, meaning that data must be loaded into registers before
it can be processed. MIPS is known for its simplicity, efficiency, and high
performance.
• It has been widely used in embedded systems, networking equipment, andother
applications where performance and power consumption are critical.
Procedure:
Step 1: get value for register $a0
Step 2: get value for register $a1
Step 3: add values in $a0 and $a1
Step 4: store result in register $v0
Step 5: print value in register $v0
Sample Code:
.data
num1: .word 10
num2: .word 5
.text
main:
# load num1 into $a0
lw $a0, num1
# load num2 into $a1
lw $a1, num2
# add num1 and num2
add $v0, $a0, $a1
# print result
li $v0, 1 # syscall code for print_int
syscall
# exit program
li $v0, 10 # syscall code for exit
syscall
Output: 15
33
How To Run:
• Open MARS MIPS Simulator.
• Click "File" -> "Open" and select your MIPS assembly file.
• Click "Assemble" to assemble the file.
• Click "Run" to execute the program.
34
Ex. 11: Write a program to generate machine code for a simple statement
To implement a java program to convert an assembly language statement to machine
Procedure
Sample Code:
import java.io.FileOutputStream;
import java.io.IOException;
public class GenerateMachineCode {
public static void main(String[] args) throws IOException {
// Create a byte array to store the machine code
byte[] machineCode = new byte[4];
// Set the opcode for the MIPS instruction in the byte array
machineCode[0] = (byte) 0x0;
// Set the source register for the first operand in the byte array
machineCode[1] = (byte) 0x0;
// Set the source register for the second operand in the byte array
machineCode[2] = (byte) 0x80;
// Set the destination register in the byte array
machineCode[3] = (byte) 0x21;
// Write the machine code to a file
try (FileOutputStream fos = new FileOutputStream(“machine_code.bin”)){
fos.write(machineCode);
}
catch (IOException e) {
e.printStackTrace();
}
}
}
35
Explanation:
The machine code instructions for MIPS are 32 bits long. Each instruction is divided into
fields, which specify the opcode, source registers, destination register, and function code.
• The opcode for the add instruction is 000000. This is stored in the first byte of the
machine code instruction, which is machineCode[0].
• The source registers for the add instruction are $a0 and $a1. These are stored in the
second a nd third bytes of the machine code instruction, which are machineCode[1] and
machineCode[2].
• The destination register for the add instruction is $v0. This is stored in the fourth byte
of themachine code instruction, which is machineCode[3].
• The function code for the add instruction is 000001. This is also stored in the fourth byte
of the machine code instruction, which is machineCode[3].
• Therefore, the machine code instruction 000000000000000010000001 translates to the
MIPS assembly instruction add $v0, $a0, $a1.
How to run:
javac GenerateMachineCode.java
java GenerateMachineCode
36
Ex. 12: Write a program to generate machine code for an indexed assignment statement
Procedure:
Sample Code:
import java.io.FileOutputStream;
import java.io.IOException;
public class GenerateMachineCodeIndexedAssignment {
public static void main(String[] args) throws IOException {
// Create a byte array to store the machine code
byte[] machineCode = new byte[6];
// Set the opcode for the MIPS instruction in the byte array
machineCode[0] = (byte) 0x2b;
// Set the source register in the byte array
machineCode[1] = (byte) 0x04;
// Set the base register in the byte array
machineCode[2] = (byte) 0x00;
// Set the offset in the byte array
machineCode[3] = (byte) 0x00;
machineCode[4] = (byte) 0x00;
machineCode[5] = (byte) 0x18;
// Write the machine code to a file
try (FileOutputStream fos = new FileOutputStream("machine_code.bin")) {
fos.write(machineCode);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explanation:
The first 6 bits (101011) are the opcode for the sw instruction.
The next 5 bits (00000) are the source register ($v0).
The next 5 bits (01000) are the base register ($a0).
The next 16 bits (0000000000000100) are the offset (4).
Assembly Statement: sw $v0, 4($a0)
Machine Statement: 10101101000001000000000000011000
37