0% found this document useful (0 votes)
16 views54 pages

CC Lab

The document is a practical file for the Compiler Construction course (CSE304) at Amity University, detailing various experiments related to compiler design. It includes aims, theories, and source code for tasks such as converting infix expressions to postfix, constructing finite automata, counting tokens, and implementing parsing techniques. The practical file is submitted by a student and contains multiple programming exercises that demonstrate key concepts in compiler construction.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views54 pages

CC Lab

The document is a practical file for the Compiler Construction course (CSE304) at Amity University, detailing various experiments related to compiler design. It includes aims, theories, and source code for tasks such as converting infix expressions to postfix, constructing finite automata, counting tokens, and implementing parsing techniques. The practical file is submitted by a student and contains multiple programming exercises that demonstrate key concepts in compiler construction.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 54

COMPILER CONSTRUCTION

CSE304
Practical file

AMITY SCHOOL OF ENGINEERING AND TECHNOLOGY


AMITY UNIVERSITY, UTTAR PRADESH

Submitted by:
Krish Dogra
A2305222573
6CSE-9X

Submitted to:
Dr Roshan Lal
INDEX

S.no Program Date Sign


.
1. To convert a given infix expression
to postfix and evaluate that postfix e

2. To construct a Deterministic Finite


Automata for Regular Expression.
3. To count the number of tokens in a
program.

4. To count the number and type of


tokens in a program.
5. To remove the Left Recursion from
a given grammar.
6. To remove ambiguity from a
grammar.
7. To construct predictive parser.

8. To compute First and Follow for a


grammar.
9. To compute Leading and Trailing
for a grammar.
10. To design a Recursive Descent
Parser.

Experiment 1
Aim: Write a program to identify keywords, constants, special characters and
identifiers from a given input string.
Language Used: C
Theory: Lexical analysis is a fundamental part of compiler design, where a
given input string is processed to identify meaningful components called
tokens. Tokens include keywords, constants, special characters, and identifiers,
each playing a crucial role in programming languages.
Keywords are predefined reserved words such as int, return, and if, which
have specific meanings in the C programming language. Constants refer to
fixed numeric values like 100 or 3.14, which do not change during execution.
Identifiers are user-defined names for variables, functions, and arrays,
following the language's naming conventions. Special characters include
symbols like +, -, *, {, }, which define operations and control structures in the
code.
The program reads an input string, processes each character, and groups them
into tokens based on predefined rules. It scans the string, checks whether a
sequence of characters matches a keyword, a constant, or an identifier, and
detects special symbols. This process ensures accurate token classification,
which is crucial for syntax analysis in a compiler.
Lexical analysis is essential for code parsing, debugging, and compilation, as
it helps detect syntax errors early. By systematically breaking the input into
tokens, the program enhances readability and assists in further stages of code
execution, such as semantic analysis and optimization.

Code:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>
const char *keywords[] = {"int", "float", "char", "double", "return", "if",
"else", "for", "while", "do", "switch", "case", "break", "continue", "void",
"static", "struct", "typedef", "const", "sizeof", "volatile", "enum", "union",
"default", "extern", "goto", "register", "short", "signed", "unsigned", "long",
"auto", "inline", "restrict", "_Alignas", "_Alignof", "_Atomic", "_Bool",
"_Complex", "_Generic", "_Imaginary", "_Noreturn", "_Static_assert",
"_Thread_local"};
int keyword_count = sizeof(keywords) / sizeof(keywords[0]);

bool isKeyword(char *word) {


for (int i = 0; i < keyword_count; i++) {
if (strcmp(word, keywords[i]) == 0) {
return true; }}
return false;
}
bool isNumber(char *word) {
for (int i = 0; word[i] != '\0'; i++) {
if (!isdigit(word[i])) {
return false; } } return true; }
bool isSpecialSymbol(char ch) {
char special_symbols[] = "!@#$%^&*()-+=|<>?/{}[]:;.,'\\";
for (int i = 0; special_symbols[i] != '\0'; i++) {
if (ch == special_symbols[i]) {
return true;
}} return false;
}
void identifyTokens(char *str) {
char token[50]; int index = 0;
for (int i = 0; str[i] != '\0'; i++) {
if (isalnum(str[i])) {
token[index++] = str[i];
} else {
if (index > 0) {
token[index] = '\0';
if (isKeyword(token)) { printf("Keyword: %s\n", token); }
else if (isNumber(token)) { printf("Constant: %s\n", token); }
else { printf("Identifier: %s\n", token); }
index = 0; }
if (isSpecialSymbol(str[i])) {
printf("Special Character: %c\n", str[i]); } } }
}
int main() {
char input[100];
printf("Enter a C statement: "); fgets(input, sizeof(input), stdin);
identifyTokens(input);
return 0;
}
Output:
Experiment 2
Aim: Write a program to count the total number of tokens in the source code.
Language Used: C
Theory: A token is the smallest meaningful unit in a programming language,
including keywords, identifiers, operators, constants, and special symbols.
Tokenization is the process of breaking source code into tokens, which is a
fundamental step in lexical analysis for compilers. This program reads a C
source code file, extracts tokens using strtok(), and counts them. It identifies
tokens by splitting the text based on predefined delimiters such as whitespace,
punctuation, and operators. Tokenization is widely used in compiler design,
syntax highlighting, and static code analysis. By implementing this process,
we can better understand how source code is structured and interpreted.
Code:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_LENGTH 100

int isSpecialCharacter(char ch) {


char specialChars[] = "(){}[];,=+-*/<>!&|\"'";
for (int i = 0; specialChars[i] != '\0'; i++) {
if (ch == specialChars[i]) {
return 1; }}
return 0;
}
int main() {
char sourceCode[MAX_LENGTH];
printf("Enter the source code: \n");
fgets(sourceCode, MAX_LENGTH, stdin);
int tokenCount = 0;
char token[MAX_LENGTH];
int index = 0;

for (int i = 0; sourceCode[i] != '\0'; i++) {


if (isalnum(sourceCode[i]) || sourceCode[i] == '_') {
token[index++] = sourceCode[i];
} else {
if (index > 0) {
token[index] = '\0';
tokenCount++;
index = 0;
}
if (isSpecialCharacter(sourceCode[i])) {
tokenCount++; } } }
if (index > 0) {
tokenCount++;
}
printf("Total number of tokens: %d\n", tokenCount);
return 0;
}
Output:
Experiment 3
Aim: Write a program in C to check whether given string belongs to given
grammar or not
Language Used: C language
Theory: This C program validates whether a given string follows the grammar
S → A B, where A → 'a' and B → 'b'. It implements recursive descent parsing,
checking if the input starts with 'a' and is followed by 'b'. The program ensures
the entire string adheres to the rules. If valid, it outputs "True"; otherwise, it
prints "False".
Source Code:
#include <stdio.h>
#include <string.h>
int S(const char *input, size_t *index);
int A(const char *input, size_t *index);
int B(const char *input, size_t *index);
int S(const char *input, size_t *index) {
size_t current_index = *index;
if (A(input, &current_index) && B(input, &current_index)) {
*index = current_index;
return 1; }
return 0;
}
int A(const char *input, size_t *index) {
if (*index < strlen(input) && input[*index] == 'a') {
(*index)++;
return 1; }
return 0;
}
int B(const char *input, size_t *index) {
if (*index < strlen(input) && input[*index] == 'b') {
(*index)++;
return 1; }
return 0;
}
int check_string(const char *input) {
size_t index = 0;
if (S(input, &index) && index == strlen(input)) {
return 1; }
return 0;
}
int main() {
char input[100];
printf("Enter a string: ");
scanf("%s", input);
if (check_string(input)) {
printf("Output: True\n");
} else {
printf("Output: False\n");
}
return 0;
}
Output:
Experiment 4
Aim: To implement a C program that converts a given Non-Deterministic
Finite Automaton (NDFA) into a Deterministic Finite Automaton (DFA)
Language Used: C Language
Theory: The conversion of NDFA to DFA is done using the subset
construction method, where each DFA state represents a set of NDFA states.
This ensures deterministic transitions by eliminating multiple paths. The
process involves state expansion, transition mapping, and minimization,
leading to an equivalent DFA with unique transitions.
Source Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STATES 10
#define MAX_SYMBOLS 10
int ndfa_states, ndfa_symbols;
char symbols[MAX_SYMBOLS];
int ndfa_table[MAX_STATES][MAX_SYMBOLS][MAX_STATES];
int dfa_table[1 << MAX_STATES][MAX_SYMBOLS];
int dfa_states = 0;
int state_map[1 << MAX_STATES];
int get_state_index(int state_set) {
if (state_map[state_set] == -1)
state_map[state_set] = dfa_states++;
return state_map[state_set];
}
void convert_ndfa_to_dfa() {
memset(dfa_table, -1, sizeof(dfa_table));
memset(state_map, -1, sizeof(state_map));
int queue[1 << MAX_STATES], front = 0, rear = 0;
queue[rear++] = 1;
state_map[1] = dfa_states++;
while (front < rear) {
int cur_state = queue[front++];
int dfa_state_index = get_state_index(cur_state);
for (int i = 0; i < ndfa_symbols; i++) {
int new_state = 0;
for (int j = 0; j < ndfa_states; j++) {
if (cur_state & (1 << j)) {
for (int k = 0; k < ndfa_states; k++) {
if (ndfa_table[j][i][k])
new_state |= (1 << k); } } }
if (new_state && state_map[new_state] == -1) {
queue[rear++] = new_state;
state_map[new_state] = dfa_states++; }
dfa_table[dfa_state_index][i] = get_state_index(new_state);
}}}
void print_dfa() {
printf("\nDFA Transition Table:\n");
printf("State ");
for (int i = 0; i < ndfa_symbols; i++)
printf("%c ", symbols[i]);
printf("\n");
for (int i = 0; i < dfa_states; i++) {
printf("Q%d ", i);
for (int j = 0; j < ndfa_symbols; j++) {
if (dfa_table[i][j] == -1)
printf("- ");
else
printf("Q%d ", dfa_table[i][j]); }
printf("\n"); }

int main() {
printf("Enter number of NDFA states: ");
scanf("%d", &ndfa_states);
printf("Enter number of input symbols: ");
scanf("%d", &ndfa_symbols);
printf("Enter the input symbols: ");
for (int i = 0; i < ndfa_symbols; i++)
scanf(" %c", &symbols[i]);
memset(ndfa_table, 0, sizeof(ndfa_table));
printf("Enter NDFA transition table (state transitions for each state and input
symbol as a space-separated list of states):\n");
for (int i = 0; i < ndfa_states; i++) {
for (int j = 0; j < ndfa_symbols; j++) {
printf("NDFA(%d, %c): ", i, symbols[j]);
char line[100];
fgets(line, sizeof(line), stdin); // Read line input
if (strlen(line) == 1) fgets(line, sizeof(line), stdin); // Handle newline
char *token = strtok(line, " ");
while (token) {
int next_state = atoi(token);
ndfa_table[i][j][next_state] = 1;
token = strtok(NULL, " "); } } }
convert_ndfa_to_dfa();
print_dfa();
return 0;
}
Output:
Experiment 5
Aim: Write a program to compute First and Follow of all the Non-terminals in
the given context free grammar.
Language Used: C
Theory: FIRST and FOLLOW sets are fundamental concepts used in syntax
analysis (parsing), particularly in LL(1) parsers. These sets help in
constructing predictive parsing tables and resolving ambiguities in grammar
rules.
FIRST Set
The FIRST set of a non-terminal X, denoted as FIRST(X), is the set of
terminals that can appear at the beginning of some string derived from X.
Rules to Compute FIRST(X)
1. If X is a terminal, then FIRST(X) = {X}.
2. If X → ε (X can derive epsilon, meaning it can be empty), then add ε
to FIRST(X).
3. If X → Y₁ Y₂ ... Yₖ, then:
o Add FIRST(Y₁) to FIRST(X) (excluding ε).
o If Y₁ can produce ε, then add FIRST(Y₂) to FIRST(X), and so
on.
o If all Yᵢ can derive ε, then add ε to FIRST(X).
FOLLOW Set
The FOLLOW set of a non-terminal X, denoted as FOLLOW(X), contains all
terminals that can appear immediately after X in some sentential form.
Rules to Compute FOLLOW(X)
1. If X is the start symbol, add $ (end of input) to FOLLOW(X).
2. If A → αXβ, then everything in FIRST(β) (except ε) is added to
FOLLOW(X).
3. If A → αX or A → αXβ where FIRST(β) contains ε, then FOLLOW(A)
is added to FOLLOW(X).
Code:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>
#define MAX_RULES 10
#define MAX_SYMBOLS 10
char productions[MAX_RULES][MAX_SYMBOLS];
char first[MAX_RULES][MAX_SYMBOLS];
char follow[MAX_RULES][MAX_SYMBOLS];
int n;
void findFirst(char, int, int);
void findFollow(char);
bool isNonTerminal(char);

void addToSet(char *set, char val) {


if (strchr(set, val) == NULL) {
int len = strlen(set);
set[len] = val;
set[len + 1] = '\0'; }
}
void findFirst(char ch, int ruleIndex, int pos) {
if (!isNonTerminal(ch)) {
addToSet(first[ruleIndex], ch);
return; }
for (int i = 0; i < n; i++) {
if (productions[i][0] == ch) {
if (productions[i][2] == '\0' || productions[i][2] == 'e') {
addToSet(first[ruleIndex], 'e');
} else {
findFirst(productions[i][2], ruleIndex, pos + 1);
}}}}
void findFollow(char ch) {
if (ch == productions[0][0]) {
addToSet(follow[0], '$'); }
for (int i = 0; i < n; i++) {
for (int j = 2; productions[i][j] != '\0'; j++) {
if (productions[i][j] == ch) {
if (productions[i][j + 1] != '\0') {
findFirst(productions[i][j + 1], i, j + 1);
for (int k = 0; k < strlen(first[i]); k++) {
if (first[i][k] != 'e') {
addToSet(follow[i], first[i][k]); } }
}
if (productions[i][j + 1] == '\0' || strchr(first[i], 'e')) {
findFollow(productions[i][0]);
for (int k = 0; k < strlen(follow[i]); k++) {
addToSet(follow[i], follow[i][k]); }}}}}
}
bool isNonTerminal(char ch) {
return ch >= 'A' && ch <= 'Z';}

int main() {
printf("Enter the number of productions: ");
scanf("%d", &n); getchar();
printf("Enter the productions (e for epsilon, format: A=B, C -> A=BC):\n");
for (int i = 0; i < n; i++) {
fgets(productions[i], MAX_SYMBOLS, stdin);
productions[i][strcspn(productions[i], "\n")] = 0;
}
for (int i = 0; i < n; i++) {
first[i][0] = '\0';
follow[i][0] = '\0';
}
for (int i = 0; i < n; i++) {
findFirst(productions[i][0], i, 0);
}
for (int i = 0; i < n; i++) {
findFollow(productions[i][0]);
}
printf("\nFIRST sets:\n");
for (int i = 0; i < n; i++) {
printf("FIRST(%c) = { %s }\n", productions[i][0], first[i]);
}
printf("\nFOLLOW sets:\n");
for (int i = 0; i < n; i++) {
printf("FOLLOW(%c) = { %s }\n", productions[i][0], follow[i]);
}return 0;
}
Output:
Experiment 6
Aim: Write a program to construct parsing table of predictive parser.
Language Used: C
Theory:
Parsing is a fundamental concept in compiler design, where it involves
analysing the syntax of a given string or source code based on predefined
grammar rules. The process ensures that the input follows the correct
grammatical structure of a language, allowing for further processing such as
semantic analysis and code generation.
This program performs First and Follow computation for a given context-free
grammar (CFG) and constructs the Predictive Parsing Table based on the
LL(1) parsing technique.
LL(1) Parsing Table Construction
The program constructs an LL(1) Parsing Table, which is used for top-down
parsing. The table is created using the following rules:
1. For each production A → α:
o Add the production to table entry M[A, a] for each a in First(α).
o If ε is in First(α), add the production to table entry M[A, b] for
each b in Follow(A).
2. If a table entry is empty, it means the input is not derivable using the
given grammar.
Code:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX 10
char grammar[MAX][20], first[MAX][20], follow[MAX][20], table[MAX]
[MAX][20];
char terminals[MAX], nonTerminals[MAX], stack[MAX], input[20];
int numProductions, numTerminals = 0, numNonTerminals = 0, top = -1;
void computeFirst();
void computeFollow();
void constructParsingTable();
void displayParsingTable();
void parseString();
void push(char);
char pop();
int isTerminal(char);
int getNonTerminalIndex(char);
int getTerminalIndex(char);

int main() {
printf("Enter the number of productions: ");
scanf("%d", &numProductions);
printf("Enter the grammar productions (e.g., S->AB | a):\n");
for (int i = 0; i < numProductions; i++) {
scanf("%s", grammar[i]);
}
computeFirst();
computeFollow();
constructParsingTable();
displayParsingTable();
parseString();
return 0;
}
void computeFirst() {
for (int i = 0; i < numProductions; i++) {
char nt = grammar[i][0];
int idx = getNonTerminalIndex(nt);
int j = 3;
while (grammar[i][j] != '\0') {
if (isTerminal(grammar[i][j]) || grammar[i][j] == 'e') {
strncat(first[idx], &grammar[i][j], 1);
break;
}j++; }} }
void computeFollow() {
follow[0][0] = '$';
for (int i = 0; i < numProductions; i++) {
char nt = grammar[i][0];
int idx = getNonTerminalIndex(nt);
int j = 3;
while (grammar[i][j] != '\0') {
if (!isTerminal(grammar[i][j]) && grammar[i][j + 1] == '\0') {
strncat(follow[idx], follow[getNonTerminalIndex(grammar[i][0])],
MAX);
} j++; } } }
void constructParsingTable() {
for (int i = 0; i < numProductions; i++) {
char nt = grammar[i][0];
int ntIndex = getNonTerminalIndex(nt);
int j = 3;
while (grammar[i][j] != '\0') {
if (isTerminal(grammar[i][j]) || grammar[i][j] == 'e') {
int tIndex = getTerminalIndex(grammar[i][j]);
strcpy(table[ntIndex][tIndex], grammar[i]);
break; } j++; } } }
void displayParsingTable() {
printf("\nLL(1) Parsing Table:\n");
for (int i = 0; i < numNonTerminals; i++) {
for (int j = 0; j < numTerminals; j++) {
if (strlen(table[i][j]) > 0) {
printf("M[%c, %c] = %s\n", nonTerminals[i], terminals[j], table[i]
[j]); } } } }
void parseString() {
printf("\nEnter input string: ");
scanf("%s", input);
strcat(input, "$");
push('$');
push(nonTerminals[0]);
int i = 0;
while (stack[top] != '$') {
char topSymbol = pop();
char currentSymbol = input[i];
if (topSymbol == currentSymbol) {
i++;
} else if (isTerminal(topSymbol)) {
printf("ERROR! Unexpected symbol: %c\n", currentSymbol);
return;
} else {
int ntIndex = getNonTerminalIndex(topSymbol);
int tIndex = getTerminalIndex(currentSymbol);
if (ntIndex == -1 || tIndex == -1 || strlen(table[ntIndex][tIndex]) == 0) {
printf("ERROR! No rule for %c with input %c\n", topSymbol,
currentSymbol);
return;
}
char *rule = strchr(table[ntIndex][tIndex], '>') + 1;
for (int j = strlen(rule) - 1; j >= 0; j--) {
if (rule[j] != 'e') {
push(rule[j]); } } } }
if (input[i] == '$') {
printf("\nParsing Successful!\n");
} else {
printf("\nParsing Failed!\n"); }
}
void push(char ch) {
if (top < MAX - 1) {
stack[++top] = ch; }
}
char pop() {
return (top >= 0) ? stack[top--] : '\0';
}
int isTerminal(char ch) {
return !isupper(ch) && ch != 'e' && ch != '\0';
}
int getNonTerminalIndex(char ch) {
for (int i = 0; i < numNonTerminals; i++) {
if (nonTerminals[i] == ch) return i;
}
nonTerminals[numNonTerminals++] = ch;
return numNonTerminals - 1;
}
int getTerminalIndex(char ch) {
for (int i = 0; i < numTerminals; i++) {
if (terminals[i] == ch) return i; }
terminals[numTerminals++] = ch;
return numTerminals - 1;
}
Output:
Experiment 7
Aim: Write a program to check whether a given grammar is LL(1) or not
using parsing table.
Language Used: C
Theory:
LL(1) parsing is a top-down method that uses a parsing table to decide the
next action. A grammar is LL(1) if:
1. No Left Recursion – Avoids infinite loops.
2. No Ambiguity – Each parsing table cell has at most one production.
3. No Left Factoring – No common prefixes in productions.
To check if a grammar is LL(1):
1. Compute First and Follow sets.
2. Build the Parsing Table using these sets.
3. If any cell has multiple entries, the grammar is not LL(1).
This ensures efficient predictive parsing without backtracking.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX 10

typedef struct {
char nonTerminal;
char productions[MAX][MAX];
int prodCount;
char first[MAX];
char follow[MAX];
} Grammar;
int n;
Grammar grammar[MAX];

int isNonTerminal(char ch) {


return (ch >= 'A' && ch <= 'Z');
}
void calculateFirst(int index, char firstSet[MAX]) {
for (int i = 0; i < grammar[index].prodCount; i++) {
char firstSymbol = grammar[index].productions[i][0];
if (!isNonTerminal(firstSymbol)) {
strncat(firstSet, &firstSymbol, 1);
} else {
for (int j = 0; j < n; j++) {
if (grammar[j].nonTerminal == firstSymbol) {
strcat(firstSet, grammar[j].first);
}}}}}
void calculateFollow() {
grammar[0].follow[0] = '$';
for (int i = 0; i < n; i++) {
for (int j = 0; j < grammar[i].prodCount; j++) {
char *prod = grammar[i].productions[j];
int len = strlen(prod);
for (int k = 0; k < len - 1; k++) {
if (isNonTerminal(prod[k])) {
strncat(grammar[i].follow, &prod[k + 1], 1); }}}}}
int checkLL1() {
char parsingTable[MAX][MAX][MAX] = {""};
for (int i = 0; i < n; i++) {
char firstSet[MAX] = "";
calculateFirst(i, firstSet);
for (int j = 0; j < strlen(firstSet); j++) {
char terminal = firstSet[j];
if (parsingTable[grammar[i].nonTerminal - 'A'][terminal - 'a'][0] != '\0'){
return 0;
}
strcpy(parsingTable[grammar[i].nonTerminal - 'A'][terminal - 'a'],
grammar[i].productions[j]); }
}
return 1;
}
int main() {
printf("Enter the number of non-terminals: ");
scanf("%d", &n);
for (int i = 0; i < n; i++) {
printf("Enter non-terminal %d: ", i + 1);
scanf(" %c", &grammar[i].nonTerminal);
printf("Enter the number of productions for %c: ",
grammar[i].nonTerminal);
scanf("%d", &grammar[i].prodCount);
printf("Enter productions for %c (one per line):\n",
grammar[i].nonTerminal);
for (int j = 0; j < grammar[i].prodCount; j++) {
scanf("%s", grammar[i].productions[j]);
}
}
calculateFollow();

if (checkLL1()) {
printf("The given grammar is LL(1).\n");
} else {
printf("The given grammar is NOT LL(1).\n");
}
return 0;
}
Output:
Experiment 8
Aim: To compute a Shift reducer parser in C for the given CFG.
Language Used: C Language
Theory: A Shift-Reduce Parser is a type of bottom-up parser that analyses a
string of tokens by shifting input symbols onto a stack and reducing them
according to grammar rules until it derives the start symbol. It is widely used
for syntax analysis in compilers.
Concepts of Shift-Reduce Parsing
1. Stack and Input Buffer
o The parser uses a stack to store symbols and intermediate
results.
o The input buffer contains the remaining tokens to be
processed.
2. Actions in Shift-Reduce Parsing
o Shift: Move the next input token from the input buffer to the
stack.
o Reduce: Replace a sequence of stack symbols (matching a
grammar rule’s RHS) with the corresponding LHS.
o Accept: If the stack contains the start symbol and the input
buffer is empty, parsing is successful.
o Error: If no valid shift or reduction is possible, parsing fails.
Source Code:
#include <stdio.h>
#include <string.h>
#define MAX 100

char stack[MAX];
int top = -1;
char input[MAX];
int ip = 0;
char *productions[] = {
"E->E+E",
"E->E*E",
"E->(E)",
"E->id" };
void printStack() {
printf("%-15s %-15s ", stack, &input[ip]);
}
void shift() {
if (input[ip] != '$') { // Prevent shifting the end marker
stack[++top] = input[ip++];
stack[top + 1] = '\0';
printStack();
printf("Shift\n"); }
}
int reduce() {
for (int i = 0; i < 4; i++) {
int len = strlen(productions[i]) - 3;
if (top >= len - 1 && strncmp(&stack[top - len + 1], &productions[i][3],
len) == 0) {
stack[top - len + 1] = 'E'; stack[top - len + 2] = '\0';
top = top - len + 1; printStack();
printf("Reduce by %s\n", productions[i]);
return 1; } }
return 0;
}
void shiftReduceParser() {
printf("\n%-15s %-15s %s\n", "Stack", "Input", "Action");
printf("---------------------------------------------------\n");
while (ip < strlen(input) - 1) { // Stop before end marker
shift();
while (reduce());
}
while (reduce());
if (top == 0 && stack[0] == 'E') {
printf("\nFinal Result: Accepted (Valid Expression)\n");
} else {
printf("\nFinal Result: Rejected (Invalid Expression)\n"); }
}
int main() {
strcpy(input, "id+id*id$"); // Example input
shiftReduceParser();
return 0;
}
Output:
Experiment 9
Aim: Write a program in C to check whether the given grammar is operator
precedence grammar or not.
Language Used: C Language
Theory: Operator Precedence Grammar (OPG) is a type of context-free
grammar used in parsing, ensuring that no production contains ε-productions,
left recursion, or consecutive non-terminals. It allows defining operator
precedence relations between terminals, enabling efficient bottom-up parsing.
OPG is widely used in expression evaluation and compiler design.
Source Code:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX 100
int hasEpsilonProduction(char grammar[MAX][MAX], int n) {
for (int i = 0; i < n; i++) {
if (strchr(grammar[i], 'ε')) {
return 1; }
}return 0;
}
int hasConsecutiveNonTerminals(char grammar[MAX][MAX], int n) {
for (int i = 0; i < n; i++) {
for (int j = 3; j < strlen(grammar[i]) - 1; j++) { // Start after '->'
if (isupper(grammar[i][j]) && isupper(grammar[i][j + 1])) {
return 1; } } }
return 0;
}
int hasLeftRecursion(char grammar[MAX][MAX], int n) {
for (int i = 0; i < n; i++) {
if (grammar[i][0] == grammar[i][3]) {
return 1; } }
return 0;
}
void checkOPG(char grammar[MAX][MAX], int n) {
printf("\nChecking Operator Precedence Grammar (OPG) conditions...\n");
if (hasEpsilonProduction(grammar, n)) {
printf("The grammar contains ε-production. Not an OPG.\n");
return; }
if (hasConsecutiveNonTerminals(grammar, n)) {
printf("The grammar has consecutive non-terminals. Not an OPG.\n");
return; }
if (hasLeftRecursion(grammar, n)) {
printf("The grammar has left recursion. Not an OPG.\n");
return; }
printf("The grammar satisfies all conditions and is an Operator Precedence
Grammar.\n"); }
int main()
{
int n = 4;
char grammar[MAX][MAX] = {
"E->E+T",
"E->T",
"T->T*F",
"T->F" };
printf("Given Grammar:\n");
for (int i = 0; i < n; i++) {
printf("%s\n", grammar[i]);
}
checkOPG(grammar, n);
return 0;
}
Output:
Experiment 10
Aim: To compute the leading and trailing of the non-Terminals in the given
CFG.
Language Used: C Language
Theory: Leading and Trailing of Non-Terminals in a CFG
In Context-Free Grammar (CFG), the concepts of Leading and Trailing help in
syntax analysis, particularly in operator precedence parsing.
1. Leading of a Non-Terminal (LEADING(A))
The Leading of a non-terminal AAA consists of all the first terminal symbols
that can appear at the beginning of some string derived from AAA.
 If A→aα, where α is a terminal, then α is in LEADING(A).
 If A→Bα, then LEADING(B) is included in LEADING(A).
2. Trailing of a Non-Terminal (TRAILING(A))
The Trailing of a non-terminal A consists of all the last terminal symbols that
can appear at the end of some string derived from A.
 If A→αa, where α is a terminal, then α is in TRAILING(A).
 If A→αB, then TRAILING(B) is included in TRAILING(A).
Source Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define MAX_NON_TERMINALS 10
#define MAX_TERMINALS 10
#define MAX_PRODUCTIONS 20
#define MAX_SYMBOLS_PER_PRODUCTION 10
typedef struct {
char lhs;
char rhs[MAX_SYMBOLS_PER_PRODUCTION];
} Production;
typedef struct {
char symbol;
char leading[MAX_TERMINALS];
int leading_count;
char trailing[MAX_TERMINALS];
int trailing_count;
} NonTerminalInfo;

int find_non_terminal_index(NonTerminalInfo non_terminals[], int


non_terminal_count, char symbol) {
for (int i = 0; i < non_terminal_count; i++) {
if (non_terminals[i].symbol == symbol) {
return i; } }
return -1;
}
bool is_terminal(char symbol, char terminals[], int terminal_count) {
for (int i = 0; i < terminal_count; i++) {
if (terminals[i] == symbol) {
return true; }}
return false;
}
void add_to_set(char set[], int *count, char symbol) {
for (int i = 0; i < *count; i++) {
if (set[i] == symbol) {
return; }
}set[(*count)++] = symbol;
}
void compute_leading(Production productions[], int production_count,
NonTerminalInfo non_terminals[], int non_terminal_count, char terminals[],
int terminal_count) {
bool changed;
do {
changed = false;
for (int i = 0; i < production_count; i++) {
char lhs = productions[i].lhs;
char *rhs = productions[i].rhs;
int lhs_index = find_non_terminal_index(non_terminals,
non_terminal_count, lhs);
if (is_terminal(rhs[0], terminals, terminal_count)) {
if (!strchr(non_terminals[lhs_index].leading, rhs[0])) {
add_to_set(non_terminals[lhs_index].leading,
&non_terminals[lhs_index].leading_count, rhs[0]);
changed = true; }
} else {
int rhs_non_terminal_index =
find_non_terminal_index(non_terminals, non_terminal_count, rhs[0]);
if (rhs_non_terminal_index != -1) {
for (int j = 0; j <
non_terminals[rhs_non_terminal_index].leading_count; j++) {
if (!strchr(non_terminals[lhs_index].leading,
non_terminals[rhs_non_terminal_index].leading[j])) {
add_to_set(non_terminals[lhs_index].leading,
&non_terminals[lhs_index].leading_count,
non_terminals[rhs_non_terminal_index].leading[j]);
changed = true; }
}
if(strlen(rhs) > 1 && rhs[1] != '\0' &&
is_terminal(rhs[1],terminals, terminal_count)){
if (!strchr(non_terminals[lhs_index].leading, rhs[1])) {
add_to_set(non_terminals[lhs_index].leading,
&non_terminals[lhs_index].leading_count, rhs[1]);
changed = true; }} }} }}
while (changed);
}
void compute_trailing(Production productions[], int production_count,
NonTerminalInfo non_terminals[], int non_terminal_count, char terminals[],
int terminal_count) {
bool changed;
do {
changed = false;
for (int i = 0; i < production_count; i++) {
char lhs = productions[i].lhs;
char *rhs = productions[i].rhs;
int lhs_index = find_non_terminal_index(non_terminals,
non_terminal_count, lhs);
int rhs_len = strlen(rhs);
if (is_terminal(rhs[rhs_len - 1], terminals, terminal_count)) {
if (!strchr(non_terminals[lhs_index].trailing, rhs[rhs_len - 1])) {
add_to_set(non_terminals[lhs_index].trailing,
&non_terminals[lhs_index].trailing_count, rhs[rhs_len - 1]);
changed = true; }
} else {
int rhs_non_terminal_index =
find_non_terminal_index(non_terminals, non_terminal_count, rhs[rhs_len -
1]);
if (rhs_non_terminal_index != -1) {
for (int j = 0; j <
non_terminals[rhs_non_terminal_index].trailing_count; j++) {
if (!strchr(non_terminals[lhs_index].trailing,
non_terminals[rhs_non_terminal_index].trailing[j])) {
add_to_set(non_terminals[lhs_index].trailing,
&non_terminals[lhs_index].trailing_count,
non_terminals[rhs_non_terminal_index].trailing[j]);
changed = true; }
}
if(rhs_len > 1 && rhs[rhs_len-2] != '\0' &&
is_terminal(rhs[rhs_len-2],terminals, terminal_count)){
if (!strchr(non_terminals[lhs_index].trailing, rhs[rhs_len-2])) {
add_to_set(non_terminals[lhs_index].trailing,
&non_terminals[lhs_index].trailing_count, rhs[rhs_len-2]);
changed = true; }}}}}
} while (changed);
}
int main() {
Production productions[MAX_PRODUCTIONS] = {
{'S', "ABc"},
{'A', "Ca"},
{'B', "bC"},
{'C', "d"}
};
int production_count = 4;
char non_terminals_chars[] = {'S', 'A', 'B', 'C'}; int non_terminal_count = 4;
char terminals[] = {'a', 'b', 'c', 'd'}; int terminal_count = 4;
NonTerminalInfo non_terminals[MAX_NON_TERMINALS];
for (int i = 0; i < non_terminal_count; i++) {
non_terminals[i].symbol = non_terminals_chars[i];
non_terminals[i].leading_count = 0;
non_terminals[i].trailing_count = 0;
memset(non_terminals[i].leading, 0, sizeof(non_terminals[i].leading));
memset(non_terminals[i].trailing, 0, sizeof(non_terminals[i].trailing));
}
compute_leading(productions, production_count, non_terminals,
non_terminal_count, terminals, terminal_count);
compute_trailing(productions, production_count, non_terminals,
non_terminal_count, terminals, terminal_count);
for (int i = 0; i < non_terminal_count; i++) {
printf("Non-Terminal: %c\n", non_terminals[i].symbol);
printf(" Leading: {");
for (int j = 0; j < non_terminals[i].leading_count; j++) {
printf("%c", non_terminals[i].leading[j]);
if (j < non_terminals[i].leading_count - 1) {
printf(", "); } }
printf("}\n");
printf(" Trailing: {");
for (int j = 0; j < non_terminals[i].trailing_count; j++) {
printf("%c", non_terminals[i].trailing[j]);
if (j < non_terminals[i].trailing_count - 1) {
printf(", "); }
}printf("}\n"); }
return 0; }

Output:
Experiment 11
Aim: Write a program in C to Check whether given CFG is a SLR parser or
not.
Language Used: C Language
Theory: This experiment aims to verify whether a given Context-Free
Grammar (CFG) can be parsed using an SLR(1) parser. We construct the
LR(0) automaton, derive the SLR(1) parsing table, and check for conflicts. If
shift-reduce or reduce-reduce conflicts exist, the grammar fails to be SLR(1).
Source Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_RULES 20
#define MAX_SYMBOLS 10
#define MAX_ITEMS 100
typedef struct {
char left;
char right[MAX_SYMBOLS];
} Rule;
Rule rules[MAX_RULES]; // Declare 'rules' globally
void calculateFirst(char symbol, char firstSet[MAX_SYMBOLS], int
numRules) {
firstSet[0] = '\0'; // Initialize to empty string
if (symbol >= 'A' && symbol <= 'Z') {
for (int i = 0; i < numRules; i++) {
if (rules[i].left == symbol) {
if (rules[i].right[0] >= 'a' && rules[i].right[0] <= 'z') {
firstSet[0] = rules[i].right[0];
firstSet[1] = '\0';
return; } } }
} else if (symbol >= 'a' && symbol <= 'z') {
firstSet[0] = symbol;
firstSet[1] = '\0';
return;
} else if (symbol == '#') {
firstSet[0] = '#';
firstSet[1] = '\0';
return; }
}
void calculateFollow(char symbol, char followSet[MAX_SYMBOLS], int
numRules) {
followSet[0] = '\0'; // Initialize to empty string
if (symbol == rules[0].left) {
followSet[0] = '$';
followSet[1] = '\0'; }
}
int checkSLR(int numRules, Rule rules[]) {
for (int i = 0; i < numRules; i++) {
for (int j = i + 1; j < numRules; j++) {
if (rules[i].left == rules[j].left && strcmp(rules[i].right, rules[j].right)
== 0) {
return 0; }}}
return 1;
}

int main() {
int numRules;
printf("Enter the number of rules: ");
scanf("%d", &numRules);
printf("Enter the grammar rules (Left -> Right):\n");
for (int i = 0; i < numRules; i++) {
scanf(" %c -> %s", &rules[i].left, rules[i].right); }
if (checkSLR(numRules, rules)) {
printf("The given CFG is SLR (according to simplified check).\n");
} else {
printf("The given CFG is NOT SLR (according to simplified check).\n");
}
printf("\n--- Sample Grammar 2 ---\n"); numRules = 3;
rules[0].left = 'S'; strcpy(rules[0].right, "AA");
rules[1].left = 'A'; strcpy(rules[1].right, "aA");
rules[2].left = 'A'; strcpy(rules[2].right, "b");
if (checkSLR(numRules, rules)) {
printf("The given CFG is SLR (according to simplified check).\n");
} else {
printf("The given CFG is NOT SLR (according to simplified check).\n");
} return 0;
}

Output:
Experiment 12
Aim: Write a program in C to Check whether given CFG is a CLR(1) parser
or not.
Language Used: C Language
Theory: In this experiment, we analyse a given Context-Free Grammar (CFG)
to determine if it is CLR(1) (Canonical LR(1)) parsable. This involves
constructing the LR(1) automaton, generating the CLR(1) parsing table, and
checking for conflicts. If no conflicts exist, the grammar is CLR(1) parsable;
otherwise, it is not.
Source Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_RULES 20
#define MAX_SYMBOLS 10
#define MAX_ITEMS 100
#define MAX_LOOKAHEAD 10
typedef struct {
char left;
char right[MAX_SYMBOLS];
} Rule;
typedef struct {
int ruleIndex;
int dotPosition;
char lookahead[MAX_LOOKAHEAD];
} Item;
void calculateFirst(char symbol, char firstSet[MAX_SYMBOLS], Rule
rules[], int numRules) {
firstSet[0] = '\0';
if (symbol >= 'A' && symbol <= 'Z') {
for (int i = 0; i < numRules; i++) {
if (rules[i].left == symbol) {
if (rules[i].right[0] >= 'a' && rules[i].right[0] <= 'z') {
firstSet[0] = rules[i].right[0];
firstSet[1] = '\0';
return; }}}
} else if (symbol >= 'a' && symbol <= 'z') {
firstSet[0] = symbol; firstSet[1] = '\0';
return;
} else if (symbol == '#') {
firstSet[0] = '#'; firstSet[1] = '\0';
return; } }
void calculateFollow(char symbol, char followSet[MAX_SYMBOLS], Rule
rules[], int numRules) {
followSet[0] = '\0';
if (symbol == rules[0].left) {
followSet[0] = '$'; followSet[1] = '\0'; }
}
int checkCLR1(int numRules, Rule rules[]) {
for (int i = 0; i < numRules; i++) {
for (int j = i + 1; j < numRules; j++) {
if (rules[i].left == rules[j].left && strcmp(rules[i].right, rules[j].right)
== 0) {
return 0; }}}
return 1;
}
int main() {
int numRules; Rule rules[MAX_RULES];
printf("Enter the number of rules: "); scanf("%d", &numRules);
printf("Enter the grammar rules (Left -> Right):\n");
for (int i = 0; i < numRules; i++) {
scanf(" %c -> %s", &rules[i].left, rules[i].right);
}
if (checkCLR1(numRules, rules)) {
printf("The given CFG is CLR(1) (according to simplified check).\n");
} else {
printf("The given CFG is NOT CLR(1) (according to simplified check).\
n");
}
printf("\n--- Sample Grammar 2 ---\n"); numRules = 5;
rules[0].left = 'S'; strcpy(rules[0].right, "aAd");
rules[1].left = 'S'; strcpy(rules[1].right, "aBd");
rules[2].left = 'A'; strcpy(rules[2].right, "c");
rules[3].left = 'B'; strcpy(rules[3].right, "c");
rules[4].left = 'X'; strcpy(rules[4].right, "a");
if (checkCLR1(numRules, rules)) {
printf("The given CFG is CLR(1) (according to simplified check).\n");
} else {
printf("The given CFG is NOT CLR(1) (according to simplified check).\
n");
} return 0;
}
Output:
Experiment 13
Aim: Write a program in C to Check whether given CFG is a LALR(1) parser
or not.
Language Used: C Language
Theory: This experiment verifies whether a given Context-Free Grammar
(CFG) can be parsed using an LALR(1) parser. We construct the LR(1)
automaton, merge states with the same LR(0) core, and generate the LALR(1)
parsing table. If no conflicts exist, the grammar is LALR(1) parsable;
otherwise, it is not.
Source Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_RULES 20
#define MAX_SYMBOLS 10
#define MAX_ITEMS 100
#define MAX_LOOKAHEAD 10
typedef struct {
char left;
char right[MAX_SYMBOLS];
} Rule;
typedef struct {
int ruleIndex;
int dotPosition;
char lookahead[MAX_LOOKAHEAD];
} Item;
void calculateFirst(char symbol, char firstSet[MAX_SYMBOLS], Rule
rules[], int numRules) {
firstSet[0] = '\0';
if (symbol >= 'A' && symbol <= 'Z') {
for (int i = 0; i < numRules; i++) {
if (rules[i].left == symbol) {
if (rules[i].right[0] >= 'a' && rules[i].right[0] <= 'z') {
firstSet[0] = rules[i].right[0];
firstSet[1] = '\0';
return; }} }
} else if (symbol >= 'a' && symbol <= 'z') {
firstSet[0] = symbol; firstSet[1] = '\0';
return;
} else if (symbol == '#') {
firstSet[0] = '#'; firstSet[1] = '\0';
return; }
}
void calculateFollow(char symbol, char followSet[MAX_SYMBOLS], Rule
rules[], int numRules) {
followSet[0] = '\0';
if (symbol == rules[0].left) {
followSet[0] = '$'; followSet[1] = '\0'; }
}
int checkLALR1(int numRules, Rule rules[]) {
for (int i = 0; i < numRules; i++) {
for (int j = i + 1; j < numRules; j++) {
if (rules[i].left == rules[j].left && strcmp(rules[i].right, rules[j].right)
== 0) {
return 0; }}
}
return 1;
}
int main() {
int numRules;
Rule rules[MAX_RULES];
printf("Enter the number of rules: ");
scanf("%d", &numRules);
printf("Enter the grammar rules (Left -> Right):\n");
for (int i = 0; i < numRules; i++) {
scanf(" %c -> %s", &rules[i].left, rules[i].right);
}

if (checkLALR1(numRules, rules)) {
printf("The given CFG is LALR(1) (according to simplified check).\n");
} else {

printf("The given CFG is NOT LALR(1) (according to simplified check).\n");


}

printf("\n--- Sample Grammar 2 ---\n");


numRules = 5;
rules[0].left = 'S'; strcpy(rules[0].right, "aAd");
rules[1].left = 'S'; strcpy(rules[1].right, "aBd");
rules[2].left = 'A'; strcpy(rules[2].right, "c");
rules[3].left = 'B'; strcpy(rules[3].right, "c");
rules[4].left = 'X'; strcpy(rules[4].right, "a");
if (checkLALR1(numRules, rules)) {
printf("The given CFG is LALR(1) (according to simplified check).\n");
} else {
printf("The given CFG is NOT LALR(1) (according to simplified
check).\n");
}
return 0;
}

Output:
Open Ended Program
Aim: To implement the concept of translator of one language to another
language.
Language Used: C Language
Theory: Compilers are essential tools that convert high-level programming
languages like C into machine-understandable code. However, before reaching
the final machine code, an intermediate step is assembly language, which
provides human-readable low-level instructions. Assembly language is a low-
level programming language that serves as an intermediary between high-level
languages like C and the binary machine code understood by the processor. It
is hardware-specific and provides direct control over a computer’s CPU,
registers, and memory. Unlike C or Python, which are portable across different
platforms, assembly language is designed for a specific processor architecture,
such as x86, x86-64, ARM, or RISC-V.
Assembly language is used in areas where performance, direct hardware
access, or low-level programming is essential. Some applications include:
 Operating System Development – Assembly is used in bootloaders
and kernel programming.
 Embedded Systems – Microcontrollers and real-time systems rely on
assembly for efficiency.
 Cybersecurity & Reverse Engineering – Used for malware analysis
and vulnerability research.
 Performance Optimization – Some critical functions in high-
performance applications are written in assembly.
How It Works
Step 1: Lexical Analysis
The program scans the input code and identifies individual tokens, such as:
 Keywords (int, if, while, return)
 Variables (x, y, sum)
 Operators (+, -, *, /)
 Numbers (10, 5, 100)
Step 2: Syntax Parsing
The extracted tokens are then analysed to recognize valid statements such as:
 Variable Assignments: int x = 10;
 Arithmetic Operations: x = x + 5;
 Conditional Statements: if (x > 10) { y = 5; }
 Loops: while (x < 20) { x++; }
Step 3: Code Generation (Assembly Translation)
The parsed statements are converted into assembly instructions, following the
x86 assembly syntax.

Source Code:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_LEN 256

void trim(char *str) {


char *end;
while (isspace((unsigned char)*str)) str++;
if (*str == 0) return;
end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end)) end--;
*(end + 1) = 0; }
void convert_assignment(char *line) {
char var[MAX_LEN];
int value;
if (sscanf(line, "int %s = %d;", var, &value) == 2) {
printf("MOV %s, %d\n", var, value); }
else if (sscanf(line, "%s = %d;", var, &value) == 2) {
printf("MOV %s, %d\n", var, value); }
else {
printf("; Unsupported assignment: %s\n", line); } }
void convert_arithmetic(char *line) {
char var1[MAX_LEN], var2[MAX_LEN], op;
int value;
if (sscanf(line, "%s = %s %c %d;", var1, var2, &op, &value) == 4) {
printf("MOV EAX, %s\n", var2);
switch (op) {
case '+': printf("ADD EAX, %d\n", value); break;
case '-': printf("SUB EAX, %d\n", value); break;
case '*': printf("IMUL EAX, %d\n", value); break;
case '/': printf("MOV EBX, %d\nDIV EBX\n", value); break; }
printf("MOV %s, EAX\n", var1); }
else {
printf("; Unsupported arithmetic: %s\n", line); } }
void convert_condition(char *line) {
char var[MAX_LEN], op[3], value[MAX_LEN];
if (sscanf(line, "if (%s %s %s) {", var, op, value) == 3) {
printf("MOV EAX, %s\n", var);
printf("CMP EAX, %s\n", value);
if (strcmp(op, "==") == 0) printf("JE LABEL_TRUE\n");
else if (strcmp(op, "!=") == 0) printf("JNE LABEL_TRUE\n");
else if (strcmp(op, "<") == 0) printf("JL LABEL_TRUE\n");
else if (strcmp(op, "<=") == 0) printf("JLE LABEL_TRUE\n");
else if (strcmp(op, ">") == 0) printf("JG LABEL_TRUE\n");
else if (strcmp(op, ">=") == 0) printf("JGE LABEL_TRUE\n");
printf("JMP LABEL_FALSE\nLABEL_TRUE:\n");
} else { printf("; Unsupported condition: %s\n", line); } }
void convert_loop(char *line) {
char var[MAX_LEN], op[3], value[MAX_LEN];
if (sscanf(line, "while (%s %s %s) {", var, op, value) == 3) {
printf("LOOP_START:\n");
printf("MOV EAX, %s\n", var);
printf("CMP EAX, %s\n", value);
if (strcmp(op, "<") == 0) printf("JL LOOP_BODY\n");
else if (strcmp(op, "<=") == 0) printf("JLE LOOP_BODY\n");
else if (strcmp(op, ">") == 0) printf("JG LOOP_BODY\n");
else if (strcmp(op, ">=") == 0) printf("JGE LOOP_BODY\n");
printf("JMP LOOP_END\nLOOP_BODY:\n");
} else {
printf("; Unsupported loop: %s\n", line); } }
void convert_function_call(char *line) {
char func[MAX_LEN], param1[MAX_LEN], param2[MAX_LEN];
if (sscanf(line, "%s(%s, %s);", func, param1, param2) == 3) {
printf("PUSH %s\n", param2);
printf("PUSH %s\n", param1);
printf("CALL %s\n", func);
printf("ADD ESP, 8 ; Clean up stack\n");
} else { printf("; Unsupported function call: %s\n", line); }
}
void translate_line(char *line) {
trim(line);
if (strncmp(line, "int ", 4) == 0 || strchr(line, '=') != NULL) {
convert_assignment(line); }
else if (strchr(line, '+') || strchr(line, '-') || strchr(line, '*') || strchr(line, '/')) {
convert_arithmetic(line); }
else if (strncmp(line, "if", 2) == 0) {
convert_condition(line); }
else if (strncmp(line, "while", 5) == 0) {
convert_loop(line); }
else if (strchr(line, '(') && strchr(line, ')')) {
convert_function_call(line); }
else if (strcmp(line, "}") == 0) {
printf("JMP END_BLOCK\nLABEL_FALSE:\n"); }
else {
printf("; Unrecognized statement: %s\n", line); }
}
int main() {
char input[MAX_LEN];
printf "Enter code below and type 'quit' after completion:\n");
while (1) {
fgets(input, MAX_LEN, stdin);
if (strncmp(input, "quit", 4) == 0) break;
translate_line(input); }
printf("\nTranslation complete!\n");
return 0;
}
Output:

You might also like