Catedra ATI
Limbaje Formale shi Proiectarea Compilatorului
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:
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 :
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
q 0-9 q
9
. E
q q q
Cifra
Cifra
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’)
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