Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 49

EXPERIMENT NO: 01

1.EXPERIMENT VISION:

Write a C program to implement the concept of a two pass assembler.

2.OBJECTIVE:

To understand the concept of two pass assembler and construct a program for it.

3.THEORY:

Two pass assemblers

A two-pass assembler makes two passes over the input program. That is, it reads the
program twice. On the first pass, the assembler constructs the symbol table. On the second pass,
the complete symbol table is used to allow expressions to be evaluated without problems due to
forward references. If any symbol is not in the symbol table during an expression evaluation, it is
an error, not a forward reference.

FI
GURE 8.3 A block diagram of a two-pass assembler. Pass 1 produces a symbol table and a copy of the
input source program for pass 2. Pass 2 produces loader code and a listing.

Briefly, for a two-pass assembler, the first pass constructs the symbol table; the second pass
generates object code. This causes a major change in the basic flow of an assembler, and results
in another important data structure: the intermediate text. Since two passes are being made over
the input assembly language, it is necessary to save a copy of the program read for pass 1, to be
used in pass two. Notice that since the assembly listing includes the generated machine language
code, and the machine language code is not produced until pass 2, the assembly listing is not
produced until pass 2. This requires storing the entire input program, including comments, and so
forth, which are not needed by the assembler during pass 2.
The intermediate text can be stored in several ways. The best storage location would be in
main memory. However, since MIX memory is so small, this technique is not possible on the
MIX computer. On other machines, with more memory, this technique is sometimes used for
very small programs. Another approach, used for very small machines, is to simply require the
programmer to read his program into the computer twice, once for pass 1, and again for pass 2. A
PDP-8 assembler has used this approach, even going so far as to require the program be read in a
third time if a listing is desired.

A more common solution is to store the intermediate text on a secondary storage device,
such as tape, drum, or disk. During pass 1, the original program is read in, and copied to
secondary storage as the symbol table is constructed. Between passes, the device is repositioned
(rewound for tapes, or the head moved back to the first track for moving head disk or drum).
During pass 2, the program is read back from secondary storage for object code generation and
printing a listing. (Notice that precautions should be taken that the same tape is not used for both
storage of the intermediate text input for pass 2 and the storage of the object code produced as
output from pass 2 for the loader.)

The general flow of a two-pass assembler differs from that given above, in that now each
card must be processed twice, with different processing on each pass. It is also still necessary to
process each type of card differently, depending upon its opcode type. This applies to both pass 1
and pass 2.

For machine instructions, pass 1 processing involves simply defining the label (if any) and
incrementing the location counter by one. ALF and CON pseudo-instructions are handled in the
same way. ORIG statements must be handled exactly as we described above, however, in order
for the location counter to have the correct value for defining labels. Similarly, EQU statements
must also be treated on pass 1. This means that no matter what approach is used, no forward
references can be allowed in ORIG or EQU statements, since both are processed during pass 1.
The END statement needs to have its label defined and then should jump to pass 2 for further
processing.

During pass 2, EQU statements can be treated as comments. The label field can likewise be
ignored. ALF, CON, and machine instructions will be processed as described above, as will
the END statement.

The need to make two passes over the program can result in considerable duplication in
code and computation during pass 1 and pass 2. For example, on both passes, we need to find the
type of the opcode; on pass 1, to be able to treat ORIG, EQU, and END statements; on pass 2, for all
types. This can result in having to search the opcode table twice for each assembly language
statement. To prevent this, we need only save the index into the opcode table of the opcode (once
it is found during pass 1) with each instruction. Also, consider that since the operand "*" may be
used in the expressions evaluated during pass 2, it is necessary to duplicate during pass 2 the
efforts of pass 1 to define the location counter, unless we can simply store with each assembly
language statement the value of the location counter for that statement.

These considerations can result in extending the definition of the intermediate text to be
more than simply the input to pass 1. Each input assembly language statement can be a record
containing (at least) the following fields.

1. the input card image.

2. the index of the opcode into the opcode table.

3. the location counter value for this statement.

Additional fields may include error flags, a pointer to the column in which the operand starts (for
free-format assemblers), a line number, and so on; whatever is conveniently computed on pass 1
and needed on pass 2.

Even more can be computed during pass 1. For example, ALF and CON statements can be


completely processed during pass 1. The opcode field, field specification, and index field for a
machine instruction can be easily computed during pass 1, and most of the time the address field,
too, can be processed on pass 1. It is only the occasional forward reference which causes a
second pass to be needed. Most two-pass assemblers store a partially digested form of the input
source program as their intermediate text for pass 2.

Assemblers, like loaders, are almost always I/O-bound programs, since the time to read a
card generally far exceeds the time to assemble it. Thus, requiring two passes means an
assembler takes twice as long to execute as an assembler which only uses one pass.

4.SOURCECODE:

#include<stdio.h>
#include<string.h>
#include<conio.h>
void main()
{
char *code[9][4]={
{"PRG1","START","",""},
{"","USING","*","15"},
{"","L","",""},
{"","A","",""},
{"","ST","",""},
{"FOUR","DC","F",""},
{"FIVE","DC","F",""},
{"TEMP","DS","1F",""},
{"","END","",""}
};
char av[2],avail[15]={'N','N','N','N','N','N','N','N','N','N','N','N','N','N','N'};
int i,j,k,count[3],lc[9]={0,0,0,0,0,0,0,0,0},loc=0;

printf("----------------------------------------------------\n");
printf("LABEL\t\tOPCODE\n");
printf("----------------------------------------------------\n\n");
for(i=0;i<=8;i++)
{
for(j=0;j<=3;j++)
{
printf("%s\t\t",code[i][j]);
}
j=0;
printf("\n");
}
getch();
printf("-----------------------------------------------------");
printf("\nVALUES FOR LC : \n\n");
for(j=0;j<=8;j++)
{
if((strcmp(code[j][1],"START")!=0)&&(strcmp(code[j][1],"USING")!=0)&&(strcmp(code[j]
[1],"L")!=0))
lc[j]=lc[j-1]+4;
printf("%d\t",lc[j]);
}
printf("\n\nSYMBOL TABLE:\n----------------------------------------------------\n");
printf("SYMBOL\t\tVALUE\t\tLENGTH\t\tR/A");
printf("\n----------------------------------------------------\n");
for(i=0;i<9;i++)
{
if(strcmp(code[i][1],"START")==0)
{
printf("%s\t\t%d\t\t%d\t\t%c\n",code[i][0],loc,4,'R');
}
else if(strcmp(code[i][0],"")!=0)
{
printf("%s\t\t%d\t\t%d\t\t%c\n",code[i][0],loc,4,'R');
loc=4+loc;
}
else if(strcmp(code[i][1],"USING")==0){}
else
{loc=4+loc;}
}
printf("----------------------------------------------------");
printf("\n\nBASE TABLE:\n-------------------------------------------------------\n");
printf("REG NO\t\tAVAILIBILITY\tCONTENTS OF BASE TABLE");
printf("\n-------------------------------------------------------\n");
for(j=0;j<=8;j++)
{
if(strcmp(code[j][1],"USING")!=0)
{}
else
{
strcpy(av,code[j][3]);
}
}
count[0]=(int)av[0]-48;
count[1]=(int)av[1]-48;
count[2]=count[0]*10+count[1];
avail[count[2]-1]='Y';
for(k=0;k<16;k++)
{
printf(" %d\t\t %c\n",k,avail[k-1]);
}
printf("-------------------------------------------------------\n");
printf("Continue..??");
getchar();
printf("PASS2 TABLE:\n\n");
printf("LABEL\t\tOP1\t\tLC\t\t");
printf("\n----------------------------------------------------\n");
loc=0;
for(i=0;i<=8;i++)
{
for(j=0;j<=3;j++)
{
printf("%s\t\t",code[i][j]);
}
j=0;
printf("\n");
}
printf("-----------------------------------------------------");
getch();
}

5.OUTPUT:

----------------------------------------------------
LABEL OPCODE
----------------------------------------------------
PRG1 START
USING * 15
L
A
ST
FOUR DC F
FIVE DC F
TEMP DS 1F
END
-----------------------------------------------------
VALUES FOR LC :
0 0 0 4 8 12 16 20 24
SYMBOL TABLE:
----------------------------------------------------
SYMBOL VALUE LENGTH R/A
----------------------------------------------------
PRG1 0 4 R
FOUR 12 4 R
FIVE 16 4 R
TEMP 20 4 R
----------------------------------------------------
BASE TABLE:
-------------------------------------------------------
REG NO AVAILIBILITY CONTENTS OF BASE TABLE
-------------------------------------------------------
0
1 N
2 N
3 N
4 N
5 N
6 N
7 N
8 N
9 N
10 N
11 N
12 N
13 N
14 N
15 Y
-------------------------------------------------------
CONTINUE..??
PASS2 TABLE:

LABEL OP1 LC

----------------------------------------------------

PRG1 START

USING * 15

A
ST

FOUR DC F

FIVE DC F

TEMP DS 1F

END

-----------------------------------------------------

6.CONCLUSION:

Thus we have studied and implementation of two pass assembler.

7.DISCUSSION QUESTIONS:

1. What is use of two pass assembler?


______________________________________________________________________________
______________________________________________________________________________

2. Explain semantic analysis phase of Compiler?


______________________________________________________________________________
______________________________________________________________________________

8.FIELD OF APPLICATION:

We can use this two pass assembler program in designing compiler.

EXPERIMENT NO: 02
1.EXPERIMENT VISION:

Write a C program to implement the concept of macro processor.

2.OBJECTIVE:

To understand the concept of macro processor and construct a program for it.

3.THEORY:

A macro processor is a program that copies a stream of text from one place to another,
making a systematic set of replacements as it does so. Macro processors are often embedded in
other programs, such as assemblers and compilers. Sometimes they are standalone programs that
can be used to process any kind of text.

4.SOURCECODE:

#include<string.h>
#include<conio.h>
#include<stdlib.h>
#include<stdio.h>
#include<math.h>
void main()
{
char *opcode,*operand,*mac,*s,*s1;
FILE *f1,*f2;
clrscr();
f1=fopen("macroin.asm","r");
f2=fopen("macrout.c","w");
rewind(f1);
while(!feof(f1))
{
fscanf(f1,"%s",mac);
if(strcmp(mac,"MACRO")==0)
{
do
{
fscanf(f1,"%s",operand);
fscanf(f1,"%s",opcode);
}while(strcmp(opcode,"MEND")!=0);
}
if(strcmp(mac,"START")==0)
{
fprintf(f2,"\n%s",mac);
do
{
c:fscanf(f1,"%s",operand);
if(strcmp(operand,"M1")==0)
{
rewind(f1);
fscanf(f1,"%s%s%s%s",s,s1,opcode,operand);
l:fscanf(f1,"%s",opcode);
printf("\n%s",opcode);
if(strcmp(opcode,"ADD")==0)
{
fscanf(f1,"%s",operand);
fprintf(f2,"\n%s\t%s",opcode,operand);
fscanf(f1,"%s",opcode);
fprintf(f2,"\n%s",opcode);
exit (0);
}else
goto l;
}
fprintf(f2,"\t%s",operand);
fscanf(f1,"%s",opcode);
if(strcmp(opcode,"CALL")==0)
goto c;
else
fprintf(f2,"\n%s",opcode);
}while(strcmp(opcode,"END")==0);
}
}
fclose(f1);
fclose(f2);
getch();
}
5.OUTPUT:

START

LDA A

START

MOVE A,B

START

ADD A,B

END

6.CONCLUSION:

Thus we have studied and implementation of macro processor.


7.DISCUSSION QUESTIONS:

1. What is use of macro processor?


______________________________________________________________________________
______________________________________________________________________________

2. Explain syntax analysis Phase of Compiler?


______________________________________________________________________________
______________________________________________________________________________

8.FIELD OF APPLICATION:

We can use this macro processor program in designing compiler.


EXPERIMENT NO: 03

1.EXPERIMENT VISION:

Write a C program to implement the concept of Lexical Analysis.

2.OBJECTIVE:

To understand the concept of Lexical analysis by identify weather a expression contains


identifier or operator.

3.THEORY:

Lexical analysis is the first phase of a compiler. It takes the modified source code from
language preprocessors that are written in the form of sentences. The lexical analyzer breaks
these syntaxes into a series of tokens, by removing any whitespace or comments in the source
code.

If the lexical analyzer finds a token invalid, it generates an error. The lexical analyzer works
closely with the syntax analyzer. It reads character streams from the source code, checks for
legal tokens, and passes the data to the syntax analyzer when it demands.

4.SOURCECODE:

#include <stdio.h>
#include <conio.h>
#include <ctype.h>
#include <string.h>
#define MAX 30
void main()
{
    char str[MAX];
    char *input, c;
    int point;
    int state = 0;
    int i = 0, j, startid = 0, endid, startcon, endcon;
    point = 0;
    for (j = 0; j < MAX; j++)
        str[j] = NULL; // Initialise NULL
    printf("\t*** Program on Lexical Analysis ***");
    printf("\n\nEnter the string: ");
    gets(str); // Accept input string
    str[strlen(str)] = ' ';
    printf("Analysis:");
    while (str[i] != NULL)
  {
        while (str[i] == ' ') // To eliminate spaces
            i++;
        switch (state)
    {
        case 0:
            if (str[i] == 'i')
                state = 1; // if
            else if (str[i] == 'w')
                state = 3; // while
            else if (str[i] == 'd')
                state = 8; // do
            else if (str[i] == 'e')
                state = 10; // else
            else if (str[i] == 'f')
                state = 14; // for
            else if (isalpha(str[i]) || str[i] == '_')
      {
                state = 17;
                startid = i;
            } // identifiers
            else if (str[i] == '<')
                state = 19;
            else if (str[i] == '-')
                state = 30;
            break;
        case 1:
            if (str[i] == 'f')
                state = 2;
            else
      {
                state = 17;
                startid = i - 1;
                i--;
      }
            break;
        case 2:
            if (str[i] == '(' || str[i] == NULL)
      {
                printf("\nif : Keyword");
                state = 0;
                i--;
      }
            else
      {
                state = 17;
                startid = i - 2;
                i--;
      }
            break;
        case 3:
            if (str[i] == 'h')
                state = 4;
            else
      {
                state = 17;
                startid = i - 1;
                i--;
      }
            break;
        case 4:
            if (str[i] == 'i')
                state = 5;
            else
      {
                state = 17;
                startid = i - 2;
                i--;
      }
            break;
        case 5:
            if (str[i] == 'l')
                state = 6;
            else
      {
                state = 17;
                startid = i - 3;
                i--;
      }
            break;
        case 6:
            if (str[i] == 'e')
                state = 7;
            else
      {
                state = 17;
                startid = i - 4;
                i--;
      }
            break;
        case 7:
            if (str[i] == '(' || str[i] == NULL)
      {
                printf("\nwhile: Keyword");
                state = 0;
                i--;
      }
            else
      {
                state = 17;
                startid = i - 5;
                i--;
      }
            break;
        case 8:
            if (str[i] == 'o')
                state = 9;
            else
      {
                state = 17;
                startid = i - 1;
                i--;
      }
            break;
        case 9:
            if (str[i] == '{' || str[i] == ' ' || str[i] == NULL || str[i] == '(')
      {
                printf("\ndo : Keyword");
                state = 0;
                i--;
      }
            break;
        case 10:
            if (str[i] == 'l')
                state = 11;
            else
      {
                state = 17;
                startid = i - 1;
                i--;
      }
            break;
        case 11:
            if (str[i] == 's')
                state = 12;
            else
      {
                state = 17;
                startid = i - 2;
                i--;
      }
            break;
        case 12:
            if (str[i] == 'e')
                state = 13;
            else
      {
                state = 17;
                startid = i - 3;
                i--;
      }
            break;
        case 13:
            if (str[i] == '{' || str[i] == NULL)
      {
                printf("\nelse: Keyword");
                state = 0;
                i--;
      }
            else
      {
                state = 17;
                startid = i - 4;
                i--;
      }
            break;
        case 14:
            if (str[i] == 'o')
                state = 15;
            else
      {
                state = 17;
                startid = i - 1;
                i--;
      }
            break;
        case 15:
            if (str[i] == 'r')
                state = 16;
            else
      {
                state = 17;
                startid = i - 2;
                i--;
      }
            break;
        case 16:
            if (str[i] == '(' || str[i] == NULL)
      {
                printf("\nfor   : Keyword");
                state = 0;
                i--;
      }
            else
      {
                state = 17;
                startid = i - 3;
                i--;
                break;
            case 17:
                if (isalnum(str[i]) || str[i] == '_')
        {
                    state = 18;
                    i++;
        }
                else if (str[i] == NULL || str[i] == '<' || str[i] == '>' || str[i] == '(' || str[i] ==
')' || str[i] == ';' || str[i] == '=' || str[i] == '+' || str[i] == '-')
                    state = 18;
                i--;
                break;
            case 18:
                if (str[i] == NULL || str[i] == '<' || str[i] == '>' || str[i] == '(' || str[i] == ')' ||
str[i] == ';' || str[i] == '=' || str[i] == '+' || str[i] == '-')
        {
                    endid = i - 1;
                    printf("");
                    for (j = startid; j <= endid; j++)
                        printf("\n%c", str[j]);
                    printf("    : Identifier");
                    state = 0;
                    i--;
        }
                break;
            case 19:
                if (str[i] == '=')
                    state = 20;
                else if (isalnum(str[i]) || str[i] == '_')
        {
                    printf("\n< : Relational operator");
                    i--;
                    state = 0;
        }
                break;
            case 20:
                if (isalnum(str[i]) || str[i] == '_')
        {
                    printf("\n<=    : Relational operator");
                    i--;
                    state = 0;
        }
                break;
            case 21:
                if (str[i] == '=')
                    state = 22;
                else if (isalnum(str[i]) || str[i] == '_')
        {
                    printf("\n> : Relational operator");
                    i--;
                    state = 0;
        }
                break;
            case 22:
                if (isalnum(str[i]) || str[i] == '_')
        {
                    printf("\n>=    : Relational operator");
                    i--;
                    state = 0;
        }
                break;
            case 23:
                if (str[i] == '=')
                    state = 24;
                else
        {
                    printf("\n=     : Assignment operator");
                    i--;
                    state = 0;
        }
                break;
                goto END;
      }
            i++;
    }
        printf("\nEnd of program");
    END:
        getch();
  }
}

5.OUTPUT:

6.CONCLUSION:

Thus we have studied and implementation of Lexical analysis.

7.DISCUSSION QUESTIONS:

3. What is use of Lexical analysis?


______________________________________________________________________________
______________________________________________________________________________

4. Explain Lexical Phase of Compiler?


______________________________________________________________________________
______________________________________________________________________________

8.FIELD OF APPLICATION:

We can use this Lexical analysis program in designing compiler.


EXPERIMENT NO: 04

1.EXPERIMENT VISION:

Write a C program to implement the top down parser.

2.OBJECTIVE:

To understand the concept of top down parser and construct a program for it.

3.THEORY:

Top-down Parsing
When the parser starts constructing the parse tree from the start symbol and then tries to
transform the start symbol to the input, it is called top-down parsing.

 Recursive descent parsing: It is a common form of top-down parsing. It is called


recursive as it uses recursive procedures to process the input. Recursive descent parsing
suffers from backtracking.
 Backtracking : It means, if one derivation of a production fails, the syntax analyzer
restarts the process using different rules of same production. This technique may process
the input string more than once to determine the right production.

4.SOURCECODE:

#include <string.h>
#include <conio.h>
char a[10];
int top = -1, i;
void error()
{
printf("Syntax Error");
}
void push(char k[]) // Pushes The Set Of Characters on to the Stack
{
for (i = 0; k[i] != '\0'; i++)
{
if (top < 9)
a[++top] = k[i];
}
}
char TOS() // Returns TOP of the Stack
{
return a[top];
}
void pop() // Pops 1 element from the Stack
{
if (top >= 0)
a[top--] = '\0';
}
void display() // Displays Elements Of Stack
{
for (i = 0; i <= top; i++)
printf("%c", a[i]);
}
void display1(char p[], int m) // Displays The Present Input String
{
int l;
printf("\t");
for (l = m; p[l] != '\0'; l++)
printf("%c", p[l]);
}
char *stack()
{
return a;
}
int main()
{
char ip[20], r[20], st, an;
int ir, ic, j = 0, k;
char t[5][6][10] = {"$", "$", "TH", "$", "TH", "$",
"+TH", "$", "e", "e", "$", "e",
"$", "$", "FU", "$", "FU", "$",
"e", "*FU", "e", "e", "$", "e",
"$", "$", "(E)", "$", "i", "$"};
// clrscr();
printf("\nEnter any String(Append with $)");
gets(ip);
printf("Stack\tInput\tOutput\n\n");
push("$E");
display();
printf("\t%s\n", ip);
for (j = 0; ip[j] != '\0';)
{
if (TOS() == an)
{
pop();
display();
display1(ip, j + 1);
printf("\tPOP\n");
j++;
}
an = ip[j];
st = TOS();
if (st == 'E')
ir = 0;
else if (st == 'H')
ir = 1;
else if (st == 'T')
ir = 2;
else if (st == 'U')
ir = 3;
else if (st == 'F')
ir = 4;
else
{
error();
break;
}
if (an == '+')
ic = 0;
else if (an == '*')
ic = 1;
else if (an == '(')
ic = 2;
else if (an == ')')
ic = 3;
else if ((an >= 'a' && an <= 'z') || (an >= 'A' && an <= 'Z'))
{
ic = 4;
an = 'i';
}
else if (an == '$')
ic = 5;
strcpy(r, strrev(t[ir][ic]));
strrev(t[ir][ic]);
pop();
push(r);
if (TOS() == 'e')
{
pop();
display();
display1(ip, j);
printf("\t%c->%c\n", st, 238);
}
else
{
display();
display1(ip, j);
printf("\t%c->%s\n", st, t[ir][ic]);
}
if (TOS() == '$' && an == '$')
break;
if (TOS() == '$')
{
error();
break;
}
}
k = strcmp(stack(), "$");
if (k == 0 && i == strlen(ip))
printf("\n Given String is accepted");
else
printf("\n Given String is not accepted");
return 0;
}
5.OUTPUT:

6.CONCLUSION:

Thus we have studied and implementation of top down parser.

7.DISCUSSION QUESTIONS:

5. What is use of top down parser?


______________________________________________________________________________
______________________________________________________________________________

6. Explain productions used in top down parser?


______________________________________________________________________________
______________________________________________________________________________

8.FIELD OF APPLICATION:

We can use this top down parser technique in designing compiler.


EXPERIMENT NO: 5

1. EXPERIMENT VISION:

Write a C program for the implementation of symbol table with functions to create, insert,
modify, search and display.

2. OBJECTIVE:

To understand the concept of symbol table and display, search, delete, modify the
contents of table.

3. THEORY:

Symbol table is a data structure containing record for each identifier or which field or
attribute of identifier with data structure allows us to find. The record for each identifier quickly
store data. An identifier in source program is detected by lexical analyzer or the identifier is
entered into symbol:

7. Each type name and its definition

8. Foreach variable name and its type in the variable is an array it also store dimension
information.

9. For each constant name.

10. For its function and procedure for its formal parameter list.
Algorithm:
1. Start the program for performing insert, display, delete, search and modify option
In symbol table
2. Define the structure of the Symbol Table
3. Enter the choice for performing the operations in the symbol Table
4. If the entered choice is 1, search the symbol table for the symbol to be inserted. If
the symbol is already present, it displays “Duplicate Symbol”. Else, insert the
symbol and the corresponding address in the symbol table.
5. If the entered choice is 2, the symbols present in the symbol table are displayed.
6. If the entered choice is 3, the symbol to be deleted is searched in the symbol
table. If it is not found in the symbol table it displays “Label Not found”. Else, the
symbol is deleted.
7. If the entered choice is 5, the symbol to be modified is searched in the symbol
table. The label or address or both can be modified.
4. Source Code:-

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
// #include <alloc.h>
#include <string.h>
#define null 0
int size = 0;
void insert();
void del();
int search(char lab[]);
void modify();
void display();
struct symbtab
{
    char label[10];
    int addr;
    struct symtab *next;
};
struct symbtab *first, *last;
void main()
{
    int op;
    int y;
    char la[10];
    // clrscr();
    do
  {
        printf("\nSYMBOL TABLE IMPLEMENTATION\n");
        printf("1. INSERT\n");
        printf("2. DISPLAY\n");
        printf("3. DELETE\n");
        printf("4. SEARCH\n");
        printf("5. MODIFY\n");
        printf("6. END\n");
        printf("Enter your option : ");
        scanf("%d", &op);
        switch (op)
    {
        case 1:
            insert();
            display();
            break;
        case 2:
            display();
            break;
        case 3:
            del();
            display();
            break;
        case 4:
            printf("Enter the label to be searched : ");
            scanf("%s", la);
            y = search(la);
            if (y == 1)
      {
                printf("The label is already in the symbol Table");
      }
            else
      {
                printf("The label is not found in the symbol table");
      }
            break;
        case 5:
            modify();
            display();
            break;
        case 6:
            break;
    }
    } while (op < 6);
    getch();
}
void insert()
{
    int n;
    char l[10];
    printf("Enter the label : ");
    scanf("%s", l);
    n = search(l);
    if (n == 1)
  {
        printf("The label already exists. Duplicate cantbe inserted\n");
  }
    else
  {
        struct symbtab *p;
        p = malloc(sizeof(struct symbtab));
        strcpy(p->label, l);
        printf("Enter the address : ");
        scanf("%d", &p->addr);
        p->next = null;
        if (size == 0)
    {
            first = p;
            last = p;
    }
        else
    {
            last->next = p;
            last = p;
    }
        size++;
  }
}
void display()
{
    int i;
    struct symbtab *p;
    p = first;
    printf("LABEL\tADDRESS\n");
    for (i = 0; i < size; i++)
  {
        printf("%s\t%d\n", p->label, p->addr);
        p = p->next;
  }
}
int search(char lab[])
{
    int i, flag = 0;
    struct symbtab *p;
    p = first;
    for (i = 0; i < size; i++)
  {
        if (strcmp(p->label, lab) == 0)
    {
            flag = 1;
    }
        p = p->next;
  }
    return flag;
}
void modify()
{
    char l[10], nl[10];
    int add, choice, i, s;
    struct symbtab *p;
    p = first;
    printf("What do you want to modify?\n");
    printf("1. Only the label\n");
    printf("2. Only the address of a particular label\n");
    printf("3. Both the label and address\n");
    printf("Enter your choice : ");
    scanf("%d", &choice);
    switch (choice)
  {
    case 1:
        printf("Enter the old label\n");
        scanf("%s", l);
        printf("Enter the new label\n");
        scanf("%s", nl);
        s = search(l);
        if (s == 0)
    {
            printf("NO such label");
    }
        else
    {
            for (i = 0; i < size; i++)
      {
                if (strcmp(p->label, l) == 0)
        {
                    strcpy(p->label, nl);
        }
                p = p->next;
      }
    }
        break;
    case 2:
        printf("Enter the label whose address is to modified\n");
        scanf("%s", l);
        printf("Enter the new address\n");
        scanf("%d", &add);
        s = search(l);
        if (s == 0)
    {
            printf("NO such label");
    }
        else
    {
            for (i = 0; i < size; i++)
      {
                if (strcmp(p->label, l) == 0)
        {
                    p->addr = add;
        }
                p = p->next;
      }
    }
        break;
    case 3:
        printf("Enter the old label : ");
        scanf("%s", l);
        printf("Enter the new label : ");
        scanf("%s", nl);
        printf("Enter the new address : ");
        scanf("%d", &add);
        s = search(l);
        if (s == 0)
    {
            printf("NO such label");
    }
        else
    {
            for (i = 0; i < size; i++)
      {
                if (strcmp(p->label, l) == 0)
        {
                    strcpy(p->label, nl);
                    p->addr = add;
        }
                p = p->next;
      }
    }
        break;
  }
}
void del()
{
    int a;
    char l[10];
    struct symbtab *p, *q;
    p = first;
    printf("Enter the label to be deleted\n");
    scanf("%s", l);
    a = search(l);
    if (a == 0)
  {
        printf("Label not found\n");
  }
    else
  {
        if (strcmp(first->label, l) == 0)
    {
            first = first->next;
    }
        else if (strcmp(last->label, l) == 0)
    {
            q = p->next;
            while (strcmp(q->label, l) != 0)
      {
                p = p->next;
                q = q->next;
      }
            p->next = null;
            last = p;
    }
        else
    {
            q = p->next;
            while (strcmp(q->label, l) != 0)
      {
                p = p->next;
                q = q->next;
      }
            p->next = q->next;
    }
        size--;
  }
}
5. OUTPUT:
6.CONCLUSION:
Thus we have studied and implementation of symbol table.

7.DISCUSSION QUESTIONS:

11. What is use of Symbol Table?


______________________________________________________________________________
______________________________________________________________________________

12. Explain LEX & YACC?


______________________________________________________________________________
______________________________________________________________________________

8.FIELD OF APPLICATION:

We can use this symbol table program in designing compiler.


EXPERIMENT NO.6

Implementation of code generation phase of compiler


Phases of Compiler Design

Compiler operates in various phases each phase transforms the source program from one
representation to another. Every phase takes inputs from its previous stage and feeds its output
to the next phase of the compiler.
There are 6 phases in a compiler. Each of this phase help in converting the high-level langue the
machine code.

The phases of a compiler are:


1. Lexical analysis
2. Syntax analysis
3. Semantic analysis
4. Intermediate code generator
5. Code optimizer
6. Code generator

Phases of Compiler

All these phases convert the source code by dividing into tokens, creating parse trees, and
optimizing the source code by different phases.

Phase 1: Lexical Analysis


Lexical Analysis is the first phase when compiler scans the source code. This process can be left
to right, character by character, and group these characters into tokens.
Here, the character stream from the source program is grouped in meaningful sequences by
identifying the tokens. It makes the entry of the corresponding tickets into the symbol table and
passes that token to next phase.
The primary functions of this phase are:
 Identify the lexical units in a source code
 Classify lexical units into classes like constants, reserved words, and enter them in
different tables. It will Ignore comments in the source program
 Identify token which is not a part of the language
Example:

x = y + 10

Tokens

X identifier

= Assignment operator

Y identifier

+ Addition operator

10 Number

Phase 2: Syntax Analysis

Syntax analysis is all about discovering structure in code. It determines whether or not a text
follows the expected format. The main aim of this phase is to make sure that the source code was
written by the programmer is correct or not.

Syntax analysis is based on the rules based on the specific programing language by constructing
the parse tree with the help of tokens. It also determines the structure of source language and
grammar or syntax of the language.

Here, is a list of tasks performed in this phase:

 Obtain tokens from the lexical analyzer


 Checks if the expression is syntactically correct or not
 Report all syntax errors
 Construct a hierarchical structure which is known as a parse tree

Example

Any identifier/number is an expression

If x is an identifier and y+10 is an expression, then x= y+10 is a statement.


Consider parse tree for the following example

(a+b)*c

In Parse Tree

 Interior node: record with an operator filed and two files for children
 Leaf: records with 2/more fields; one for token and other information about the token
 Ensure that the components of the program fit together meaningfully
 Gathers type information and checks for type compatibility
 Checks operands are permitted by the source language

Phase 3: Semantic Analysis

Semantic analysis checks the semantic consistency of the code. It uses the syntax tree of the
previous phase along with the symbol table to verify that the given source code is semantically
consistent. It also checks whether the code is conveying an appropriate meaning.

Semantic Analyzer will check for Type mismatches, incompatible operands, a function called
with improper arguments, an undeclared variable, etc.

Functions of Semantic analyses phase are:

 Helps you to store type information gathered and save it in symbol table or syntax tree
 Allows you to perform type checking
 In the case of type mismatch, where there are no exact type correction rules which satisfy
the desired operation a semantic error is shown
 Collects type information and checks for type compatibility
 Checks if the source language permits the operands or not

Example
float x = 20.2;
float y = x*30;

In the above code, the semantic analyzer will typecast the integer 30 to float 30.0 before
multiplication

Phase 4: Intermediate Code Generation

Once the semantic analysis phase is over the compiler, generates intermediate code for the target
machine. It represents a program for some abstract machine.

Intermediate code is between the high-level and machine level language. This intermediate code
needs to be generated in such a manner that makes it easy to translate it into the target machine
code.

Functions on Intermediate Code generation:

 It should be generated from the semantic representation of the source program


 Holds the values computed during the process of translation
 Helps you to translate the intermediate code into target language
 Allows you to maintain precedence ordering of the source language
 It holds the correct number of operands of the instruction

Example

For example,

total = count + rate * 5

Intermediate code with the help of address code method is:

t1 := int_to_float(5)
t2 := rate * t1
t3 := count + t2
total := t3
Phase 5: Code Optimization

The next phase of is code optimization or Intermediate code. This phase removes unnecessary
code line and arranges the sequence of statements to speed up the execution of the program
without wasting resources. The main goal of this phase is to improve on the intermediate code to
generate a code that runs faster and occupies less space.
The primary functions of this phase are:

 It helps you to establish a trade-off between execution and compilation speed


 Improves the running time of the target program
 Generates streamlined code still in intermediate representation
 Removing unreachable code and getting rid of unused variables
 Removing statements which are not altered from the loop

Example:

Consider the following code

a = intofloat(10)
b=c*a
d=e+b
f=d

Can become

b =c * 10.0
f = e+b
Phase 6: Code Generation

Code generation is the last and final phase of a compiler. It gets inputs from code optimization
phases and produces the page code or object code as a result. The objective of this phase is to
allocate storage and generate relocatable machine code.

It also allocates memory locations for the variable. The instructions in the intermediate code are
converted into machine instructions. This phase coverts the optimize or intermediate code into
the target language.

The target language is the machine code. Therefore, all the memory locations and registers are
also selected and allotted during this phase. The code generated by this phase is executed to take
inputs and generate expected outputs.

Example:

a = b + 60.0

Would be possibly translated to registers.

MOVF a, R1
MULF #60.0, R2
ADDF R1, R2
EXPERIMENT NO: 07

1.EXPERIMENT VISION:

Write C program to count no. of vowels in words.

2.OBJECTIVE:

 To understand LEX & YACC program structure.


 To how to recognize vowels in word.

3.THEORY:

The letters A, E, I, O, and U are called vowels. The other letters in the alphabet are
called consonants.

A vowel is classified as a speech sound produced by a comparatively open configuration of the


vocal tract, with vibration of the vocal cords but without audible friction. 

4.SOURCECODE:

#include <stdio.h>

#include <string.h>

/* main: main loop for counting vowels in word */

int main(void)

size_t i, j, word_len, vowels_len;

int vowel_count;

char word[21]; /* 20 + '\0' */

static const char vowels[] = "aeiouaeiou";

printf("enter a word: ");

scanf("%20s", word);
/* initialise variables */

word_len = strlen(word);

vowels_len = strlen(vowels);

vowel_count = 0;

for (i = 0; i < word_len; ++i)

{ /* loop through word */

for (j = 0; j < vowels_len; ++j)

{ /* loop through vowels */

if (word[i] == vowels[j])

++vowel_count;

break;

printf("\nThe no. of vowels in '%s' is %d\n", word, vowel_count);

/* indicate success */

return 0;

5.OUTPUT:
6.CONCLUSION:

Thus we have studied and implementation of above experiment.

7.DISCUSSION QUESTIONS:

1. What is use of YACC?


______________________________________________________________________________
______________________________________________________________________________

2.Explain Syntax Phase of Compiler?


______________________________________________________________________________
______________________________________________________________________________

8.FIELD OF APPLICATION:

We can use this Lexical analysis program in designing compiler.

You might also like