Sunteți pe pagina 1din 11

1

INTRODUCERE
1.1 Obiectul i utilitatea disciplinei
Tehnicile de compilare sunt tehnici de programare specializate utilizate, n primul rnd, la scrierea programelor de translatare dar, n acelai timp, aplicabile la realizarea unei game de programe similare translatoarelor. Se tie c translatorul este un program care verific programele scrise de utilizator ntr-un anumit limbaj de programare i le traduce n programe echivalente exprimate ntr-un limbaj accesibil calculatorului (de exemplu cod main). Exponentul cel mai cunoscut al familiei programelor de translatare este compilatorul, ceea ce justific i denumirea disciplinei. Istoria scrierii compilatoarelor a debutat n 1957, cnd a fost realizat primul compilator pentru limbajul de programare Fortran. Performana deosebit pentru vremea aceea, aparine unei echipe de cercettori americani, de la firma I.B.M, condus de John Backus. Implementarea compilatorului a durat circa 18 luni ceea ce poate s par mult astzi cnd compilatoare pentru limbaje de programare comparabile cu Fortran sau chiar mai complexe (Pascal, C, Ada, Java) se pot scrie ntr-un timp mult mai scurt. Principalii factori care au contribuit la aceast evoluie sunt: - Definirea tiinific a noilor limbaje de programare prin includerea unor concepte de baz comune i respectarea riguroas a anumitor principii care uureaz verificarea i translatarea programelor. - Elaborarea, ntre timp, a unei teorii a compilrii bine fundamentat matematic prin Algebra superioar, Logica Matematic i n special prin Teoria limbajelor formale. - Evoluia continu a Ingineriei programrii care a disciplinat i eficientizat activitatea de programare n general i, n special, realizarea programelor de mari dimensiuni. - Apariia i perfecionarea unor unelte software specializate pentru proiectarea i scrierea compilatoarelor. Utiliznd astfel de unelte, se pot genera n mod automat pri ale unui compilator ceea ce reduce durata lui de implementare la sptmni sau chiar la zile. Astzi nu este posibil s se scrie programe de translatare competitive fr cunoaterea teoriei compilrii i ignornd aplicarea tehnicilor, algoritmilor i metodelor specifice. Mai mult, cel care se ocup de implementarea unui limbaj de programare

trebuie s aib solide cunotine att din domeniul general al tiinei calculatoarelor (arhitectur, cod main, echipamente periferice) ct i o privire corect i complet asupra ntregului domeniu al limbajelor de programare. Din acest motiv, disciplina Tehnici de compilare este logic legat de disciplina Conceptele fundamentale ale limbajelor de programare n care s-au introdus deja o serie de cunotine, concepte i noiuni care vor fi utilizate i dezvoltate n continuare: limbaje de programare caracteristici, clasificri, implementarea unui limbaj de programare, sintaxa i semantica unui limbaj simbolisme pentru reprezentarea lor, gramatica unui limbaj, arbore sintactic, probleme legate de implementarea diferitelor concepte i mecanisme specifice limbajelor de programare etc. Coninutul disciplinei Tehnici de compilare este util i pentru realizarea altor tipuri de programe dect cele din familia compilatoarelor: Sisteme de operare, Sisteme de gestiune a bazelor de date, Editoare de texte, toat gama de Programe utilitare sau chiar simple aplicaii care presupun, ca interfa cu utilizatorul, un limbaj de comunicare (comand). Cu ct acest limbaj este mai complicat, cu att este mai util s stpnim astfel de tehnici i metode. Aceste cunotine sunt importante chiar din faza definirii i proiectrii limbajului. Aplicate corect, ele pot conduce la simplificarea limbajului de comunicare i, implicit, la eficientizarea procesului de traducere. Se poate concluziona c teoria compilrii ofer tehnici i metode avansate de programare utile ntr-o gam larg de aplicaii n care comunic dou sisteme, cazul cel mai frecvent fiind acela n care cele dou sisteme sunt omul i respectiv calculatorul.

1.2. Funciile unui compilator


Simplu exprimat, un compilator este un translator care citete un program scris ntr-un limbaj de nivel nalt (limbajul surs) i l traduce ntr-un limbaj echivalent n alt limbaj, de nivel mai sczut (limbajul obiect sau destinaie).

Fig. 1.1. Compilatorul n mod frecvent limbajul destinaie este limbaj main sau un limbaj apropiat de acesta. Ca o parte important a procesului de traducere, compilatorul semnaleaz utilizatorului prezena erorilor n programul surs.

Existena, n prezent, a mii de limbaje surs, varietatea limbajelor destinaie, diferenele, mai mici sau mai mari, de structur i funcionare, au condus la o aparent copleitoare diversificare a compilatoarelor. n ciuda acestei complexiti i varieti aparente, sarcinile de baz pe care trebuie s le ndeplineasc orice compilator sunt n esen aceleai. nelegnd aceste sarcini se pot construi compilatoare pentru o mare varietate de limbaje surs i calculatoare destinaie folosind aceleai tehnici de baz.
Cunotinele despre organizarea i scrierea compilatoarelor au crescut enorm de la nceputul apariiei primelor compilatoare, n deceniul 6. De atunci s-au descoperit tehnici sistematice pentru tratarea multora dintre sarcinile importante care apar n compilatoare. S-au dezvoltat de asemenea, limbaje de implementare bune, ambiane de programare i unelte software. n aceste condiii, un compilator substanial poate fi implementat chiar i ca proiect ntr-un curs de un semestru de proiectare a compilatoarelor.

Modelul analiz-sintez al compilrii Funciile de principiu ale unui compilator sunt: Analiza programului surs; Sinteza programului destinaie (obiect); Corespunztor acestor funcii, compilatoarele se compun din dou pri.. Partea de analiz desface programul surs n componenetele sale de baz i creaz o reprezentare intermediar a programului surs. Partea de sintez construiete programul destinaie din aceast reprezentare intermediar.

1.3 Structura unui compilator


Datorit complexitii sale deosebite, procesul de compilare se descompune n mai multe faze, fiecare faz transformnd programul surs dintr-o reprezentare n alta. n fig. 1.2 se prezint o structur tipic a unui compilator. n practic, unele dintre faze pot fi grupate, iar reprezentrile intermediare ntre fazele grupate nu trebuie construite explicit (vezi 1.6).
Gestionarea tabelei de simboluri O funcie esenial a compilatorului este s nregistreze identificatorii utilizai n programul surs i s colecioneze informaii despre diferitele atribute ale fiecrui identificator. Aceste atribute se pot referi la memoria alocat pentru un identificator, tipul su, domeniul de valabilitate i, n cazul numelor de proceduri, informaii ca numrul i tipul argumentelor, metoda de transmitere a fiecrui argument iar, pentru funcii, i tipul valorii returnate. O tabel de simboluri (TS) este o structur de date care conine o nregistrare pentru fiecare identificator, cu cmpuri pentru atributele identificatorului. Structura de date trebuie s permit regsirea rapid a nregistrrii pentru fiecare identificator. Crearea nregistrrii unui identificator n TS se face la analiza lexical sau la cea sintactic. Informaiile corespunztoare atributelor se introduc ulterior, pe msur ce se

avanseaz n procesul de compilare. Aceste informaii se utilizeaz pentru a efectua verificrile semantice (de exemplu verificrile de tip) precum i pentru generarea corect a codului obiect.
Program surs

Analizor lexical

Analizor sintactic

Analizor semantic Gestionarea tabelei de simboluri

Tratarea erorilor

Generator de cod intermedia

Optimizor de cod

Generator de cod

Program destinaie

Fig. 1.2. Fazele unui compilator Detectarea i semnalarea erorilor Fiecare faz de compilare poate descoperi erori. Reacia compilatorelor la erori poate fi diferit: a) Oprirea compilrii la prima eroare, semnalarea i corectarea ei n programul surs, urmat de reluarea compilrii de la nceput. b) Tratarea erorilor detectate astfel nct compilarea s poat continua, permind s se detecteze i alte erori care vor fi corectate n mod global, la sfrit. Acest mod de tratare se numete revenirea din eroare. n ceea ce privete gestionarea tratrii erorilor, ea poate fi global, pe ntregul compilator, cum sugereaz fig. 1.2 sau poate fi distribuit fiecrei faze.

Cea mai mare parte dintre erorile de compilare sunt detectate n fazele de analiz sintactic i semantic.

1.4 Obiectul fazelor de analiz a programului surs


Partea de analiz a procesului de compilare const, la rndul ei din 3 faze: 1. Analiza lexical sau liniar irul de caractere formnd programul surs este citit de la stnga la dreapta i grupat n atomi sau simboluri lexicale care sunt secvene de caractere avnd o semnificaie comun. De exemplu, n instruciunea de atribuire a:=b+c*10 sunt localizai urmtorii atomi: - identificatorii a,b i c; - simbolurile := , + i *; - numrul ntreg 10. Tot pe parcursul acestei faze se elimin spaiile dintre atomi iar atomii sunt codificai numeric pentru a uura prelucrarea lor n continuare. 2. Analiza sintactic sau ierarhic Atomii sunt grupai ierarhic n colecii mai mari (expresii, declaraii, instruciuni) cu o semnificaie comun, reprezentnd propoziiile gramaticale ale programului surs. n timpul analizei sintactice, operaiile indicate n programul surs sunt determinate i nregistrate ntr-o structur ierarhic numit arbore. Deseori se utilizeaz un tip special de arbore numit arbore sintactic, n care fiecare nod reprezint o operaie iar fiii unui nod sunt argumentele operaiei. Fiecrei propoziii i corespunde, de regul, un arbore sintactic. (fig. 1.3).

Fig. 1.3. Arbore sintactic pentru a:=b+c*10

Structura ierarhic a unui program este, n multe cazuri, exprimat prin reguli recursive. De exemplu, sunt binecunoscute regulile recursive de definire a expresiilor. Similar, ntr-o mare categorie de limbaje, i instruciunile sunt definite prin reguli recursive. mprirea n analiz lexical i sintactic este oarecum arbitrar. De obicei se alege o mprire care simplific sarcina total a analizei. Astfel, numerele, sirurile de caractere, cuvintele (identificatorii) semnele de punctuaie sunt considerate simboluri lexicale n timp ce expresiile, instruciunile, declaraiile sunt construcii sintactice. 3. Analiza semantic sau contextual Efectueaz verificrile care in de sensul (nelesul) componentelor programului i culege informaii de tip pentru faza urmtoare: generarea de cod. Ea utilizeaz structura ierarhic determinat de faza de analiz sintactic pentru a identifica operatorii i operanzii expresiilor i instruciunilor. O component important a analizei semantice este verificarea tipurilor. Aceast component verific dac fiecare operator are operanzi permii de specificarea limbajului surs. De exemplu, definiiile multor limbaje de programare consider c este eroare atunci cnd un numr real este utilizat pentru a indexa un tablou. O alt parte distinct o reprezint analiza de domeniu adic verificarea utilizrii fiecrui identificator strict numai n domeniul su de vizibilitate.

1.5 Obiectul fazelor de sintez a programului destinaie


4. Generarea codului intermediar Dup analizele sintactic i semantic, unele compilatoare genereaz o reprezentare intermediar explicit a textului surs. Se poate concepe aceast reprezentare intermediar ca un program pentru un calculator abstract. Reprezentarea intermediar trebuie s aib dou proprieti importante: s fie uor de generat i s fie uor de tradus n program destinaie. Exist diferite forme de reprezentari intermediare. O form des utilizat este aa numitul cod cu 3 adrese care seamn cu limbajul de asamblare pentru un calculator n care fiecare locaie de memorie poate juca rol de registru. Codul cu 3 adrese const dintr-o secven de instruciuni, fiecare avnd cel mult trei operanzi. Aceast form intermediar are cteva proprieti: a) Fiecare instruciune cu 3 adrese are cel mult un operator pe lng cel de atribuire; n prealabil, compilatorul trebuie s decid asupra ordinei n care se execut operaiile (prioritatea operatorilor);

10

b) Pentru a pstra valorile calculate n fiecare instruciune, compilatorul trebuie s genereze variabile temporare (variabile create de compilator fr coresponden direct n textul surs);

c) Pot exista instruciuni cu 3 adrese cu mai puin de 3 operanzi.

5. Optimizarea codului Scopul acestei faze este acela de a mbuntii codul intermediar, astfel nct s rezulte cod main mai rapid. n acest sens se acioneaz pentru eliminarea redundanelor, a calculelor i variabilelor inutile. Exist mari diferene ntre optimizarea de cod care se face n diferite compilatoare. n aa numitele compilatoare cu optimizare, n care acestei faze i se acord o importan deosebit, fraciunea din timpul de compilare cheltuit pentru optimizare este foarte mare. Exist ns i optimizri simple care mbuntesc considerabil eficiena programului obiect fr a ncetini prea mult compilarea. 6. Generarea de cod Generarea codului destinaie (obiect) este faza final a compilatorului. Codul obiect generat poate s fie, de exemplu, cod main relocabil sau un cod virtual. Pe lng transformarea instruciunilor intermediare n secvene de instruciuni main (virtuale) mai trebuie rezolvate urmtoarele probleme: - selecionarea i alocarea de celule de memorie pentru variabilele din program; - alegerea i implementarea celor mai eficiente tehnici de acces la fiecare variabil n parte (inclusiv la componentele variabilei) utiliznd toate posibilitile de adresare ale calculatorului: indexare, indirectare etc.; - alocarea registrelor pentru calcule i pentru reinerea temporar a rezultatelor intermediare.

n fig. 1.4 se prezint, pe faze, ntregul proces de traducere al instruciunii a:=b+c*10 i principalele structuri de date asociate compilrii. Observaie: Se consider c a, b i c sunt variabile reale.

11

a:=b+c*10

TS 1 a b c

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

Analizor lexical 2 id1:=id2+id3*10 3 4 Analizor sintactic

:= := id1 id2 id3 + * id 10 2 *

id

Analizor semantic

id

num

10

:= id1 id2 id3 10 + * inttoreal

Generator de cod intermediar

temp1:=inttoreal(10) temp2:=id3*temp1 temp3:=id2+temp2 id1:=temp3

Optimizor de cod

temp1:=id3*10.0 id1:=id2+temp1

Generator de cod

(id3) -> R2 #10.0 * (R2) -> R2 (id2) -> R1 (R2) + (R1) -> R1 (R1) -> id1

Fig. 1.4. Traducerea instruciunii a:=b+c*10 i principalele structuri de date necesare

12

1.6. Gruparea fazelor


Delimitarea fazelor de compilare ca n paragrafele precedente urmrete organizarea logic a unui compilator. n diferite implementri concrete apar abateri de la aceast delimitare, n special datorit gruprii mpreun a unor activiti din mai multe faze. O prim grupare este aceea n front-end i back-end. Front end este prima parte a unui compilator, adic acele faze sau pri din faze care depind n primul rnd de limbajul surs. Ea include de obicei: analiza lexical i sintactic, crearea TS, analiza semantic i generarea codului intermediar. n front end se poate, de asemenea, face o superficial optimizare de cod i trebuie obligatoriu inclus tratarea erorilor specific fazelor componente. Back end include acele poiuni din compilator care depind de calculatorul destinaie. Nu depinde direct de limbajul surs ci numai de limbajul intermediar. Include optimizarea codului i generarea de cod mpreun cu operaiile necesare de tratare a erorilor i cele de manipulare a TS. Gruparea fazelor n acest fel ar permite, de exemplu, s se realizeze un compilator pentru un limbaj pe un anumit calculator refcnd partea de back end a unui compilator pentru acelai limbaj, operaional pe alt calculator. Este de asemenea tentant ca mai multe compilatoare pentru limbaje diferite s genereze acelai cod intermediar i s utilizeze acelai back end. Front end-urile fiind diferite, se obin mai multe compilatoare pentru acelai calculator. Datorit diferenelor ntre limbajele surs, aceast a doua modalitate n-a fost prea mult exploatat. n practica realizrii compilatoarelor, mai multe faze ale compilrii sunt, de obicei, implementate mpreun n aa numitele treceri. O trecere const din citirea unui fiier de intrare reprezentnd textul surs al programului sau codul intermediar generat de trecerea anterioar, efectuarea operaiilor specifice fazelor componente i crearea unui fiier de ieire coninnd cod intermediar pentru trecerea urmtoare sau codul obiect final. n practic exist o mare varietate de grupri ale fazelor n treceri. Din acest motiv, dar i din motive de claritate, se prefer studierea tehnicilor de compilare pe faze. Activitile fazelor componente ale unei treceri sunt ntreesute. De exemplu, pentru cazul n care analiza lexical, analiza sintactic, analiza semantic i generarea codului obiect sunt grupate ntr-o singur trecere, irul de atomi generat de analiza lexical poate fi tradus direct n cod intermediar. Programul principal (coordonatorul) unei astfel de treceri poate fi analizorul sintactic. El ncearc s identifice structura gramatical a irului de atomi, apelnd analizorul lexical, ca funcie, ori de cte ori are nevoie de un atom. Pe msur ce se avanseaz n identificarea structurii gramaticale, analizorul sintactic apeleaz generatorul de cod intermediar, care efectueaz i verificrile semantice, organizate, de asemenea, ca funcii distincte. Citirea i scrierea fiierelor intermediare dureaz un timp relativ mare, acesta fiind un argument pentru un numr mic de treceri. Pe de alt parte, gruparea ntr-o singur trecere a unui numr mare de faze poate s duc la limitarea dimensiunii

13

programelor compilate, datorit volumului mare de date care trebuie reinute, incluznd i forma intern a programului. Pentru unele faze, gruparea ntr-o trecere unic este destul de simpl (de exemplu analiza lexical i sintactic). n schimb generarea de cod este mai greu de efectuat pn cnd nu s-a generat n ntregime reprezentarea intermediar (ex. variabilele utilizate nainte de a fi declarate n PL/I, Algol 68 i salturile nainte n codul obiect corespunztoare implementrii unor instruciuni condiionale sau ciclice).

1.7 Unelte pentru construcia compilatoarelor


La scrierea de compilatoare se utilizeaz, evident, uneltele software generale disponibile pe calculatorul respectiv (exemplu: depanatoare, editoare etc.). n plus au fost dezvoltate i unelte specializate care ajut la implementarea diferitelor faze ale unui compilator. La scurt timp dup ce s-au scris primele compilatoare au aprut sisteme care s ajute n procesul de scriere a compilatoarelor (compiler-compiler, generatoare de compilatoare, sisteme de scriere a translatoarelor). n general, ele sunt orientate spre un anumit model de limbaj i sunt potrivite pentru a genera compilatoare pentru limbaje similare cu modelul. De exemplu, pornind de la faptul c pentru multe limbaje de programare analiza lexical este n esen aceeai (difer doar cuvintele cheie i semnele care trebuie recunoscute), astfel de sisteme conin rutine de analiz lexical fix. Acestor rutine li se furnizeaz lista cuvintelor cheie. Metoda nu poate fi aplicat pentru acele limbaje care conin atomi nestandard ( exemplu identificatori incluznd i alte caractere dect litere sau cifre). Alte unelte generale au fost create pentru proiectarea automat a unor componente ale compilatorului. Aceste unelte utilizeaz limbaje specializate pentru specificarea i implementarea componentei respective, pe baza unor algoritmi complicai. Ele sunt cu att mai utile cu ct ascund detaliile algoritmilor de generare i produc componente care se pot uor integra cu restul compilatorului. Exemple: 1. Generatoare de analizoare lexicale. Specificarea de intrare a generatorului se bazeaz pe expresii regulate. Analizorul lexical generat n mod automat este un automat finit. 2. Generatoare de analizoare sintactice. Generarea analizorului sintactic se face pe baza gramaticii limbajului, furnizat la intrare. Importana acestei metode a sczut pe msur ce s-a simplificat implementarea direct a analizei sintactice.

14

3. Maini de traducere dirijate de sintax. Acestea produc colecii de rutine care parcurg arborele sintactic, genernd cod intermediar. 4. Generatoare de cod automate. O astfel de unealt primete o colecie de reguli care definesc traducerea fiecrei operaii din limbajul intermediar n limbajul main pentru calculatorul destinaie. Regulile trebuie s includ suficiente detalii pentru a putea trata diferitele metode de acces posibile pentru date; de exemplu variabilele pot fi n registre, ntr-o locaie fix (static) n memorie sau li se poate aloca o poziie ntr-o stiv. Tehnica de baz este potrivirea tiparelor: instruciunile din codul intermediar sunt nlocuite cu tipare care reprezint secvene de instruciuni main. 5. Maini pentru fluxul datelor. Sunt utilizate la optimizarea de cod pentru culegerea de informaii despre modul de transmitere a valorilor ntre diferitele pri ale programului. Maina primete la intrare detalii despre relaia dintre instruciunile de cod intermediar i informaia culeas.

15