0% found this document useful (0 votes)
8 views39 pages

Print 2

The document describes multiple experiments involving C programs for various computational tasks in computer science, including string validation against grammar, NDFA to DFA conversion, computation of FIRST and FOLLOW sets in context-free grammar, and construction of predictive parsing tables. Each experiment includes an aim, theory, and source code demonstrating the implementation of the respective algorithms. The programs utilize concepts such as recursive descent parsing, subset construction, and LL(1) parsing techniques.
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)
8 views39 pages

Print 2

The document describes multiple experiments involving C programs for various computational tasks in computer science, including string validation against grammar, NDFA to DFA conversion, computation of FIRST and FOLLOW sets in context-free grammar, and construction of predictive parsing tables. Each experiment includes an aim, theory, and source code demonstrating the implementation of the respective algorithms. The programs utilize concepts such as recursive descent parsing, subset construction, and LL(1) parsing techniques.
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/ 39

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;
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:

You might also like