Sunteți pe pagina 1din 16

Universitatea Tehnica a Moldovei

Filiera Francofona Informatica

Catedra ATI
Limbaje Formale shi Proiectarea Compilatorului

Tema : Analizatorul lexical.


Varianta 8

A efectuat: studentul grupei FI-071:


Negrivoda Serghei

A verificat : lector superior:


Marusic Galina

1
Chisinau 2009

Cuprinsul proiectului:
Varianta 8

1. Foaia de titlu
2. Scopul lucrării
3. Descrierea limbajului dat
4. Notiuni teoretice.
5. Rezolvarea sarcinii
6. Listingul programului
7. Concluzie

2
Scopul lucrarii:
1. În descrierea neformală a unui limbaj dat să se evidenţieze lexemele.
Pentru fiecare tip de lexemă să se construiască un automat finit, care
acceptă lexeme corecte.
2. Determinaţi dacă automatele construite sunt deterministe.
3. Dacă automatul finit nu este detrminist, să se construiască pentru el un
automat finit determinist echivalent.
4. Construiţi schema analizatorului lexical pentru limbajul dat.
5. Testaţi lucrul analizatorului lexical pe 5 şiruri de intrare corecte şi 3
şiruri, care conţin lexeme incorecte sau simboluri neacceptate de limbaj,
construind pentru aceste şiruri:
 Vectorul sintactic;
 Vectorul semantic;
 Completarea tabelului pentru fiecare tip de lexemă.
6. Elaboraţi un program pentru analizatorul lexical şi demonstraţi lucrul
programului pentru şirurile de intrare construite.

3
Descrierea limbajului dat:
Identificatorul in limbajul FORTRAN se defineşte ca o secvenţă din litere şi
cifre, care începe în mod obligatoriu cu o literă. Lungimea identificatorului
nu trebuie să depăşească 6 simboluri. Constantele sunt numere întregi,
reale şi cu precizie dublă. Numerele reale se scriu în forma obişnuită cu
punct zecimal. Aceste numere pot fi urmate de un exponent care are forma
E <semn>< număr întreg>. Semnul poate fi omis. Numărul de precizie
dublă se deosebeşte de cel real doar prin prezenţa obligatorie a
exponentului, indicat prin litera D în loc de E.
Textele propuse spre analizare se definesc cu ajutorul producţiilor:
<program> → <listă de instrucţiuni>
<listă de instrucţiuni> → <instrucţiune>
<listă de instrucţiuni> → <instrucţiune> <listă de instrucţiuni>
<instrucţiune> → <identificator> = <expresie>
<instrucţiune> → <etichetă>:<identificator>= <expresie>
<etichetă> → <număr întreg>
<expresie> →<număr>
<expresie> →(<expresie>)
<expresie> →<identificator>
<expresie> →<expresie> <operaţie> <expresie>
<operaţie> → +
<operaţie> → −
<operaţie> → /
<operaţie> → *
<operaţie> → **
De menţionat că eticheta poate ocupa poziţiile 1-5 în rînd, iar instrucţiunia
7-72, rîndurile avînd lungimea maximă 80 de simboluri. Spaţiile libere se
ignorează.

4
Notiuni teoretice:

Limbajul Fortran este decanul de varsta al limbajelor de larga folosinta . A


aparut in 1956 si isi datoreaza numele prescurtarii cuvintelor : FORmula
TRANslation ( Traducere de formule ). Initial reprezenta un limbaj orientat
pe calcule stiintifice avand definite concepte precum : matrice , functii
trigonometrice , numere reale in dubla precizie . Versiunile ulterioare care au
cunoscut o mare popularitate au extins posibilitatile limbajului
trasformandu-l intr-un limbaj eficient , de uz general .In prezent exista
pentru IBM-PC doua implementari mai importante ale limbajului : Microsoft
Fortran , Fortran for Windows .

Desi nu poate fi considerat „ depasit „ din punct de vedere conceptual ( este


un limbaj algoritmic – structurat ) este neindicata folosirea lui datorita
absentei unor medii de programare performante si pentru ca tendinta
actuala ii este defavorabila .

Primul compilator FORTRAN a fost dezvoltat pentru IBM 704 în 1954–57 de


o echipă IBM condusă de John W. Backus. Acesta a fost un compilator de
optimizare, deoarece autorii considerau că nimeni nu ar fi folosit limbajul
dacă performanţele sale nu ar fi fost comparabile cu Assemblerul.

Limbajul a fost adoptat pe scară largă de către oamenii de ştiinţă pentru


scrierea programelor ce foloseau numere în mod intensiv, fapt ce a încurajat
autorii de compilatoare să producă soft-ul lor în aşa fel încât să genereze
cod mai rapid. În special includerea unui tip de date numeric complex în
limbajul FORTRAN l-a făcut potrivit pentru folosirea în ştiinţa
computaţională.

Numeroase standarde ale limbajului au apărut: FORTRAN II în 1958,


FORTRAN IV în 1961, FORTRAN 66 în 1966, FORTRAN 77 în 1977 ,
Fortran 90 în 1990, Fortran 95 în 1995, şi Fortran 2003 în 2003. Fortran III
a fost creat în 1958, lăsând posibilitatea includerii de cod asamblare de tip
Inline în programele sale; dar nu a fost niciodată dat spre folosinţă deoarece
conceptul de portabilitate al unui limbaj de nivel înalt ar fi fost pierdut.

5
Interfaţa analizorului lexical
Dupa cum se ştie analizorul lexical funcţioneză ca un modul in cadrul unui
compilator sau a oricărui program care realizează prelucrări ce necesită
identificarea unor şiruri ce pot fi descrise de o gramatică regulată. In
funcţionarea sa analizorul lexical interacţionează cu celelalte componente
ale compilatorului prin intermediul unei interfeţe specifice :

Rolul analizei lexicale este de a traduce textul programului intr-o secvenţă


de atomi lexicali. In acest mod se obţine un "text" mai uşor de prelucrat de
către celelalte componente ale compilatorului, deoarece se realizează o
abstractizare a unei infinităţi de şiruri de intrare in atomi de tip -
identificatori, constante, etc. De asemenea prin eliminarea blancurilor şi a
altor separatori irelevanţi (ca de exemplu comentarii), textul prelucrat se
poate reduce drastic. De cele mai multe ori analiza lexicală realizează şi alte
activitati auxiliare ca de exemplu păstrarea evidenţei numărului de linii
citite. O astfel de informaţie este foarte utilă pentru construirea mesajelor de
eroare. Cand se proiectează un analizor lexical se pune in general problema

6
care este nivelul de complexitate al atomilor lexicali consideraţi. De exemplu
in cazul in care un limbaj utilizează numere complexe, se pune problema
dacă analizorul lexical va fi cel care va recunoaşte o constantă de forma:
(<real>, <real>)
producand un atom lexical corespunzător sau recunoaşterea unei astfel de
constante ramane in sarcina nivelului analizei sintactice. In general un
analizor lexical este specificat sub forma :
p1 {actiune 1}
p2 {actiune 2}
...
pn {actiune n}
unde pi este o expresie regulata, iar actiune i este o secvenţă de operaţii care
se execută pentru fiecare subşir care corespunde modelului oferit de pi.
Dacă in tabela nu există şirul căutat rezultatul este 0.Pentru cuvintele cheie
de obicei se face o tratare speciala, de exemplu se poate initializa tabela de
simboli cu intrari corespunzătoare tuturor cuvintelor cheie din limbajul
respective executand apeluri de forma :
insert_s("if", cod_if);
insert_s("else", cod_else);
etc. inainte de a se incepe execuţia efectivă a compilatorului. In acest caz
recunoaşterea cuvintelor cheie se va face apoi intr-un mod similar cu a
oricarui alt identificator. Se observă deci de ce in majoritatea limbajelor de
programare cuvintele cheie nu pot să fie utilizate ca nume cu altă
semnificaţie. O astfel de tratare are avantajul ca in cazul in care se face o
declaraţie de tip de forma:
typedef int intreg;
după introducerea in tabela de simboli a cuvintului intreg de către analizorul
lexical, analizorul sintactic va putea să completeze apoi intrarea in tabela de
simboli cu informaţiile corespunzătoare numelui unui tip. In acest mod in
următoarele intalniri ale acestui cuvant analizorul lexical va putea să
transmită ca ieşire atomul lexical corespunzător unui nume de tip.In cazul
constantelor analizorul lexical realizează recunoaşterea acestora şi
memorează valorile corespunzătoare in tabela de constante pentru a permite
utilizarea ulterioară in cadrul generării de cod. Analizorul sintactic apelează
de obicei analizorul lexical ca pe o funcţie care are ca valoare codul
atomului lexical recunoscut de către analizorul lexical. Se observă ca in

7
acest caz utilizarea unei proceduri de tip ungetc() nu este suficientă. In
general o colecţie de funcţii care asigură citirea textului sursă pentru
analizorul lexical trebuie să satisfacă următoarele condiţii:
• funcţiile trebuie să fie cat mai rapide, realizand un număr minim de
copieri pentru caracterele parcurse;
• existenţa unui mecanism care să permită examinarea unor caractere in
avans şi revenirea pe şirul de intrare;
• să admită atomi lexicali suficient de lungi;
Pentru a obţine o utilizare eficientă a operaţiilor legate de accesul la disc
este necesar ca dimensiunea bufferuluisă fie adaptată modului de alocare a
spatiului pe disc. Astfel, pentru sistemul de operare MS-DOS utilizarea unui
buffer mai mic decat 512 octeţi nu are nici o justificare (o operaţie de citire
va citii cel puţin un sector de pe disc). De preferat este ca bufferul să fie un
multiplu al unităţii de alocare a spatiului pe disc.
O alternativă de specificare a limbajelor constă în definirea unui algoritm
care să răspundă „Da” în cazul în care un şir x aparţine limbajului şi „Nu”
dacă şirul nu aparţine limbajului. Unitatea de comandă are un „program
cablat" care impune o anumită funcţionare a maşinii. Funcţionarea (sau
evoluţia) este o secvenţă de „mişcări". Mişcarea constă dintr-o modificare a
configuraţiei maşinii de către o operaţie. Vom înţelege prin configuraţie
ansamblul stărilor componentelor maşinii.
Mişcarea se poate defini printr-o pereche formată din configuraţia din care
pleacă maşina şi configuraţia
în care ajunge in urma, mişcării.
Banda de intrare

Cap de citire
Unitatea de comandă

memorie

Asemenea „modele fizice''sînt intuitive şi ele stau la baza interpretării


relaţiilor matematice ce vor forma snbstanta acestui paragraf. Pentru a fi
însa efectiv utilizabil, un algoritm de analiză trebuie riguros fundamentat.
8
Rezolvarea sarcinii.
Gramatica:
1.P→L 6.E→n
2.L→I 7.E→i
3.L→IL 8.E→n*E
4.I→i=E 9.E→i*E
5.I→e i =E
Unde:
I-instructiune i-identificator
E-expresie e-eticheta
n-numere intregi, reale si cu precizie dubla
Lexeme Codul Tabelul şi codul lexemelor
Numar 1 pentru limbajul dat:
eticheta 2
identificato 3
r 4
= 5
* 6
/ 7
+ 8
- 9
** 10
( 11
)
AF pentru numere intregi:

q 0-9 q

AF pentru numere reale:

9
. E
q q q

Cifra
Cifra

AF pentru numere reale cu precizie dubla:


. D
q q q

Cifra
Cifra
AF pentru identificatori:
q 1

l q 1

f q 2
a
a q 1

d q 2

q q1
e q
1
i q 2
f q
l 1

i q 2

q 0
Schema analizatorului lexical pentru limbajul dat:

a,f,l,di,e Codul
10 cautarea dupa TI completarea
,r AF pentru
AF
AFpentru
pentru
pentru
respectiv
AF num. cautarea dupa TI completarea
înreale Next,ERROR-
ivectorului
EOF
D
(,0-9
)E cu
0,1,2 nu
illegal identifier
sintactic si semantic
ieşiredubla da
identificatori
num.intregi
ERROR-unknown
vectorul
precizie
num.reale nu
symbol ERROR- illegal
vectorului identifier
sintactic si semantic
Analizatorului lexical pe 5 şiruri de intrare corecte şi 3 incorecte:
Sirul de intrare corecta 1: a1 * 3 al1 7.5E - / fil2 + 6
Vect.sintactic 3 5 1 3 1 8 6 3 7 1
Vect.semantic 1 1 2 2 3 3
TI: 1→a1 TN: 1→3
2→al1 2→7.5E
3→fil2 3→6
Sirul de intrare corecta 2: 9.6D + - fi1 ( 3 * alfa1 ) / 4
Vect.sintactic 1 7 8 3 1 1 5 3 1 6 1
0 1
Vect.semantic 1 1 2 2 3
TI: 1→fi2 TN: 1→9.6D
2→alfa1 2→3
3→4
Sirul de intrare corecta 3: ( alf2 ** ( 6.2E – f0 / 9 ) + ) =
Vect.sintactic 1 3 9 1 1 8 3 6 1 1 7 1 4
0 0 1 1
Vect.semantic 1 1 2 2
TI: 1→alf2 TN: 1→6.2E
2→f0 2→9
Sirul de intrare corecta 4: 0 – file2 + ** ( a1 / 9 )
Vect.sintactic 1 8 3 7 9 1 3 6 1 11
0
Vect.semantic 1 1 2 2
TI: 1→file2 TN: 1→0
2→a1 2→9
Sirul de intrare corecta 5: ( alfad2 – al1 + 9.3E / 5 * filer1 ** 7.7D )
Vect.sintactic 1 3 8 3 7 1 6 1 5 3 9 1 11
0
Vect.semantic 1 2 1 2 3 3
TI: 1→alfad2 TN: 1→9.3E
2→al1 2→5
3→filer1 3→7.7D
Sirul de intrare incorecta 1: 3 + end * a1 – a2 / 2.5E * 3.3
Vect.sintactic 1 7 -1 5 3 8 -1 6 1 5 -
1
11
Vect.semantic 1 1 2
TI: 1→a1 TN: 1→3
2→2.5E
ERROR(1)-illegal constant(‘end’)
ERROR(2)-illegal identifier(‘a2’)
ERROR(3)-unknown symbol(‘3.3’)

Sirul de intrare incorecta 2: 76 + 2 – al1 // 3.2 * + alf2 ++ 53


Vect.sintactic -1 7 1 8 3 - -1 5 7 3 -1 -1
1
Vect.semantic 1 1 2
TI: 1→al1 TN: 1→2
2→alf2
ERROR(1)- unknown symbol(‘76’)
ERROR(2)- unknown symbol(‘//’)
ERROR(3)- unknown symbol(‘3.2’)
ERROR(4)- unknown symbol(‘++’)
ERROR(5)- unknown symbol(‘53’)
Sirul de intrare incorecta 3: a0 *- + begin 2.3A ( +/ 12 fa )
Vect.sintactic -1 - 7 -1 - 1 -1 -1 -1 11
1 1 0
Vect.semantic
ERROR(1)-illegal identifier(‘a0’)
ERROR(2)-unknown symbol(‘*-’)
ERROR(3)-illegal constant(‘begin’)
ERROR(4)-illegal identifier(‘2.3A’)
ERROR(5)-unknown symbol(‘+/’)
ERROR(6)-unknown symbol(‘12’)
ERROR(7)-illegal identifier(‘fa’)

12
Listingul programului:
#include<stdio.h> for(int j=0;j<sin;j++)if(semantic[j]!=0)
#include <conio.h> {if(sintactic[j]==14||
#include <stdlib.h> sintactic[j]==15)cout<<semantic[j]<<"
#include <string.h> ";else cout<<" ";}else cout<<" ";}
#include <iostream.h> void eroare(int j,int l, int k){int
char sir[100]; a;cout<<endl;
int sintactic[50],semantic[50]; if(j==1){ cout<<"illegal number (";
char ti[10][30],te[10][30]; for(a=l;a<k;a++) cout<<sir[a];
int i=0,sin=0,identif=0,expr=0; cout<<")"<<endl;}
void completaresin(int k){ if(j==2){ cout<<"illegal symbol of
sintactic[sin]=k; sin++;} assignment (";for(a=l;a<k;a++)
void completaresemi(int j,int l, int k){int cout<<sir[a]; cout<<")"<<endl;}
y=0; char b[10]; if(j==3) {cout<<"illegal identifier
for(int m=0;m<k-l;m++) (";for(a=l;a<k;a++) cout<<sir[a];
b[m]=sir[l+m];b[m]=NULL; //cout<<b; cout<<")"<<endl;}
getch(); if(j==4){ cout<<"unknown symbol
for(m=0;m<identif;m++) (";for(a=l;a<k;a++) cout<<sir[a];
if(strcmp(ti[m],b)==0) y=1; cout<<")"<<endl;}
if(y==0&&j==3){ for(m=0;m<k-l;m++) if(j==5){cout<<"incorect expression
ti[identif][m]=sir[l+m];identif++; (";for(a=l;a<k;a++) cout<<sir[a];
semantic[sin-1]=identif;} cout<<")"<<endl;}
} }
void completareseme(int j,int l, int k){int void afnumere(int j){int
y=0; char b[30]; k=j,q=0,x=j,incorect=0;
for(int m=0;m<k-l;m++) do {j++;} while(sir[j]!=' '&&
b[m]=sir[l+m];b[m]=NULL; //cout<<b; sir[j]!=NULL&&sir[j]!=','&&sir[j]!='='
getch(); &&sir[j]!=':'&&sir[j]!=')'); //k-j
for(m=0;m<expr;m++)if(strcmp(te[m],b) q=0;
==0) y=1; while(x<j){
if(y==0&&j==1){ for(m=0;m<k-l;m++) switch(q){
te[expr][m]=sir[l+m];expr++;semantic[si case 0:{ if(sir[x]=='0'||sir[x]=='1'||
n-1]=expr;} sir[x]=='2'||sir[x]=='3'||sir[x]=='4'||
if(y==0&&j==2){ for(m=0;m<k-l;m++) sir[x]=='5'||sir[x]=='6'||sir[x]=='7'||
te[expr][m]=sir[l+m];expr++;semantic[si sir[x]=='8'||sir[x]=='9') q=0;else
n-1]=expr;} if(sir[x]=='.') q=1;else incorect=1;
} break;}
void afisaresin(){cout<<"vectorul case 1:{if(sir[x]=='E') q=2; else
sintactic: "<<endl; for(int j=0;j<sin;j++) if(sir[x]=='D') q=3;else incorect=1;
cout<<sintactic[j]<<" ";} break;}
void afisaresem(){cout<<endl<<"vectorul case 2:{ if(sir[x]=='0'||sir[x]=='1'||
semantic: "<<endl; sir[x]=='2'||sir[x]=='3'||sir[x]=='4'||
13
sir[x]=='5'||sir[x]=='6'||sir[x]=='7'|| case 5:{ if(sir[x]=='2') q=11; else
sir[x]=='8'||sir[x]=='9') q=2;else incorect=1; break;}
incorect=1; break;} case 6:{ if(sir[x]=='0') q=11; else
case 3:{ if(sir[x]=='0'||sir[x]=='1'|| if(sir[x]=='i') q=7; else incorect=1;
sir[x]=='2'||sir[x]=='3'||sir[x]=='4'|| break;}
sir[x]=='5'||sir[x]=='6'||sir[x]=='7'|| case 7:{ if(sir[x]=='2') q=11; else
sir[x]=='8'||sir[x]=='9') q=3;else if(sir[x]=='l') q=8; else incorect=1;
incorect=1; break;} break;}
default: break;} case 8:{ if(sir[x]=='1') q=11; else
x++; if(sir[x]=='i') q=9; else incorect=1;
} break;}
if(q==0&&incorect==0){completaresin(1) case 9:{ if(sir[x]=='2') q=11; else
; completareseme(1,k,j);} if(sir[x]=='e') q=10; else incorect=1;
if(q==2&&incorect==0){completaresin(2) break;}
; completareseme(2,k,j);} case 10:{ if(sir[x]=='1') q=11; else
if(q==3&&incorect==0){completaresin(2) incorect=1; break;}
; completareseme(2,k,j);} case 11:{incorect=0; break;}
if(incorect==1){eroare(1,k,j);} default :{incorect=1; break;}
i=j; }
} x++;}
void afidentificator(int j){int if(incorect==0){completaresin(3);
k=j,x=j,q=0,incorect=0; completaresemi(3,k,j);} else
do {j++;} while(sir[j]!=' '&& { completaresin(-1); eroare(3,k,j);}
sir[j]!=NULL&&sir[j]!=','&&sir[j]!='=' i=j;
&&sir[j]!=':'&&sir[j]!=')'); //k-j }
if(sir[x]=='a') q=1; if(sir[x]=='f') q=6; void unknown(int j){int k=j;
x++; do {j++;} while(sir[j]!=' '&&
while(x<=j){ sir[j]!=NULL&&sir[j]!=','&&sir[j]!=';'&
switch(q){ &sir[j]!=':');
case 1:{ if(sir[x]=='1') q=11; else eroare(4,k,j);completaresin(-1);
if(sir[x]=='l') q=2; else incorect=1; i=j;}
break;} void main(){clrscr();
case 2:{ if(sir[x]=='1') q=11; else cout<<"Introduceti sirul: ";gets(sir);
if(sir[x]=='f') q=3; else incorect=1; while(sir[i]){
break;} switch(sir[i]){
case 3:{ if(sir[x]=='2') q=11; else case '0':{afnumere(i); break;}
if(sir[x]=='a') q=4; else incorect=1; case '1':{afnumere(i); break;}
break;} case '2': {afnumere(i); break;}
case 4:{ if(sir[x]=='1') q=11; else case '3':{afnumere(i); break;}
if(sir[x]=='d') q=5; else incorect=1; case '4': {afnumere(i); break;}
break;} case '5': {afnumere(i); break;}
case '6': {afnumere(i); break;}
14
case '7': {afnumere(i); break;} case ')': {completaresin(11); i++; break;}
case '8': {afnumere(i); break;} case' ':{i++; break;}
case '9': {afnumere(i); break;} default:{ unknown(i); break;}
case 'a':{afidentificator(i); break;} } }
case 'f':{afidentificator(i); break;} cout<<endl;afisaresin();afisaresem();
case '=':{completaresin(4);i++; break;} cout<<endl<<"Tidentif: ";
case'*':{if(sir[i+1]=='*') for(int v=0;v<identif;v++)
completaresin(9); else {cout<<v+1<<")"<<ti[v]; cout<<", ";}
completaresin(5);i++; break;} cout<<endl<<"Tnumere: ";
case '/':{completaresin(6);i++; break;} for(v=0;v<expr;v++)
case '+':{completaresin(7);i++; break;} {cout<<v+1<<")"<<te[v]; cout<<", ";}
case '-':{completaresin(8);i++; break;} getch();
case '(': {completaresin(10); i++; break;} }

Rezultatul programului:

15
Concluzii:
Efectuind lucrarea de curs, am facut cunostinta cu metodele de
executare a unui analizator lexiacal, si cu princiipile de baza a unui
analizator lexical.In lucrarea data eu am lucrat cu identificatorii,
numerele intregi, reale shi cu precizie dubla. Am invatat sa facem
sirurile corecte si incorecte shi cum sa le exprimam printr-un program
efectuat cu ajutorul limbajului Fortran. In finele lucrarii am executat un
program ce calculeaza shi executa ceia ce am facut noi singuri.

16