0% found this document useful (0 votes)
34 views

EXP - 4 - To - 6CD - Lab Manual - ODD - 2024 - Removed

Ex 4 to 6 pdf of experiments

Uploaded by

maasalamix
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
34 views

EXP - 4 - To - 6CD - Lab Manual - ODD - 2024 - Removed

Ex 4 to 6 pdf of experiments

Uploaded by

maasalamix
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/ 16

Compiler Design (3170701)

Experiment No - 4
Aim: Introduction to YACC and generate Calculator Program

Date: Competency and Practical

Skills:
• Understanding of YACC and its role in compiler construction
• Ability to write grammar rules and YAAC programs for a given language
• Ability to develop program using YACC

Relevant CO: CO2

Objectives:
By the end of this experiment, the students should be able to:
➢ Understand the concept of YACC and its significance in compiler construction
➢ Write grammar rules for a given language
➢ Implement a calculator program using YACC

Software/Equipment: Windows/Linux Operating System, YACC Compiler, Text editor,


Command prompt or terminal

Theory:
YACC (Yet Another Compiler Compiler) is a tool that is used for generating parsers. It is used in
combination with Lex to generate compilers and interpreters. YACC takes a set of rules and
generates a parser that can recognize and process the input according to those rules.

The grammar rules that are defined using YACC are written in BNF (Backus-Naur Form) notation.
These rules describe the syntax of a programming language.

INPUT FILE:
→ The YACC input file is divided into three parts.
/* definitions */
....
%%
/* rules */
....
%%
/* auxiliary routines */
....

Definition Part:
→ The definition part includes information about the tokens used in the syntax definition.

Rule Part:
→ The rules part contains grammar definition in a modified BNF form. Actions is C code in { }
and can be embedded inside (Translation schemes).

Auxiliary Routines Part:


→ The auxiliary routines part is only C code.
→ It includes function definitions for every function needed in the rules part.
→ It can also contain the main() function definition if the parser is going to be run as a program.
→ The main() function must call the function yyparse().
Compiler Design (3170701)

For Compiling YACC Program:


1. Write lex program in a file file.l and yacc in a file file.y
2. Open Terminal and Navigate to the Directory where you have saved the files.
3. type lex file.l
4. type yacc file.y
5. type cc lex.yy.c y.tab.h -ll
6. type ./a.out

The program for generating a calculator using YACC involves the following steps:
➢ Defining the grammar rules for the calculator program
➢ Writing the Lex code for tokenizing the input
➢ Writing the YACC code for parsing the input and generating the output

Program:

calculator.l

%option noyywrap

%%

[0-9]+ { yylval = atoi(yytext); return NUMBER; }


[+\-*/] { return yytext[0]; }
\n { return '\n'; }
. { return yytext[0]; }

%%

calculator.y
%{
#include <stdio.h>
#include <stdlib.h>
int yylex(void);
void yyerror();
%}

%token NUMBER

%left '+' '-'


%left '*' '/'

%%
lines:
Compiler Design (3170701)
| lines expr '\n' { printf("Result: %d\n", $2); }
;

expr:
expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr { $$ = $1 / $3; }
| NUMBER { $$ = $1; }
;

%%

#include "lex.yy.c"

int main() {
printf("Enter expressions (Ctrl+D to exit):\n");
yyparse();
return 0;
}

void yyerror()
{}

Observations and Conclusion:

The provided code implements a basic calculator using Yacc and Lex. It parses and evaluates arithmetic
expressions, printing the results. The lexer (calculator.l) recognizes numbers, operators, and newlines,
while the parser (calculator.y) defines the grammar rules for expressions. The main function reads input
expressions from the user, parses and evaluates them, and prints the results. This code demonstrates a
simple example of using Yacc and Lex to build a calculator, which can be extended for more complex
functionality.
Quiz:
1. What is YACC?
2. What is the purpose of YACC?
3. What is the output of YACC?
4. What is a syntax analyzer?
5. What is the role of a lexical analyzer in YACC?
Compiler Design (3170701)
Experiment No - 5
Aim: Implement a program for constructing
a. LL(1) Parser
b. Predictive Parser

Date: Competency and Practical

Skills:
• Understanding Parsers and its role in compiler construction
• Ability to write first and follow for given grammar
• Ability to develop LL(1) and predictive parser using top down parsing approach

Relevant CO: CO2

Objectives:
By the end of this experiment, the students should be able to:
➢ Understand the concept parsers and its significance in compiler construction
➢ Write first and follow set for given grammar
➢ Implement a LL(1) and predictive grammar using top down parser

Software/Equipment: C compiler

Theory:

❖ LL(1) Parsing: Here the 1st L represents that the scanning of the Input will be done from
the Left to Right manner and the second L shows that in this parsing technique, we are
going to use the Left most Derivation Tree. And finally, the 1 represents the number of
look-ahead, which means how many symbols are you going to see when you want to make
a decision.

Essential conditions to check first are as follows:


1. The grammar is free from left recursion.
2. The grammar should not be ambiguous.
3. The grammar has to be left factored in so that the grammar is deterministic grammar.
These conditions are necessary but not sufficient for proving a LL(1) parser.

Algorithm to construct LL(1) Parsing Table:


Step 1: First check all the essential conditions mentioned above and go to step 2.
Step 2: Calculate First() and Follow() for all non-terminals.
1. First(): If there is a variable, and from that variable, if we try to drive all the strings then
the beginning Terminal Symbol is called the First.
2. Follow(): What is the Terminal Symbol which follows a variable in the process of
derivation.
Step 3: For each production A –> α. (A tends to alpha)
1. Find First(α) and for each terminal in First(α), make entry A –> α in the table.
2. If First(α) contains ε (epsilon) as terminal, then find the Follow(A) and for each terminal in
Follow(A), make entry A –> ε in the table.
3. If the First(α) contains ε and Follow(A) contains $ as terminal, then make entry A –> ε in
the table for the $.
To construct the parsing table, we have two functions:
In the table, rows will contain the Non-Terminals and the column will contain the Terminal
Symbols. All the Null Productions of the Grammars will go under the Follow elements and the
remaining productions will lie under the elements of the First set.
Compiler Design (3170701)

❖ Predictive Parser

Predictive parser is a recursive descent parser, which has the capability to predict which production
is to be used to replace the input string. The predictive parser does not suffer from backtracking.
To accomplish its tasks, the predictive parser uses a look-ahead pointer, which points to the next
input symbols. To make the parser back-tracking free, the predictive parser puts some constraints
on the grammar and accepts only a class of grammar known as LL(k) grammar.

Predictive parsing uses a stack and a parsing table to parse the input and generate a parse tree. Both
the stack and the input contains an end symbol $ to denote that the stack is empty and the input is
consumed. The parser refers to the parsing table to take any decision on the input and stack element
combination.

In recursive descent parsing, the parser may have more than one production to choose from for a
single instance of input, whereas in predictive parser, each step has at most one production to
choose. There might be instances where there is no production matching the input string, making
the parsing procedure to fail.

Program-1:
#include <stdio.h>
#include <string.h>

char input[100];
int pos = 0;

void S();
void A();

void error() {
printf("Error parsing input\n");
Compiler Design (3170701)
exit(1);
}

void match(char terminal) {


if (input[pos] == terminal)
pos++;
else
error();
}

void S() {
if (input[pos] == 'a') {
match('a');
A();
match('d');
} else {
error();
}
}

void A() {
if (input[pos] == 'b') {
match('b');
A();
} else if (input[pos] == 'c') {
match('c');
} else {
error();
}
}

int main() {
printf("Enter string: ");
scanf("%s", input);
S();
if (input[pos] == '\0') {
printf("Input is valid\n");
} else {
printf("Error: Incomplete parse\n");
}
return 0;
}

Program -2:
#include <stdio.h>
#include <string.h>

int lookahead = 0;
char input[100];

void match(char terminal) {


if (input[lookahead] == terminal)
lookahead++;
else {
Compiler Design (3170701)
printf("Syntax Error\n");
exit(1);
}
}

void E(); // E -> TE'


void T(); // T -> FT'
void E_prime(); // E' -> +TE' | ε
void T_prime(); // T' -> *FT' | ε
void F(); // F -> (E) | id

void E() {
T();
E_prime();
}

void E_prime() {
if (input[lookahead] == '+') {
match('+');
T();
E_prime();
}
}

void T() {
F();
T_prime();
}

void T_prime() {
if (input[lookahead] == '*') {
match('*');
F();
T_prime();
}
}

void F() {
if (input[lookahead] == '(') {
match('(');
E();
match(')');
} else if (input[lookahead] == 'i') {
match('i');
} else {
printf("Syntax Error\n");
exit(1);
}
}

int main() {
printf("Enter input string: ");
scanf("%s", input);
E();
if (input[lookahead] == '\0')
printf("Input is valid\n");
Compiler Design (3170701)
else
printf("Syntax Error\n");
return 0;
}

Observations and Conclusion:

Program -1:

The program is a simple checker to see if a string follows a specific pattern. The pattern is:
• The string must start with the letter 'a'.
• After 'a', there can be any number of the letter 'b'.
• Then, there must be the letter 'c'.
• Finally, the string must end with the letter 'd'.
So, valid strings include:
• "aAd"
• "abAd"
• "abbAd"
• "abcAd"
Invalid strings include:
• "ad"
• "abd"
• "acd"
• "bc"
• "abc"
The program checks the input string against this pattern and tells you if it's valid or not
Program-2:

The program is a simple checker to see if an arithmetic expression is valid. It checks if the expression
follows the rules of arithmetic, such as:
• Parentheses must be balanced.
• Operators must be used correctly (e.g., '+' between two numbers).
• Identifiers must be used correctly (e.g., 'i' can be used as a variable).
If the expression follows these rules, the program says it's valid. Otherwise, it says there's a syntax error.
Compiler Design (3170701)
Quiz:
1. What is a parser and state the Role of it?
2. Types of parsers? Examples to each.
3. What are the Tools available for implementation?
4. How do you calculate FIRST(),FOLLOW() sets used in Parsing Table construction?
5. Name the most powerful parser.
Compiler Design (3170701)

Experiment No - 06
Aim: Implement a program for constructing
a. Recursive Decent Parser (RDP)
b. LALR Parser

Date: Competency and Practical

Skills:
• Understanding of RDP and bottom up parsers and its role in compiler construction
• Ability to write acceptance of string through RDP and parsing of string using LALR
parsers for a given grammar
• Ability to develop RDP and LALR parser using bottom up approach

Relevant CO: CO2

Objectives:
By the end of this experiment, the students should be able to:
➢ Understand the RDP ,broad classification of bottom up parsers and its significance in
compiler construction
➢ Verifying whether the string is accepted for RDP, a given grammar is parsed using LR
parsers.
➢ Implement a RDP and LALR parser

Software/Equipment: C compiler

Theory:

❖ Recursive Descent Parser:

Recursive Descent Parser uses the technique of Top-Down Parsing without backtracking. It can be
defined as a Parser that uses the various recursive procedure to process the input string with no
backtracking. It can be simply performed using a Recursive language. The first symbol of the string
of R.H.S of production will uniquely determine the correct alternative to choose.
The major approach of recursive-descent parsing is to relate each non-terminal with a procedure.
The objective of each procedure is to read a sequence of input characters that can be produced by
the corresponding non-terminal, and return a pointer to the root of the parse tree for the non-
terminal. The structure of the procedure is prescribed by the productions for the equivalent non-
terminal.
The recursive procedures can be simply to write and adequately effective if written in a language
that executes the procedure call effectively. There is a procedure for each non-terminal in the
grammar. It can consider a global variable lookahead, holding the current input token and a
procedure match (Expected Token) is the action of recognizing the next token in the parsing process
and advancing the input stream pointer, such that lookahead points to the next token to be parsed.
Match () is effectively a call to the lexical analyzer to get the next token.
For example, input stream is a + b$.
lookahead == a
Compiler Design (3170701)
match()
lookahead == +
match ()
lookahead == b
……………………….
……………………….
In this manner, parsing can be done.

❖ LALR (1) Parsing:

LALR refers to the lookahead LR. To construct the LALR (1) parsing table, we use the
canonical collection of LR (1) items.
In the LALR (1) parsing, the LR (1) items which have same productions but different look
ahead are combined to form a single set of items
LALR (1) parsing is same as the CLR (1) parsing, only difference in the parsing table.
Example
S → AA
A → aA
A→b
Add Augment Production, insert '•' symbol at the first position for every production in G
and also add the look ahead.
S` → •S, $
S → •AA, $
A → •aA, a/b
A → •b, a/b
I0 State:
Add Augment production to the I0 State and Compute the ClosureL
I0 = Closure (S` → •S)
Add all productions starting with S in to I0 State because "•" is followed by the non-
terminal. So, the I0 State becomes
I0 = S` → •S, $
S → •AA, $
Add all productions starting with A in modified I0 State because "•" is followed by the
non-terminal. So, the I0 State becomes.
I0= S` → •S, $ S
→ •AA, $
A → •aA, a/b
A → •b, a/b
I1= Go to (I0, S) = closure (S` → S•, $) = S` → S•, $
I2= Go to (I0, A) = closure ( S → A•A, $ )
Add all productions starting with A in I2 State because "•" is followed by the non-
terminal. So, the I2 State becomes
I2= S → A•A, $
A → •aA, $
A → •b, $
I3= Go to (I0, a) = Closure ( A → a•A, a/b )
Add all productions starting with A in I3 State because "•" is followed by the non-
terminal. So, the I3 State becomes
Compiler Design (3170701)
I3= A → a•A, a/b
A → •aA, a/b
A → •b, a/b
Go to (I3, a) = Closure (A → a•A, a/b) = (same as I3)
Go to (I3, b) = Closure (A → b•, a/b) = (same as I4)
I4= Go to (I0, b) = closure ( A → b•, a/b) = A → b•, a/b
I5= Go to (I2, A) = Closure (S → AA•, $) =S → AA•, $
I6= Go to (I2, a) = Closure (A → a•A, $)
Add all productions starting with A in I6 State because "•" is followed by the non-
terminal. So, the I6 State becomes
I6 = A → a•A, $
A → •aA, $
A → •b, $
Go to (I6, a) = Closure (A → a•A, $) = (same as I6)
Go to (I6, b) = Closure (A → b•, $) = (same as I7)
I7= Go to (I2, b) = Closure (A → b•, $) = A → b•, $
I8= Go to (I3, A) = Closure (A → aA•, a/b) = A → aA•, a/b
I9= Go to (I6, A) = Closure (A → aA•, $) A → aA•, $
If we analyze then LR (0) items of I3 and I6 are same but they differ only in their
lookahead.
I3 = { A → a•A, a/b
A → •aA, a/b
A → •b, a/b
}
I6= { A → a•A, $
A → •aA, $
A → •b, $
}
Clearly I3 and I6 are same in their LR (0) items but differ in their lookahead, so we can
combine them and called as I36.
I36 = { A → a•A, a/b/$
A → •aA, a/b/$
A → •b, a/b/$
}
The I4 and I7 are same but they differ only in their look ahead, so we can combine them
and called as I47.
I47 = {A → b•, a/b/$}
The I8 and I9 are same but they differ only in their look ahead, so we can combine them
and called as I89.
I89 = {A → aA•, a/b/$}
Drawing DFA:

LALR (1) Parsing table:


Compiler Design (3170701)

Program-1:
#include <stdio.h>
#include <string.h>

char input[100];
int lookahead = 0;

void match(char terminal) {


if (input[lookahead] == terminal) {
lookahead++;
} else {
printf("Syntax Error\n");
exit(1);
}
}

void S() {
if (input[lookahead] == 'a') {
match('a');
S();
match('b');
}
}

int main() {
printf("Enter the string: ");
scanf("%s", input);
S();
if (input[lookahead] == '\0')
printf("Accepted\n");
else
printf("Syntax Error\n");
return 0;
}

Program-2:
ex6p2.l
%option noyywrap

%%

[0-9]+ { yylval = atoi(yytext); return NUMBER; }


[+\-*/] { return yytext[0]; }
\n { return '\n'; }
Compiler Design (3170701)
. { return yytext[0]; }

%%

ex6p2.y
%{
#include <stdio.h>
#include <stdlib.h>

void yyerror();
int yylex(void);
%}

/* Token declarations */
%token NUMBER

/* Define operator precedence and associativity */


%left '+' '-'
%left '*' '/'

/* Grammar rules */
%%
lines:
| lines expr '\n' { printf("Result: %d\n", $2); }
;

expr:
expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr { $$ = $1 / $3; }
| NUMBER { $$ = $1; }
;
%%

#include "lex.yy.c"

int main() {
printf("Enter an expression (Ctrl+D to exit):\n");
yyparse();
return 0;
}

void yyerror()
{}
Compiler Design (3170701)
Observations and Conclusion:

Program -1:

The program is a simple checker to see if a string follows a specific pattern. The pattern is:
• The string must start with the letter 'a'.
• Then, there can be any number of the letters 'a' and 'b', as long as there are always the same
number of each.
• Finally, the string must end with the letter 'b'.
So, valid strings include:
• "a"
• "ab"
• "aabbb"
• "aaabbbb"
Invalid strings include:
• "b"
• "abbb"
• "aabb"
• "aaabbbbbb"
The program checks the input string against this pattern and tells you if it's valid or not.

Program-2:

This program implements a simple calculator using LEX and YACC. The LEX file defines the
lexical rules for recognizing numbers and operators, while the YACC file specifies the grammar
rules for arithmetic expressions. The main function reads the input expression, parses it using
YACC, and prints the result. The yyerror function is a placeholder for handling syntax errors but is
currently empty. This program demonstrates the basic workflow of using LEX and YACC for
building a language parser and interpreter.
Compiler Design (3170701)
Quiz:

1. What do you mean by shift reduce parsing?


2. Provide broad classification of LR parsers.
3. Differentiate RDP and LALR parser.
4. How to do merging of itemsets?

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy