Documente Academic
Documente Profesional
Documente Cultură
2008 2009
REPROGRAFIA UNIVERSITII TRANSILVANIA DIN BRAOV
TEHNICI DE COMPILARE
Program
surs
Compilator
Program
obiect
Mesaje de
eroare
Figura 1.1
Un compilator este un program care are ca intrare un program surs, scris ntrun limbaj evoluat, iar ca ieire furnizeaz un program obiect. n timpul traducerii,
compilatorul afieaz mesajele de eroare, putnd chiar s corecteze anumite erori.
Exist mii de limbaje de programare, de la cele tradiionale (FORTRAN,
PASCAL) pn la limbaje specializate aprute n diferite domenii ale aplicaiilor
calculatoarelor. Cu ct programul surs este scris ntr-un limbaj mai evoluat (mai
apropiat de limbajul natural), cu att structura compilatorului este mai complicat.
Pag.1
TEHNICI DE COMPILARE
Limbajul obiect poate avea mai multe accepiuni: alt limbaj de programare sau
un limbaj main pentru orice calculator ntre un microprocesor i un supercomputer.
Dei compilatoarele pot fi ntr-un singur pas, multi-pass, load-and-go (evit
trecerea prin faze necesare numai programelor complexe form direct executabil)
i altele, tehnicile de baz folosite sunt aceleai.
Compilatoarele single-pass efectueaz o singur trecere asupra programului
surs, pe cnd cele multi-pass proceseaz de mai multe ori sursa.
Primele compilatoare au nceput s apar prin anul 1950, experimentarea i
implementarea lor fiind fcut independent de grupuri diferite. Primele ncercri de
compilare au nceput cu traducerea expresiilor aritmetice n cod main. n aceast
perioad, compilatoarele erau considerate programe deosebit de dificile. De exemplu,
compilatorul FORTRAN (Backus .a., 1957) a necesitat 18 ani de munc n echip
pentru implementare. De atunci s-au descoperit tehnici pentru rezolvarea
principalelor probleme care apar n compilare, tehnici care au fcut posibil scrierea
unui compilator n cadrul unui proiect studenesc.
Compilatorul se compune din dou pri principale: analiza i sinteza. Partea de
analiz realizeaz descompunerea programului surs n pri constituente i crearea
reprezentrii intermediare. Partea de sintez construiete programul obiect dorit din
reprezentarea intermediar.
n timpul analizei, operaiile implicate de programul surs sunt determinate i
nregistrate ntr-un arbore. De cele mai multe ori se folosete un arbore sintactic n
care fiecare nod reprezint o operaie, iar descendenii nodului sunt operanzi
(argumentele operaiei).
Exemplul 1.1 Arborele sintactic pentru instruciunea de atribuire:
valoare := valoareinit + cant * 40
este urmtorul:
:=
valoare
valoareinit
cant
40
Figura 1.2
Multe instrumente care manipuleaz programe surs execut nti anumite tipuri
de analiz. Exemple de astfel de instrumente sunt:
Pag.2
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
Pag.4
TEHNICI DE COMPILARE
Program surs
Analizor lexical
(scanner)
Analiza
Analizor sintactic
(parser)
Analizor semantic
Managerul
tabelei de
simboluri
Recuperarea
erorilor
Generatorul codului
intermediar
Sinteza
Optimizarea codului
Generatorul de cod
Program obiect
Figura 1.3
TEHNICI DE COMPILARE
1.2.3.Faza de analiz
Pe parcursul compilrii, forma programului surs se schimb. Vom ilustra acest
lucru considernd traducerea instruciunii:
valoare := valoareinit + cant * 40
( 1.a)
(a) Faza de analiz lexical citete caracterele programului surs i le grupeaz
n uniti lexicale sau token-i, unde fiecare unitate lexical reprezint o secven
logic de caractere ca, de exemplu, un identificator, un cuvnt cheie (if, while, begin),
un caracter sau un multicaracter (:=, <= etc.). Secvena de caractere formnd un
token se numete lexem pentru token. Token-ilor li se adaug valoarea lexical.
De exemplu, cnd identificatorul cant este gsit, analizorul lexical genereaz un
token, id, dar adaug i lexema cant n tabela de simboluri dac ea nu exist
deja.
Astfel, n urma analizei lexicale a instruciunii ( 1.a) se va obine:
id1 := id2 + id3 * 40
( 1.b)
Observaia 1.2. Se poate utiliza un token pentru := dar ngreuneaz expunerea; de
asemenea, token-ul numr pentru constanta 40.
De asemenea, n tabela de simboluri se introduce valoare n momentul
depistrii unitii lexicale id1, valoareinit n momentul depistrii unitii lexicale
id2, respectiv cant n momentul depistrii unitii lexicale id3.
(b) n faza de analiz sintactic se ncearc construirea arborelui de derivaie,
o structur ierarhic a irului de token-i.
O structur tipic de date pentru arbore este prezentat n Figura 1.4.
:=
:=
id1
id
1
id
id2
+
2
*
id3
40
id
num (40)
Figura1.4
TEHNICI DE COMPILARE
frunz este o nregistrare cu dou sau mai multe cmpuri, unul pentru a identifica
token-ul i unul pentru a nregistra informaii despre acel token.
Pentru exemplul considerat, Figura 1.6 arat succesiunea fazelor n procesul
analizei.
n mod obinuit, arborele sintactic pentru instruciunea de atribuire se
construiete conform regulilor gramaticale ale limbajului, care folosesc regulile
definirii unei expresii aritmetice:
1.
Orice identificator este o expresie.
2.
Orice numr este o expresie.
3.
Dac expresie1 i expresie2 sunt expresii, atunci tot expresii sunt i:
expresie1 + expresie2
expresie1 * expresie2
(expresie1)
unde regulile 1 i 2 sunt nerecursive iar regula 3 este recursiv.
Similar, pentru instruciune multe limbaje folosesc regulile de definire:
1. Dac identificator1 este un identificator i expresie2 este o expresie, atunci
identificator1 := expresie2
este o instruciune (de atribuire).
2. Dac expresie1 este o expresie i instruciune2 este o instruciune, atunci
while (expresie1) do instruciune2
i
if (expresie1) then instruciune2
sunt instruciuni.
Atunci arborele sintactic pentru instruciunea de atribuire considerat arat ca n
Figura 1.5 .
Faza de analiz lexical rezolv capetele nnegrite din figur, pentru care se
folosesc gramatici regulate i automate cu numr finit de stri. n faza de analiz
sintactic, definiiile recursive sunt modelate cu ajutorul gramaticilor independente de
context.
n mod uzual nu se folosete un arbore sintactic aa de complicat ci unul de
analiz ca cel din Figura 1.4 .
(c) n faza de analiz semantic, analizorul caut erorile semantice i adun
informaii pentru generarea de cod.
O parte important din analiza semantic este verificarea tipurilor. Analizorul
semantic verific dac fiecare operand cu care opereaz un operator este de tipul
cerut n limbajul de programare surs. Totui specificarea limbajului permite i
anumite corecii, de exemplu cnd o operaie aritmetic este aplicat unui ntreg i
unui real. n acest caz, compilatorul trebuie s converteasc ntregul ntr-un real.
Pentru exemplul considerat de noi ( 1.a), 40 este un ntreg i cant este real, deci
analizorul semantic va converti 40 n real prin apelarea subrutinei IntToReal i deci
adaug un nod n plus la arborele sintactic.
Pag.7
TEHNICI DE COMPILARE
1.2.4.Faza de sintez
(a)
Faza de generare a codului intermediar: dup analiza sintactic i
semantic, anumite compilatoare genereaz o reprezentare intermediar explicit a
programului surs, ca un program pentru o main abstract. Aceast reprezentare
trebuie s aib dou caliti: s fie uor de realizat i uor de tradus n program obiect.
O reprezentare intermediar poate avea o mulime de forme, una din acestea
fiind codul cu trei adrese, n care exist cel mult trei operanzi.
n aceast form, compilatorul poate decide n ce ordine s execute operaiile
(operatorul * este anterior operatorului +).
(b) n faza de optimizare de cod, compilatorul tinde s mbunteasc codul
intermediar n sensul micorrii timpului de execuie. Astfel, n exemplul nostru (
1.a), compilatorul poate deduce c are de fcut o singur conversie pentru constanta
40 n real, deci nu are rost s o memoreze i nici s adauge IntToReal ci s foloseasc
direct 40.0 .
De asemenea, cmp3 este folorit numai o dat pentru a memora i transmite
valoarea sa lui id1. Atunci, devine sigur nlocuirea lui cmp3 prin id1.
(c) Faza final a compilatorului este faza de generare de cod obiect, care
const ntr-un cod main relocatabil sau ntr-un cod de asamblare. Sunt selectate
locaii de memorie pentru fiecare din variabilele folosite n program. Apoi
instruciunile intermediare sunt traduse fiecare n cte o secven de instruciuni n
limbaj de asamblare.
Un aspect deosebit de important este atribuirea variabilelor regitrilor de
memorie. Pentru exemplul nostru ( 1.a), codul de asamblare ar fi:
MOVF
MULF
MOVF
ADDF
MOVF
id3,R2
#40.0,R2
id2,R1
R2,R1
R1,id1
Pag.8
TEHNICI DE COMPILARE
valoare := valoareinit + cant * 40
analizor lexical
id1 := id2 + id3 * 40
analizor sintactic
:=
+
id1
id2
*
id3
num(40)
analizor semantic
:=
+
id1
id2
*
id3
IntToReal(40)
generatorul codului
intermediar
temp1 := IntToReal(40)
temp2 := id3 * temp1
temp3 := id2 + temp2
id1 := temp3
optimizarea codului
temp1 := id3 * 40.0
id1 := id2 + temp1
generarea codului
MOVF id3, R2
MULF #40.0, R2
MOVF id2, R1
ADDF R2, R1
MOVF R1, id1
Pag.9
TEHNICI DE COMPILARE
Primul generator de acest tip a fost LEX, realizat n 1975 de M.E.Lesk, sub
sistemul de operare Unix. Pentru MS-DOS exist varianta PCLEX;
Un alt generator de scanner este FLEX al proiectului GNU, unul din cele mai
eficiente generatoare de analizor lexical;
TEHNICI DE COMPILARE
Pag.11
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
Tabela de
simboluri
Figura 2.1
n afar de aceast sarcin principal analizorul lexical mai realizeaz alte cteva
aciuni cum ar fi: elimin comentariile, spaiile i caracterele de tip tab sau
newline. De asemenea, analizorul lexical are sarcina de a corela mesajele de eroare
din compilator cu programul surs, deci trebuie s in minte numrul caracterului
newline citit pentru a-l putea asocia cu un mesaj de eroare. La anumite compilatoare,
analizorul lexical trebuie s realizeze o copie a programului surs cu mesajele de
eroare inserate n el.
n anumite situaii analizorul lexical este mprit n dou faze: prima, numit
scanner, iar a doua, numit anali lexical. Scanner-ul realizeaz numai aciuni
simple, ca de exemplu compactarea programului surs, n timp ce analizorul lexical
realizeaz partea cea mai complicat.
Exist cteva raiuni care au condus la separarea fazei de analiz lexical de fazele
de analiz sintactic i semantic i anume:
Simplificarea descrierii limbajului, datorat eliminrii comentariilor i a
spaiilor.
Eficiena compilrii este mbuntit. O analiz lexical separat permite
folosirea unor tehnici care mresc viteza de execuie a compilatorului. Astfel
sunt tehnicile de memorare n zona tampon specializate pentru citirea
caracterelor de intrare i procesarea token-ilor.
Portabilitatea compilatorului crete pentru c particulariti ale alfabetului de
intrare i alte anomalii dependente de dispozitiv pot fi eliminate n faza de
analiz lexical. De exemplu reprezentarea unor simboluri nestandard ca ^ n
Pascal poate fi izolat de analizorul lexical.
Pag.13
TEHNICI DE COMPILARE
O unitate lexical poart numele de token, iar irul de caractere care compun o
unitate lexical formeaz o lexem. Regula care descrie mulimea lexemelor care
formeaz o unitate lexical se numete pattern.
Token-ii sunt tratai ca simboluri terminale ntr-o gramatic pentru limbajul surs n
faza de analiza sintactic.
n cele mai multe limbaje de programare urmtoarele construcii sunt tratate ca
uniti lexicale: cuvnt rezervat, operator, identificator, constant, iruri literale i
semne de punctuaie (paranteze, virgul i punct-virgul).
Prezentm n continuare cteva exemple de token-i, lexeme i pattern-uri.
Token
Lexeme
Descrierea informativ a pattern-ului
const
const
const
if
if
if
relaie
<, <=, >, =, <>
< sau <= sau > sau = sau <>
identificator Pi, alpha, D2, valoare Liter urmat de litere i cifre
numr
3.1416, 0, 6.02E23
Orice constant numeric
literal
mesaj
Orice ir de caractere cuprins ntre
i (cu excepia caracterului )
Cnd un pattern are mai multe lexeme analizorul lexical trebuie s produc
informaii suplimentare despre lexema ntlnit. De exemplu token-ul numr
desemneaz numerele 3,1416 i 0 i 6,02E23 iar pentru generatorul de cod este
important s tie care dintre ele este folosit.
Exist anumite limbaje n care conveniile limbajului mresc dificultatea analizei
lexicale. De exemplu n limbajul Fortran este important construcia n poziie fix a
liniei de intrare. Astfel aliniamentul unei lexeme poate fi important n stabilirea
corectitudinii programului surs (primul caracter poate reprezenta o etichet,
comentariu sau continuarea liniei anterioare).
n limbajele moderne aa numitul format liber de intrare permite construciei s fie
poziionat oriunde n linia de intrare, astefel nct acest aspect devine mai puin
important.
De asemenea tratarea blank-urilor este diferit n funcie de limbaj i anume la
limbaje ca Fortran sau Algol-68 blank-urile nu sunt semnificative cu excepia
apariiei ntr-o constant alfanumeric. De aceea ele pot fi eliminate simplificnd
programul surs. Totui aceste convenii pot complica restul analizei.
De exemplu, recunoaterea unitilor lexicale dintr-o instruciune DO n limbajul
Fortran poate fi foarte dificil. n instruciunea:
DO 5 I = 1.25
nu putem spune dac DO este un cuvnt cheie sau face parte din identificatorul DO5I
dect n momentul cnd este vzut caracterul . n intrare. Pe de alt parte, n
instruciunea:
DO 5 I = 1,25
Pag.14
TEHNICI DE COMPILARE
Pag.15
TEHNICI DE COMPILARE
2.4.ERORI LEXICALE
n faza de analiz lexical sunt depistate puine erori, datorit faptului c analizorul
lexical are un punct de vedere foarte restrns asupra programului surs.
De exemplu dac ntr-un program C se ntlnete fi pentru prima dat n urmtorul
context:
fi( a == f(x) )
atunci analizorul lexical nu este capabil s spun dac fi este cuvntul cheie if scris
greit sau identificatorul fi. n acest caz analizorul lexical consider fi drept
identificator i las celorlalte faze ale compilatorului sarcina de a depista eroarea.
n general, analizorul lexical citete caracterele programului surs de la stnga la
dreapta pn cnd se poate forma un token. Presupunem c n acest mod nu poate
forma un token din caracterele citite. Atunci sunt posibile patru tipuri de "reparare" a
erorii:
1. Elimin un caracter strin;
2. Insereaz un caracter care lipsete;
3. nlocuiete un caracter incorect printr-unul corect;
4. Schimb dou caractere adiacente.
Astfel de transformri se fac n ideea de a repara intrarea. Cea mai simpl strategie
de acest fel ar fi de a vedea dac un prefix al intrrii rmase poate fi transformat ntro lexem valid dintr-o singur transformare. Aceast strategie presupune c cele mai
multe erori lexicale sunt rezultatul unei singure erori, presupunere care ns nu este
valabil n practic.
O cale de a gsi erorile n program este de a calcula numrul minim de transformri
de eroare necesare pentru a transforma programul ntr-unul corect din punct de vedere
sintactic. Spunem c un program surs are k erori dac cel mai scurt ir de
transformri de eroare necesare pentru a-l transforma ntr-un program valid are
lungimea k. Aceast noiune de "distan minim" este mai mult teoretic.
2.5.TAMPONAREA INTRRII
Exist trei metode generale de abordare a implementrii unui analizor lexical:
1. Utilizarea unui generator de analizor lexical, cum ar fi limbajul LEX, care
produce analizorul lexical pornind de la o expresie regulat; n acest caz,
generatorul dispune de rutine pentru citirea i tamponarea intrrii.
2. Scrierea analizorului lexical ntr-un limbaj de programare convenional,
utiliznd facilitile de intrare/ieire pe care limbajul respectiv le pune la
dispoziia programatorului.
3. Scrierea analizorului lexical n limbaj de programare, n care gestionarea
intrrilor i ieirilor trebuie fcute de programator.
Dificultatea implementrii crete n ordinea prezentrii celor trei metode. Din
pcate, metoda cea mai greu de implementat este i cea mai rapid n sensul vitezei de
lucru a analizorului. Cum analiza lexical este singura faz a compilatorului n care
Pag.16
TEHNICI DE COMPILARE
programul surs este citit caracter cu caracter, este posibil ca, compilatorul s petreac
o mare parte din timp la nivelul acestei faze, cu toate c celelalte faze sunt mult mai
complexe.
Pentru multe limbaje surs exist momente cnd analizorul lexical trebuie s
priveasc nainte un numr de caractere pentru a-i da seama dac lexema citit pentru
un ablon este valid. Se folosete n acest sens un buffer (zon tampon) mprit n
dou jumti, fiecare putnd conine maxim N caractere, ca n Fig. 2.3 n general, N
este numrul de caractere care ncap ntr-un bloc de disc.
E
eof
forward
nceput_de_lexem
2.5.1.Santinele
Dac folosim schema prezentat n Fig. 2.3. trebuie verificat de fiecare dat dac
pointerul anticipativ a trecut de sfritul unei jumti de buffer pentru a ti dac
trebuie ncrcat cealalt jumtate. Exceptnd momentul cnd a ajuns la sfritul unei
jumti, codul din Fig. 2.4. execut dou teste la fiecare avans al pointerului
Pag.17
TEHNICI DE COMPILARE
anticipativ. Putem reduce aceste dou teste la unul, dac vom plasa la sfritul
fiecrei jumti un caracter special, numit santinel (eof).
Fig. 2.5. prezint tamponarea intrrii cu santinele.
E
eof
eof
eof
forward
nceput_de_lexem
2.6.SPECIFICAREA TOKEN-ILOR
Expresiile regulate sunt o noiune important pentru specificarea token-ilor.
Fiecare pattern produce o mulime de iruri, astfel expresiile regulate servesc pentru
denumirea acestor iruri.
Vom folosi noiunile introduse n cadrul teoriei limbajelor formale: cuvnt, prefix,
sufix, subcuvnt (subir), subcuvnt propriu i subsecven a lui s (un cuvnt format
cu simboluri ale lui s, pstrnd ordinea). Subsecvena se obine dintr-un cuvnt prin
eliminarea unor subcuvinte (de exemplu, aab este subsecven a cuvntului cabacab).
De asemenea, vom folosi operaii cu limbaje cunoscute deja.
Pag.18
TEHNICI DE COMPILARE
nchiderea (r)*
indic limbajul L(r)
Observaia 2.1
a) Parantezele exterioare folosite mai sus nu au nici o semnificaie i pot fi eliminate
dac dorim.
b) Operaiile , i | au precedena descrescnd de la cea mai mare ( ) la cea mai
mic ( | ).
c) Toate operaiile sunt asociative iar alternarea este i comutativ.
d) Dac dou expresii regulate r i s indic acelai limbaj, spunem c ele sunt
echivalente i notm r=s. De exemplu, (a | b) (b | a) .
Exist un numr de reguli algebrice pentru expresii regulate care pot fi folosite
pentru a le transforma n alte expresii echivalente, conform tabelului urmtor.
Tabelul 2.1
Regul
Descriere
r|s=s|r
Alternarea este comutativ
r|(s|t)=(r|s)|t
Alternarea este asociativ
(rs)t=r(st)
Concatenarea este asociativ
Pag.19
TEHNICI DE COMPILARE
r(s|t)=(rs)|(rt)
(s|t)r=(sr)|(tr)
r=r
r=r
r*=(r|)*
r**=r*
2.6.1.Definiii regulate
Printr-o notaie convenabil dorim s dm denumiri pentru anumite expresii
regulate i s le dm i definiia, adic modul lor de construcie.
Dac este un alfabet de baz, atunci o definiie regulat este un ir de definiii de
forma:
d1 r1
d 2 r2
...
d n rn
unde fiecare di este un nume distinct i fiecare ri este o expresie regulat peste
simbolurile din {d 1 , d 2 ,..., d n } . Pentru a distinge numele de simboluri, convenim
s punem fiecare nume ntre .
Exemplul 2.1. Identificatorii din Pascal sunt cuvinte formate din litere i cifre,
ncepnd cu o liter. Deci definiiile regulate sunt n acest caz de forma:
litera mare A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P |
| Q | R |S| T | U | V | W | X | Y | Z
litera mic a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t |
|u|v|w|x|y|z
litera litera mare | litera mic
cifra 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
id litera ( litera | cifra )
Exemplul 2.2. Numerele fr semn din Pascal sunt iruri de forma 5280, 39.37,
6.33E4 sau 1.89E-4. Acestea pot fi construite cu urmtoarele definiii:
cifra 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
cifre cifra ( cifra )
<fracie-opional> . cifre |
<exponent-opional> (E( | | ) cifre ) |
<numr> <cifre><fracie-opional><exponent-opional>
Se impun n practic anumite notaii prescurtate:
1. Semnul "+" pentru una sau mai multe apariii.
Pag.20
TEHNICI DE COMPILARE
r rr
r r |
2. Semnul "?" pentru o apariie sau nici una.
r? r |
De exemplu, relund Exemplul 2.2, putem defini:
cifra 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
cifre cifra
<fracie-opional> (. cifre )?
<exponent-opional> (E( | | ) cifre )?
<numr> <cifre><fracie opional><exponent opional>
3. Semnele "[" i "]" pentru delimitarea unei clase de caractere; n cazul unui
interval cunoscut de simboluri, se poate utiliza semnul "-" pentru precizarea clasei
respective.
De exemplu, [abc] indic expresia a|b|c, unde a,b,c sunt simboluri. Prin [a-z]
indicm expresia a|b|c||z.
Astfel, identificatorii se pot scrie:
id [A Za z][A Za z0 9] .
Observaia 2.2. Nu toate limbajele pot fi descrise prin expresii regulate. n cazul
limbajelor de programare, nu poate fi descris un ntreg limbaj de programare prin
expresii regulate, ci numai anumite pri din el (construcia elementelor de baz ale
limbajului, din simbolurile de baz).
2.7.RECUNOATEREA TOKEN-ILOR
Avnd o specificaie a token-ilor, problema care se pune este de a recunoate aceti
token-i.
Pentru c expresiile regulate sunt forme condensate de descriere a limbajelor
regulate (limbaje de tip 3), recunoaterea unor astfel de limbaje se face cu ajutorul
automatelor finite.
Ne vom folosi de urmtoarele rezultate din teoria limbajelor formale:
1) Construcia unui automat finit nedeterminist (cu -tranziii) dintr-o expresie
regulat.
2) Construcia unui automat finit determinist echivalent cu unul nedeterminist.
3) Construcia expresiilor regulate pornind de la o gramatic de tip 3.
4) Construcia automatului cu numr minim de stri echivalent cu un automat
determinist dat.
n final, recunoaterea token-ilor este realizat de un program care simuleaz
funcionarea unui automat finit determinist, indicat printr-o diagram de tranziie.
Exemplul 2.3 Considerm urmtorul fragment de gramatic:
Pag.21
TEHNICI DE COMPILARE
if if
then then
else else
oprel | | | | |
id litera(litera | cifra)*
num cifra+ (.cifra+)?(E(+|-)?cifra+)?
unde litera i cifra au fost definite n Exemplul 2.1.
Pentru acest fragment de limbaj, analizorul lexical recunoate cuvintele cheie if,
then, else i lexemele oprel, id i num. Vom presupune cuvintele cheie ca fiind
rezervate, adic nu pot fi folosite drept identificatori. Vom mai presupune c
lexemele sunt separate prin iruri nenule de caractere blank, tab i newline.
Analizorul lexical va sri spaiile albe folosind expresiile regulate definite de:
delim blank | tab | newline
ws delim+
n cazul descoperirii unei descrieri pentru ws, analizorul nu produce nici un token
ci caut urmtorul token situat dup ws.
inta noastr este de a construi un analizor lexical care s izoleze lexemele pentru
urmtorul token n buffer-ul de intrare i s produc ca ieire o pereche de forma
(token, atribut) folosind tabela de traducere dat n tabelul urmtor.
Tabelul 2.2
Expresie
Token
Atribut
regulat
ws
if
if
then
then
else
else
id
pointer n tabela de simboluri
id
num
pointer n tabela de simboluri
num
<
LT
oprel
<=
LE
oprel
=
EQ
oprel
<>
NE
oprel
>
GT
oprel
>=
GE
oprel
Vom construi nti un graf reprezentnd diagrama de stare sau diagrama
tranziiilor.
Pag.22
TEHNICI DE COMPILARE
n graf, nodurile sunt stri legate ntre ele prin sgei numite arcuri, care sunt
etichetate cu caractere de intrare. Eticheta "altceva" se refer la orice caracter care nu
este indicat prin celelalte arcuri care prsesc acelai nod.
Presupunem c schema este determinist (desigur c se poate implementa i una
nedeterminist).
O anumit stare este considerat stare iniial, simbolizat cu o sgeat de intrare
notat "start", iar anumite arce au i aciuni care trebuiesc executate cnd fluxul de
control parcurge un astfel de arc (de exemplu, citete un nou caracter).
Vom prezenta acum diagramele de tranziie pentru:
a) oprel
b) id i cuvintele cheie
c) num (numr fr semn n Pascal)
d) delim
start
<
>
altceva
return(oprel, LE)
return(oprel, NE)
*
return(oprel, LT)
=
>
return(oprel, EQ)
=
return(oprel, GE)
6
altceva
*
8
return(oprel, GT)
liter
altceva
10
11 * return(gettoken(), install_id())
TEHNICI DE COMPILARE
13
12
cifr
.
E
14
16
66
altceva
*
27
77
cifr
cifr
cifr
15
return(numr, install_num())
16
66
17
cifr
altceva
18
77
cifr
19
return(numr,install_num())
*
altceva
24
return(numr, install_num())
TEHNICI DE COMPILARE
delimitator
start
delimitator
23
altceva
24
25
TEHNICI DE COMPILARE
switch (start)
{ case 0: start=9; break;
case 9: start=12; break;
case 12: recover(); break;
default: /*eroare de compilare*/
}
return start;
}
token nexttoken()
{ while(1)
{switch (state)
{case 0: c=nextchar();/* c este caracterul privit
nainte */
if (c==blank || c==tab || c==newline)
{ state=0;
lexeme_beginning++; /* nceputul
lexemei avanseaz cu 1 */
}
else if (c=='<') state=1;
else if (c=='=') state=5;
else if (c=='>') state=6;
else state=fail();
break;
case 1: c=nextchar();
if (c=='=') state=2;
else if (c=='>') state=3;
else state=fail();
break;
case 2: return (oprel,LE);
case 3: return (oprel,NE);
case 4: retract(1);
return (oprel,LT);
case 5: return (oprel,EQ);
case 6: c=nextchar();
if (c=='=') state=7;
else state=8;
break;
case 7: return (oprel,GE);
case 8: retract(1);
return (oprel,GT);
case 9: c=nextchar();
if (isletter(c)) state=10;
else state=fail();
Pag.26
TEHNICI DE COMPILARE
break;
case 10: c=nextchar();
if (isletter(c)) state=10;
else if (isdigit(c)) state=10;
else state=11;
break;
case 11: retract(1);
return (gettoken(),install_id());
case 12: c=nextchar();
if (isdigit(c)) state=13;
else state=fail();
break;
case 13: c=nextchar();
if (isdigit(c)) state=13;
else if(c=='.') state=14;
else if(c=='E') state=16;
else state=27;
break;
case 14: c=nextchar();
if (isdigit(c)) state=15;
else state=fail();
break;
case 15: c=nextchar();
if (isdigit(c)) state=15;
else if (c=='E') state=16;
else state=24;
break;
case 16: c=nextchar();
if (c=='+' || c=='-') state=17;
else if (isdigit(c)) state=18;
else state=fail();
break;
case 17: c=nextchar();
if (isdigit(c)) state=18;
else state=fail();
break;
case 18: c=nextchar();
if (isdigit(c)) state=18;
else state=19;
case 19: retract(1);
return (num, install_num());
case 24: retract(1);
return (num, install_num());
case 27: retract(1);
Pag.27
TEHNICI DE COMPILARE
Lex compiler
lex.yy.C
ir de intrare
a.out
C compiler
a.out
ir de token-i
TEHNICI DE COMPILARE
lexemelor. Aciunile care sunt asociate cu expresiile regulate din lex.1 sunt
secvene de cod C i sunt introduse direct n lex.yy.c.
3. lex.yy.c este executat folosind un compilator C pentru a produce un program
obiect a.out, analizatorul lexical care transform un ir de intrare ntr-un ir
de token-i.
TEHNICI DE COMPILARE
id
{litera}({litera}| {cifra})*
num {cifra}+(\.{cifra}+)?(E[+\-]?{cifra}+)?
%%
{ws} {/*nici o actiune i nici un return*/}
if
{return(IF);}
then {return(THEN);}
else {return (ELSE);}
{id} {yylval=install_id( ) ; return (ID);}
{num} {yylval=install_num( ); return (NUM);}
< {yylval=LT; return(OPREL);}
<= {yylval=LE; return(OPREL);}
= {yylval=EQ; return(OPREL);}
<> {yylval=NE; return(OPREL);}
> {yylval=GT; return(OPREL);}
>= {yylval=GE; return(OPREL);}
%%
install_id( ){ /*procedura de instalare a lexemei, a carei prim caracter este pointat
de yytext i a crei lungime este yylong , n tabela de simboluri, i
returnarea unui pointer thereto*/
}
install-num( ){
/*procedura similar de instalare a lexemei care este un numar */
}
Observaia 2.4. n seciunea de declaraii sunt definite constantele declarate folosite
de regulile de traducere. Aceste declaraii sunt cuprinse ntre paranteze speciale de tip
%{ i }%. Orice apare ntre astfel de paranteze este copiat direct n analizatorul
lexical lex.yy.c i nu este tratat ca parte a definiiilor regulate sau a regulilor de
traducere. Exact acelai tratament este aplicat i procedurilor auxiliare din seciunea a
treia (install_id i install_num), acestea sunt copiate n lex.yy.c caracter cu caracter.
De asemenea n aceast seciune sunt incluse i cteva definiii regulate.
Fiecare definiie este format dintr-un nume i o expresie regulat indicat de acel
nume. De exemplu delim se folosete pentru clasa [ \t\u ], adic oricare din cele trei
simboluri: blank, tab(t) sau newline (n).
A doua definiie este a spaiilor albe (ws) care reprezint orice ir nevid de
delim. Se observ c delim este introdus ntre acolade pentru a se distinge de
patternul constand din cinci litere delim.
n definiia literei se folsete deasemenea simbolul de clasa [A-Za-z] care
indic toate literele mari de la A la Z i cele mai mici de la a la z.
Parantezele { } folosite n definiia id sunt folosite numai pentru grupare, iar | este
metasimbolul Lex folosit pentru reuniune.
n definiia num se folosete metasimbolul ? nsemnnd zero sau o apariie i \
pentru [+\-] pentru a nu confunda cu caracterul folosit n descrierea unei clase care
cuprinde un interval.
Exist i alt cale de a face ca un ir de caractere s aib sensuri naturale:
introducerea ntre apostrofuri.
Pag.30
TEHNICI DE COMPILARE
Acum s considerm regulile de traducere din seciunea care urmeaz dup primul
%%. Prima regul spune c dac apare ws nu exist nici o aciune. A doua regul
spune c dac se observ literele if, atunci ntoarce tokenul IF care este o constant
declarat reprezentnd un anume interes pentru parser. Urmtoarele reguli procedeaz
la fel cu tokenul then i else.
n regula pentru id se observ dou aciuni:
-nti variabila yylval ia valoarea install_id( ), o procedura descris n
seciunea a treia. yylval este o variabil a crei definiie apare n ieirea lex, lex.yy.c,
i este de asemenea folosit de parser, reprezentnd valoarea lexical;
-apoi return (id) ntoarce numai codul pentru clasa tokenului.
Nu prezentam detaliile pentru procedura install_id. Totui putem spune a ea
privete n tabela de simboluri pentru lexema produs de patern-ul id. Procedura
folosete dou variabile yytext i yylong.Variabila yytext corespunde acelei variabile
pe care am numit-o nceput de lexem i este un pointer la primul caracter al
lexemei; yyleng este un ntreg artnd ct de lung este lexema.
Presupunem c analizatorul lexical rezultat din programul lex are o intrare
constnd din dou tab-uri, literele if i un blank. Cele doua tab-uri sunt cel mai lung
prefix al unui pattern i anume ws. Pentru ws nu exist nici o aciune aa c
analizorul lexical mut pointerul de nceput de lexem, yytext , la i i ncepe s
caute urmtorul token.
Urmtoarea lexem este if. Aici este posibil s considerm i patternul if i
{id} pentru aceiai lexem i nici una din forme nu este mai lung dect cealalt.
Pentru c patternul if precede patternul {id} conflictul este rezolvat n favoarea
cuvntului cheie if. n general pentru rezolvarea unor asemenea conflicte este bine s
folosim o strategie prin care cuvintele cheie sunt rezervate i listate nainte de
patternul pentru identificatori.
Pentru alt exemplu presupunem c <= sunt primele dou caractere citite.
Strategia Lex de selectare a celui mai lung prefix elimina conflictul dintre <= i <.
2.9.2.Operatorul de privit nainte, lookahead
Exist situaii cnd analizatorul lexical trebuie s priveasc nainte depind
sfritul lexemei pentru a determina tokenul cu siguran. (Ex. DO n Fortran). n
limbajul Lex se folosete operatorul de privit nainte (lookahead) / i atunci un pattern
arat sub forma r1/r2 unde r1 i r2 sunt expresii regulate. Aceast succesiune
definete forma prin expresia regulat r1, dar numai dac este urmat de expresia r2.
De exemplu specificaia Lex pentru a recunoate cuvntul cheie DO din Fortran este:
DO/({litera}|{cifra})*=({litera}|{cifra})*,
Cu aceast specificaie, analizorul lexical va privi nainte pe bufferul su de intrare
cutnd un ir de litere i cifre urmate de =, urmat de un ir de litere i cifre, urmat
de virgul, pentru a fi sigur c nu este vorba de o instruciune de atribuire. Atunci
numai caracterele D i O precednd operatorul / vor forma lexema. Dup ce s-a
precizat lexema, yytext puncteaz pe D i yyleng este egal cu 2.
Pag.31
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
- indic domenii n cadrul unei clase. Astfel [a-z0-9] indic litere mici de la a
la z i cifrele de la 0 la 9.
Observia 2.9. Daca i - apare ntre caracterele unei clase el trebuie s fie primul
sau ultimul caracter din clasa respectiv:
TEHNICI DE COMPILARE
ab$
este echivalent cu
ab/ \n
7) Definiii. Pentru definiii se folosesc tot parantezele { i }.
De exemplu {cifr} reprezint definiia pentru cifr.
8) \( i \) sunt paranteze folosite pentru a indica caracterul care
se repet iar repetarea primului, a celui de-al doilea sau a celui de-al IX-lea caracter
din ablon se face prin \1,\2, sau \9.
Astfel de exemplu:
\([a-z]\)\1
reprezint cuvinte din dou litere identice iar
`
\([a-z]\)\([a-z]\)[a-z]\2\1
reprezint palindroame formate din cinci litere.
TEHNICI DE COMPILARE
care atunci cnd va ntlni abc\def va potrivi numai primele cinci caractere abc\,
dup care apelul lui yymore va face s fie adugat i def. De notat ca semnul
trebuie tratat n <<procesarea dorit de utilizator >>.
}
[-+/*\n]
[ \t]+
{
yylval = atoi(yytext);
return INTEGER;
return *yytext;
;
2.10.AUTOMATE FINITE
O diagram de tranziie generalizat se numete automat finit. Un automat finit
poate fi determinist (AFD), caz n care arcele care pleac dintr-o stare sunt etichetate
Pag.36
TEHNICI DE COMPILARE
cu simboluri diferite, sau nedeterminist (AFN), unde din anumite stri pleac arce
etichetate cu un acelai simbol. Amndou tipurile de automate pot recunoate foarte
precis mulimile regulate. Deci, automatele finite recunosc exact ceea ce recunosc i
expresiile regulate. n ceea ce privete timpul de lucru, un AFD conduce la o
recunoatere mult mai rapid a unui cuvnt dect un AFN, ns un AFD ocup mult
mai mult spaiu dect un AFN.
n cele ce urmeaz vom prezenta o metod de transformare a expresiilor regulate
n automat finit determinist.
2.10.1.Construirea unui AFD pornind de la o expresie regulat
Vom folosi o expresie regulat r extins, prin concatenarea cu simbolul special #.
Astfel, construind AFD pentru expresia r#, orice stare cu o #-tranziie va fi o stare
final.
Expresia regulat extins va fi reprezentat printr-un arbore sintactic cu simboluri
pe post de frunze i operatori pe post de noduri interioare. Un nod interior poate fi un
nod cat, or sau star, reprezentnd concatenarea, operatorul | sau *. Fig.2.24(a)
reprezint un arbore sintactic pentru o expresie regulat cu nodurile cat marcate prin
puncte. Arborele poate fi construit n aceeai manier n care construim arborele
sintactic pentru o expresie aritmetic. Frunzele pot fi etichetate cu simboluri din
alfabet sau cu . Fiecrei frunze neetichetate cu i asociem un numr natural unic
reprezentnd poziia simbolului respectiv. Un simbol poate avea deci mai multe
poziii. Strile numerotate ale AFN din Fig. 2.24(c) corespund poziiilor simbolurilor
din arborele sintactic al Fig. 2.24(a). Strile neimportante ale AFN sunt reprezentate
prin litere mari din alfabet n Fig. 2.24(c). O stare a unui AFN este important dac
are mcar o a-tranziie cu a .
n timpul construirii AFN dou mulimi de stri pot fi identificate dac au
aceleai stri importante. Astfel, AFD din Fig. 2.24(b) poate fi obinut din AFN
reprezentat n Fig. 2.24(c).
Pentru a construi AFD din expresia regulat r# vom utiliza urmtoarele patru
funcii: nullable, firstpos, lastpos i followpos. Pentru fiecare nod n din arborele
sintactic al expresiei r#, definim:
firstpos(n) ca fiind mulimea poziiilor care se potrivesc cu primul simbol al unui
cuvnt generat de subexpresia de rdcin n
lastpos(n)ca fiind mulimea poziiilor care se potrivesc cu ultimul simbol al unui
cuvnt generat de subexpresia de rdcin n
nainte ns de a afla firstpos i lastpos trebuie s tim care noduri din arbore pot
genera subexpresii pentru limbaje ce conin cuvntul vid. Aceste noduri sunt numite
nullable i pentru fiecare nod n considerm nullable(n ) adevrat, dac n este nod
nullable i fals, n caz contrar.
Pentru a afla valorile acestor funcii, utilizm o regul de baz referitoare la expresiile
formate dintr-un singur simbol i trei reguli inductive care ne permit s determinm
valorile funciilor lucrnd cu arborele sintactic de la frunze spre rdcin. Regulile
Pag.37
TEHNICI DE COMPILARE
.
.
.
#
6
b
4
a
3
start
1,2,3
a
1
1,2,3
,4
1,2,3
,5
1,2,3
,6
a
b
2
start
c1
firstpos(n)
lastpos(n)
adevrat
fals
{i}
{i}
nullable( c1 )
sau
nullable(c2 )
firstpos( c1 )
firstpos( c2 )
lastpos(c1 )
lastpos( c2 )
nullable( c1 )
i
nullable( c2 )
if nullable(c1 ) then
firstpos( c1 )
firstpos(c2 )
else firstpos( c1 )
if nullable(c1 ) then
lastpos( c1 )
lastpos( c2 )
else lastpos(c2 )
c2
c1
n
nullable(n)
c2
Pag.38
TEHNICI DE COMPILARE
n
firstpos( c1 )
adevrat
lastpos( c1 )
c1
{1,2}
{1,2}
{1,2}
{1} a {1}
{6}
{5}
{4}
{3}
{6} # {6}
{5} b {5}
{4} b {4}
{3} a {3}
{2} b {2}
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
2.11. REZUMAT
Faza de analiz lexical este prima i cea mai simpl dintre fazele de analiz
pentru c regulile gramaticale verificate sunt de tip 3. Intrarea ntr-un analizor lexical
este programul surs, considerat un ir de caractere iar ieirea este un ir de uniti
lexicale. n esen un analizor lexical este un automat finit care recunoate unitile
lexicale: cuvinte cheie, identificatori, etichete, constante numerice, alfanumerice sau
logice, operatori i separatori etc.
Implementarea unui analizor lexical se poate face sciind un program care s
simuleze funcionarea automatului finit care recumoate unitile lexicale sau
folosind generatorul de analizor lexical LEX.
Exerciii
1. Care este alfabeul de intrare pentru urmtoarele limbaje de programare:
a. PASCAL;
b. C;
c. JAVA.
2. Care este convenia utilizrii blanck-urilor n fiecare dintre limbajele de la ex.1.
3. Identificai lexemele din urmtoarele secvene de programe:
a. Prgramul C
#include<iostream.h>
int multa[9],multb[9],multc[9],n,m,i,j,k,gasit;
main()
{cout<<"nr de elemente al multimii A";cin>>n;
for(i=0;i<n;i++)
{cout<<"mult["<<i+1<<"]=";cin>>multa[i];
}
cout<<"nr de elemente al multimii B";cin>>m;
for(i=0;i<m;i++)
{cout<<"mult["<<i+1<<"]=";cin>>multb[i];
}
k=0;
for(i=0;i<n;i++)
{gasit=0;
for(j=0;j<=m && !gasit;j++)
if(multa[i]==multb[j])gasit=1;
if(gasit)multc[k++]=multa[i];
}
cout<<"A intersectat cu B"<<endl;
for(i=0;i<k;i++)cout<<multc[i]<<endl;
return i;
}
b. Programul Java
public class Stiva
{ private int a[];
public Stiva()
{a=new int[0];
}
public Stiva(int q)
{ a=new int[1];
a[0]=q;
}
public Stiva(int q[])
Pag.41
TEHNICI DE COMPILARE
{ a=new int[q.length];
for(int i=0;i<q.length;i++)
a[i]=q[i];
}
public void push(int q)
{if (!this.contains(q))
{int b[]=new int[a.length+1];
b[0]=q;
for (int i=0;i<a.length;i++)
b[i+1]=a[i];
a=b;
}
}
public int pop()
{ return a[0];
}
public boolean contains(int b)
{ for(int i=0;i<a.length;i++)
if (a[i]==b)
return true;
return false;
}
public static void main(String args[])
{Stiva st=new Stiva();
st.push(10);
System.out.println(st.pop());
}
}
c. Programul Pascal
uses crt;
type mat=array[1..10,1..10]of 0..1;
var n:integer;
A,D:mat;
procedure cit_mat(var n:integer;var A:mat);
var i,j:integer;
begin
write('Introduceti nr. de noduri:');
readln(n);
writeln('Introduceti matricea de adiacenta:');
for i:=1 to n do
for j:=1 to n do
begin
write('a[',i,',',j,']=');
readln(a[i,j]);
end;
end;
procedure Roy_W(n:integer;A:mat;var D:mat);
var i,j,k:integer;
begin
D:=A;
for K:=1 to n do
for i:=1 to n do
for j:=1 to n do
if (D[i,j]=0) and(i<>k) and(j<>k) and(j<>k)
then if D[i,k]<=D[k,j]
then D[i,j]:=D[i,k]
else D[i,j]:=D[k,j];
end;
procedure afisare(n:integer;D:mat);
var i,j:integer;
begin
writeln;writeln('Matricea drumurilor :');
for i:=1 to n do
Pag.42
TEHNICI DE COMPILARE
begin
for j:=1 to n do
write('
',D[i,j]);
writeln;
end;
end;
begin{program principal}
clrscr;
cit_mat(n,A);
Roy_W(n,A,D);
afisare(n,D);
readln;
end.
Pag.43
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
n exemplul de mai sus am notat prin . reducerile care se fac n stiva automatului.
Arborele de sintax al expresiei aritmetice se construiete pornind de la rdcin
spre rezultat, fiecare calcul n automatul de mai sus, care nu este reducere, genernd o
arborescen a lui.
E
id
( E
id
id
Figura 3.1.
TEHNICI DE COMPILARE
Exemplul 3.2. Considernd aceeai gramatic din Exemplul 3.1, care genereaz
expresiile aritmetice cu cele patru operaii vom obine automatul push-down:
M=({q0,q1}, {id,(,),+,-,/,*}, {E,A,id,(,),+,-,/,*,$}, , q0, $, {q1}),
unde funcia de tranziie, , conine urmtoarele seturi de tranziii:
(q0, id, Z) ( q0, Zid )
(q0, (, Z) ( q0, Z( )
(q0, ), Z) ( q0, Z) )
(pentru orice simbol push-down, Z)
(q0, +, Z) ( q0, Z+ )
I.
(q0, -, Z) ( q0, Z- )
(q0, /, Z) ( q0, Z/ )
(q0, *, Z) ( q0, Z* )
II.
III.
(q0, , $E ) ( q1, )
TEHNICI DE COMPILARE
analizor
lexical
analizor
sintactic
arbore
sintactic
cere urmtoarea
unitate lexical
tabela
de
simboluri
modul panic
corectare global.
Modul panic este metoda cea mai uor de implementat. Atunci cnd descoper o
eroare, analizorul terge simboluri din irul de intrare pn cnd o unitate lexical
Pag.48
TEHNICI DE COMPILARE
dorit este gsit (de obicei un delimitator). Aceast metod sare foarte des peste o
parte semnificativ a intrrii fr s verifice dac exist i alte erori, ns are avantajul
simplitii i al faptului c nu poate intra ntr-un ciclu infinit ca n cazul altor metode.
n situaia n care prezena mai multor erori ntr-o instruciune este rar, metoda poate
fi foarte util.
Nivelul expresie const ntr-o corectare local a intrrii rmase. Astfel, analizorul
poate nlocui un prefix al intrrii rmase cu un ir care-i va permite s continue
analiza. O corecie local tipic este nlocuirea unei virgule prin punct i virgul,
tergerea sau inserarea unui simbol punct_i_virgul. Trebuie avut grij ns ca aceste
nlocuiri s nu duc la un ciclu infinit, cum ar fi, de exemplu, inserare continu a unui
simbol naintea simbolului curent din intrare. Acest tip de nlocuiri pot corecta orice
ir de intrare i a fost folosit n implementarea multor compilatoare. Principalul
dezavantaj este dificultatea de a trata o eroare n situaia n care a aprut nainte de
momentul detectrii ei.
Strategia regulilor eronate poate fi utilizat atunci cnd cunoatem foarte bine erorile
ce pot fi ntlnite. Se utilizeaz o gramatic extins, n care au fost adugate producii
generatoare de structuri greite. Cnd o producie eronat este folosit de analizor,
putem diagnostica eroarea respectiv i preciza construcia greit n programul surs.
Corectarea global const ntr-o secven minimal de modificri care trebuie aplicate
programului surs pentru a obine o intrare corect. Exist algoritmi care, avnd ca
intrare un ir incorect x i o gramatic G, vor genera arborele sintactic pentru irul
asociat y, astfel nct numrul de inserri, tergeri sau nlocuiri de uniti lexicale
necesare pentru a ajunge de la x la y s fie minim. Din pcate, aceste metode sunt n
general prea costisitoare n ceea ce privete spaiul necesar i timpul de lucru, aa c
prezint doar interes teoretic.
Multe structuri ale limbajelor de programare se construiesc recursiv i pot fi definite
cu ajutorul gramaticilor independente de context. De exemplu, putem avea o
instruciune condiional definit printr-o regul ca:
Dac S1 i S2 sunt instruciuni i E este o expresie atunci
(3.1)
if E then S1 else S2 este o instruciune.
Aceast form a instruciunii condiionale nu poate fi specificat utiliznd expresiile
regulate, cum se ntmpla n cazul unitilor lexicale. Pe de alt parte, utiliznd o
variabil sintactic instr pentru clasa instruciunilor i expr pentru clasa expresiilor,
putem exprima (3.1) cu urmtoarea regul (producie) gramatical:
instr if expr then instr else instr
(3.2)
TEHNICI DE COMPILARE
(3.4)
instr
if
expr
E1
instr
instr
then
if
expr then
E2
if
instr
S1
else
instr
S2
expr
then
instr
instr
else
S2
E1
if
expr
E2
then
instr
S1
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
sunt A_producii i intrarea ncepe cu un cuvnt nevid derivat din , nu tim dac s
extindem A la 1 sau la 2. Oricum, putem amna decizia extinznd A la A. Apoi,
dup ce am vzut intrarea derivat din , extindem A la 1 sau la 2, n funcie de
noua intrare. Astfel, factorizate la stnga, produciile iniiale devin:
A A
A 12
Algoritmul 3.2. Factorizarea la stnga a unei gramatici
Intrare: o gramatic G
Ieire: o gramatic factorizat la stnga G, echivalent cu G
Metod: pentru fiecare neterminal A se gsete cel mai lung prefix comun pentru
dou sau mai multe din alternativele lui A. Dac , nlocuiete toate A_regulile
A 12n
unde reprezint toate alternativele care nu ncep cu , cu regulile
A A
A 12n
unde A este un neterminal. Se repet acest proces pn nu mai exist dou alternative
ale aceluiai neterminal care s aib un prefix comun.
Exemplul 3.5. S considerm urmtoarea gramatic :
S iEtSiEtSeSa
Eb
(3.9)
Factoriznd la stnga aceast gramatic obinem:
S iEtSSa
S eS
(3.10)
Eb
Astfel, extindem S la iEtSS cu intrarea i i ateptm pn iEtS este vzut n
intrare pentru a decide dac extindem S la eS sau la . Evident, gramaticile (3.9) i
(3.10) sunt amndou ambigue i, cu intrarea e, nu va fi clar ce alternativ s folosim
pentru S.
TEHNICI DE COMPILARE
S
d
(a)
S
d
b
(b)
(c)
TEHNICI DE COMPILARE
E :
T
+
E
1
4
T:
T :
10
F
*
T
8
11
T
12
13
F:
14
15
16
17
id
TEHNICI DE COMPILARE
+
E :
E :
ur
m
tT
o
ar
+
ea
(a)
E:
(b)
+
E:
(c)
(d)
Fig. 3.7. Diagrame de tranziie simplificate.
Aceeai tehnic o aplicm diagramelor pentru Ti T. Setul rezultat este prezentat n
Fig. 3.8. O implementare n limbajul C a diagramelor din Fig. 3.8. ruleaz cu 20_25
mai repede dect o implementare a diagramelor din Fig. 3.6.
+
E:
T:
F:
14
13
15
16
)
17
id
TEHNICI DE COMPILARE
Stiva
X
Y
Z
a +
b $
program de
analiz
predictiv
Ieire
tabela de
analiz
M
Fig. 3.9. Model al analizorului predictiv nerecursiv
Un astfel de analizor conine un buffer de intrare, o stiv, o tabel de analiz i un
set de reguli sau eroare ca ieire. Bufferul de intrare conine irul ce va fi analizat,
terminat prin $.
Stiva conine o secven de simboluri gramaticale cu $ la baz. Iniial, stiva conine
simbolul de start al gramaticii, deasupra lui $.Tabela de analiz este un tablou
bidimensional MA, a, unde A este un neterminal i a este un terminal sau $.
Analiza este controlat de un program care se comport dup cum urmeaz.
Programul consider X, simbolul din vrful stivei i a, simbolul curent de intrare.
Aceste dou simboluri determin aciunea pe care o va face analizorul. Exist trei
posobiliti n acest sens:
1. Dac X a $ analizorul anun analiz cu succes a intrrii.
2. Dac Xa$ analizorul scoate X de pe stiv i deplazeaza pointerul anticipativ
o poziie spre dreapta n intrare.
3. Dac X este un neterminal, programul consult MX, a. Acest element este fie
o X_producie a gramaticii, fie mesaj de eroare.
Dac MX, aX UVW , analizorul nlocuiete X din vrful stivei prin UVW (cu
U n vrf). Ca ieire, analizorul va scoate producia folosit (se poate executa orice alt
cod aici).
Dac MX, a eroare, analizorul apeleaz o rutin de tratare a erorii.
Comportarea analizorului este descris de urmtorul algoritm:
Algoritmul 3.3. Analiza predictiv nerecursiv.
Intrare: un cuvnt w i o tabel de analiz M pentru gramatica G.
Ieire: dac w este n L(G), o derivaie la stnga a lui w; altfel, mesaj de eroare.
Pag.57
TEHNICI DE COMPILARE
Metod: iniial, stiva conine $S, cu S simbolul iniial al gramaticii G n vrf; bufferul
de intrare conine w$. Programul care utilizeaz tabela de analiz M pentru a produce
un arbore sintactic pentru irul de intrare este prezentat n Fig. 3.10.
reseteaz ip la primul simbol din w$;
repeat
fie X simbolul din vrful stivei i a simbolul indicat de ip;
if X este un terminal sau $
then
if X a
then scoate X de pe stiv i avanseaz ip
else eroare()
else X este un neterminal
if MX, a X Y1Y2 Yk
then begin
scoate X de pe stiv;
pune Yk, Yk-1 ,Y1 pe stiv, cu Y1 n vrf;
scoate producia X Y1Y2 Yk;
end
else eroare()
until X $ stiva este vid
E
E TE
E TE
E
E+TE
E
E
T
TT
TFT
T
T
TFT
T
T
F
Fid
F(E)
Fig. 3.11. Tabela de analiz pentru gramatica din (3.7.).
3.2.2.1.Construirea tabelei de analiz
Pentru construirea tabelei de analiz se folosesc dou funcii asociate
gramaticii G i anume FIRST i FOLLOW.
Dac este un cuvnt format din simboluri ale gramaticii G, atunci FIRST() este
mulimea terminalelor cu care ncep cuvintele derivate din . Dac deriveaz ,
atunci este i el n FIRST().
Dac A este un neterminal, atunci FOLLOW(A) este mulimea terminalelor
care pot *apare imediat dup A ntr-o form propoziional; adic mulimea
terminalelor a pentru care exist o derivaie S Aa, cu i cuvinte oarecare.
Pag.58
TEHNICI DE COMPILARE
Dac A este cel mai din dreapta simbol ntr-o form propoziional, atunci $ este n
FOLLOW(A).
Funciile First i Follow se construiesc conform urmtorilor algoritmi.
Algoritm de calcul al lui FIRST(X):
Pentru fiecare simbol gramatical X, se aplic urmtoarele reguli pn cnd nici un
terminal sau nu mai poate fi adugat unei mulimi:
1. Dac X este simbol terminal, atunci FIRST(X){X}.
2. Dac X este o producie, atunci adaug la FIRST(X).
3. Dac X este neterminal i X Y1Y2Yk este o producie, atunci, dac
pentru un i avem FIRST(Yj ) cu j1,2, ,i1, adaug FIRST(Yi ) la FIRST(X);
Dac FIRST(Yj ) pentru j 1,2,,k atunci adaug la FIRST(X).
Putem acum afla FIRST() pentru orice cuvnt X1X2Xn astfel:
- Toate simbolurile diferite de din FIRST(X1) sunt adugate la
FIRST(X1X2Xn);
- Dac este n FIRST(X1) toate simbolurile diferite de din FIRST(X2) sunt
adugate la FIRST(X1X2Xn);
- Dac este n FIRST(X1) i n FIRST(X2) atunci toate simbolurile diferite de
din FIRST(X3) sunt adugate la FIRST(X1X2Xn) i aa mai departe.
- Dac este n FIRST(Xi) pentru i1,2,,n atunci adaug la
FIRST(X1X2Xn).
Algoritm de calcul al lui FOLLOW(A):
Pentru toate neterminalele A, se aplic urmtoarele reguli pn cnd nimic nu
mai poate fi adugat:
1. Pune $ n FOLLOW(S), unde S este simbolul iniial i $ este marcatorul
de sfrit al intrrii.
2. Dac exist o producie A B, atunci tot ce conine FIRST(), n
afar de simbolul , este adugat la FOLLOW(B).
3. Dac exist o producie A B sau o producie
A B cu
FIRST() atunci tot ce conine FOLLOW(A) este adugat la
FOLLOW(B).
Prezentm n continuare coninutul succesiv al stivei, al benzii de intrare i
regulile furnizate la ieire de analizorul predictiv cu irul de intrare:
id + id id$
Pag.59
TEHNICI DE COMPILARE
STIV
INTRARE
IEIRE
$E
id + id id$
$E T
id + id id$ E TE
$E T F id + id id$ T FT
$E T id id + id id$ F id
$E T
+ id id$
T
$E
+ id id$
E +TE
$E T +
+ id id$
$E T
id id$
T FT
$E T F id id$
F id
$E T id id id$
$E T
id$
T FT
$E T F id$
id$
F id
$E T F id$
$E T id $
T
$
$E T
E
$
$E
$
Fig. 3.12. Aciunile analizorului predictiv cu intrarea id+idid.
Exemplul 3.9. Dac considerm gramatica
E TE
E +TE
T FT
T FT
F (E) id
atunci:
FIRST(E) FIRST(T) FIRST(F) { ( , id }
FIRST(E ) { + , }
FIRST(T ) { , }
FOLLOW(E) FOLLOW(E ) { ) , $ }
FOLLOW(T) FOLLOW(T ) { + , ) , $ }
FOLLOW(F) { + , , ) , $ }.
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
Dac M[A, a]sinc, atunci neterminalul din vrful stivei este eliminat n ncercarea
de a relua analiza. Dac unitatea lexical din vrful stivei nu se potrivete cu simbolul
de intrare, unitatea lexical respectiv este eliminat.
NeterSimbol de intrare
minal
$
id
E
sinc
E TE
E TE sinc
E
E TE
E E
T
sinc
sinc
sinc
T FT
T FT
T
T T FT
T T
F
sinc
sinc
F id
F (E)
Fig. 3.14. Tabela de analiz pentru gramatica din (3.7).
Cu intrarea greit )id+id analizorul i mecanismul de tratare a erorilor din
Fig. 3.14. se comport ca n Fig. 3.15.
Nivelul expresie (propoziie) este implementat prin completarea elementelor
nedefinite din tabela de analiz cu pointeri ctre rutine de tratare a erorii. Aceste rutine
pot schimba, insera sau terge simboluri din intrare i afia mesaje de eroare adecvate.
Pot, de asemenea, elimina elemente de pe stiv. n orice caz, trebuie s ne asigurm c
nu exist posibilitatea intrrii ntr-un ciclu infinit.
STIV
$E
$E
$E T
$E T F
$E T id
$E T
$E T F
$E T F
$E T
$E
$E T+
$E T
$E T F
$E T id
$E T
$E
$
INTRARE
) id + id$
id + id$
id + id$
id + id$
id + id$
+ id$
+ id$
+ id$
+ id$
+ id$
+ id$
id$
id$
id$
$
$
$
REMARC
Eroare, sare )
id este n FIRST(E)
TEHNICI DE COMPILARE
Pag.63
TEHNICI DE COMPILARE
STIV
sm
Xm
sm-1
Xm-1
s0
a1 .ai an $
Program de
analiz LR
aciune
IEIRE
goto
TEHNICI DE COMPILARE
Pag.65
TEHNICI DE COMPILARE
aciune
Stare
id
goto
)
E T F
1 2 3
0
s5
s4
1
s6
acc
2
r2 s7
r2 r2
3
r4 r4
r4 r4
4
s5
s4
8 2 3
5
r6 r6
r6 r6
6
s5
s4
9 3
7
s5
s4
10
8
s6
s11
9
r1 s7
r1 r1
10
r3 r3
r3 r3
11
r5 r5
r5 r5
Fig. 3.18. Tabel de analiz pentru gramatica expresiilor aritmetice.
Codurile din tabel sunt:
1. si trece n starea i
2. rj reduce cu producia (j)
3. acc accept
4. elementele nedefinite nseamn eroare.
Cu intrarea idid+id, analizorul se comport ca n Fig. 3.19. n linia (1) analizorul
are 0 pe stiv i id simbolul curent de intrare. Deoarece aciune[0, id]s5, analizorul
deplaseaz id i 5 pe stiv (trece n starea 5) cum se poate vedea n linia (2). Acum
aciune[5, ]r6, ceea ce nseamn c se face o reducere cu regula (6), adic cu Fid.
Sunt scoase dou simboluri de pe stiv i deoarece goto[0, F]3, sunt adugate F i 3
pe stiv. Restul simbolurilor din intrare sunt tratate similar.
STIVA
INTRARE
ACIUNE
(1)0
id id + id $ Deplasare
(2)0 id 5
id + id $ Reduce cu F id
(3)0 F 3
id + id $ Reduce cu T F
(4)0 T 2
id + id $ Deplasare
id + id $ Deplasare
(5)0 T 2 7
+ id $ Reduce cu F id
(6)0 T 2 7 id 5
+ id $ Reduce cu T T F
(7)0 T 2 7 F 10
(8)0 T 2
+ id $ Reduce cu E T
(9)0 E 1
+ id $ Deplasare
(10)0 E 1 + 6
id $ Deplasare
(11)0 E 1 + 6 id 5
$ Reduce cu F id
(12)0 E 1 + 6 F 3
$ Reduce cu T F
(13)0 E 1 + 6 T 9
$ Reduce cu EE+T
(14)0 E 1
$ Accept
Fig. 3.19. Comportarea analizorului LR cu intrarea idid+id.
Pag.66
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
Operaia nchidere
Dac I este o mulime de iteme ale gramaticii G, atunci nchidere(I) este o mulime de
iteme construit din I aplicnd urmtoarele reguli:
1. Iniial, toate itemele din I sunt n nchidere(I): nchidere(I):= I
2. Dac AB este n nchidere(I) i B este o producie, atunci adaug
itemul B la nchidere(I) (dac nu este deja acolo). Se aplic aceast regul pn
cnd nu mai putem aduga nici un item nou la nchidere(I).
Intuitiv un item AB este n nchidere(I) arat c, la un anumit moment, n
procesul de analiz, dup ce l-am vzut pe , ne gndim ca am putea vedea un subir
derivat din B. Dac B este o producie ne putem astepta s vedem un ir derivat
din la acest punct. Acesta este motivul includerii produciei B n nchidere(I).
Exemplul 3.14. Fie gramatica extins pentru expresii aritmetice:
E E
E E + T T
T T F F
(3.11)
F (E)id
Dac I este mulimea de iteme {[E.E]}, atunci nchidere(I) va conine itemele:
E E
E E + T
E T
T T F
T F
F (E)
F id
Functia nchidere(I) se calculeaza cu algoritmul din fig. 3.20.
Mutimile de itemuri pot fi mprite n dou clase:
Itemuri nucleu, care includ itemul iniial, S .S, i toate itemurile a cror
punct nu se afl la captul stng;
Itemuri nenucleu, care au punctul la captul drept.
Fiecare mulime de itemuri care ne intereseaz este format din nchiderea unei
mulimi de itemuri nucleu. itemurile adugate fiind nenucleu. Atunci mulimea de
itemuri se poate reprezenta numai prin nucleu, restul putnd fi construite.
function inchidere(I );
begin
J:=I;
repeat
for fiecare item AB din J i fiecare productie gramatical B
astfel incat B. nu este in J do
adauga B.
until nici un item nu mai poate fi adugat
return J;
end
Figura 3.20.
Pag.68
TEHNICI DE COMPILARE
Operaia goto
Pentru o mulime de iteme I i X un simbol al gramaticii, definim goto(I, X)
nchiderea mulimii itemelor de forma [AX] astfel nct [AX] s fie n I.
Intuitiv, dac I este mulimea itemelor valide pentru un prefix viabil , atunci goto(I,X)
este mulimea itemelor valide pentru un prefix viabil X.
Exemplul 3.15. Dac I{[E E], [E E + T] }, atunci
goto(I, +) conine
itemele:
E E + T
T T F
T F
F (E)
F id
I1:
I2:
E E
E E + T
E T
T T F
T F
F (E)
F id
E E
EE + T
ET
TT F
I7:
I4:
F (E )
E E + T
E T
T T F
T F
F (E)
F id
I5:
Fid
I6:
EE + T
T T F
T F
F (E)
F id
I8:
TT F
F (E)
F id
F(E)
EE + T
I9:
EE + T
TT F
I10:
TT F
I11:
F(E)
Pag.69
TEHNICI DE COMPILARE
I3:
TF
I1
I6
T
F
(
id
I2
I7
F
(
id
I3
(
I4
id
spre I7
spre I3
spre I4
spre I5
I10
spre I4
spre I5
(
)
I11
I8
+
id
I9
spre I6
spre I2
spre I3
I5
derivaie la dreapta
Pag.70
TEHNICI DE COMPILARE
SAw12w
Faptul c A12 este valid pentru 1 ne ajut s decidem dac facem o deplasare
sau o reducere atunci cnd gsim 1 pe stiva analizorului. n particular, dac 2,
atunci se sugereaz c nu am gsit nc un subcuvnt ce poate fi redus i deci trebuie
s facem o deplasare. Dac 2, atunci se pare c A1 este o producie cu care ar
trebui s reducem. Desigur, dou iteme valide pot s ne spun lucruri diferite despre
acelai prefix viabil. Unele din aceste conflicte pot fi rezolvate dac privim nainte i
urmtorul simbol de intrare, dar exist conflicte care nu pot fi rezolvate dac vom
folosi metodele de analiz LR pentru a construi tabele de analiz pentru gramatici
arbitrare.
Exemplul 3.18. S considerm gramatica (3.11), ale crei colecie canonic i funcii
goto sunt prezentate n Fig. 3.22. i 3.23. Evident, cuvntul E+T este un prefix viabil
pentru gramatica (3.11). Automatul din Fig. 3.23. se va afla dup citirea acestui prefix
n starea I7, care conine itemele
T T F
F (E)
F id
care sunt exact itemele valide pentru E+T. Pentru a vedea acest lucru s considerm
urmtoarele trei derivaii la dreapta:
E
E E EE E
E+TE+TE+T
E + T F E + T F E + T F
E + T (E) E + T id
Prima derivaie arat validitatea lui TTF, a doua, validitatea lui F(E) i a treia,
validitatea lui Fid pentru prefixul viabil E+T. Se poate demonstra c nu exist alte
iteme valide pentru E+T, dect cele din I7.
Algoritmul construciei tabelei de analiz SLR
Putem prezenta acum modul de construcie al funciilor aciune i goto ale unei tabele
de analiz SLR din automatul finit determinist care recunoate prefixe valide.
Algoritmul 3.6. Construcia tabelei de analiz SLR
Intrare: o gramatic extins G.
Ieire: funciile aciune i goto ale tabelei de analiz SLR pentru G.
Metod:
1. Se construiete C{I0, I1, ,In}, colecia mulimilor de iteme LR(0) pentru G.
2. Starea i este construit din Ii. Aciunile de analiz pentru starea i sunt
determinate n felul urmtor:
a) dac [Aa] este n Ii i goto(Ii, a)Ij atunci definete aciune[i, a]j; n
acest caz a este un terminal
Pag.71
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
I0:
I1:
I :
2
S S
S L R
S R
L R
L id
R
L
S S
SL R
RL
I3:
I:
4
I5:
S R
L R
L R
L id
R L
Lid
I6:
I7:
SL R
L R
L id
R L
LR
I8:
RL
I9:
SL R
Dac considerm I2, primul item definete aciune[2,]s6, dar FOLLOW(R) conine
i deci al doilea item definete aciune[2,]reduce cu RL. Deci, aciune[2,]
este definit multiplu, ceea ce nseamn c avem un conflict deplasarereducere n
starea 2 cu simbolul de intrare .
3.3.2.Tratarea erorilor n analiza LR
Un analizor LR va detecta o eroare atunci cnd va consulta tabela aciune i va
gsi un element nedefinit. Erorile nu sunt niciodat detectate cnd se consult tabela
goto.
n analiza LR putem implementa modul panic de tratare a erorilor n felul
urmtor. Citim n jos pe stiv pn cnd este gsit o stare s cu goto pentru un
neterminal A. Zero sau mai multe simboluri de intrare sunt atunci ignorate pn cnd
este gsit un simbol a care poate urma dup A. n acest moment analizorul reia
analiza. Ar putea exista mai multe alegeri pentru neterminalul A. n mod normal,
acetia vor fi neterminali reprezentnd pri de program majore, cum ar fi expresii,
instruciuni sau blocuri. De exemplu, dac A este un neterminal instr atunci a ar putea
fi punct-i-virgul sau end.
Aceast metod ncearc s izoleze fraza care conine eroarea sintactic. Nivelul
propoziie de tratare a erorilor este implementat prin examinarea fiecrui element
nedefinit al tabelei aciune i identificarea celei mai frecvente erori de programare care
ar putea s determine apelul acelui element nedefinit. n acest moment, poate fi scris
o rutin de tratare a erorii care s modifice stiva sau intrarea n funcie de aciunea care
a provocat acea eroare.
Exemplul 3.27. S considerm din nou gramatica pentru expresii aritmetice (3.14).
Fig. 3.31. reprezint tabela de analiz LR a acestei gramatici modificat pentru a trata
i corecta erorile. Am schimbat toate strile care apeleaz o reducere cu un simbol de
intrare nlocuind toate elementele nedefinite cu acea reducere. Efectul este de a
ntrzia detectarea erorii pn cnd una sau mai multe reduceri vor fi fcute, dar
eroarea va fi oricum detectat nainte ca o nou deplasare s fie efectuat. Restul
elementelor nedefinite au fost completate cu pointeri ctre rutine de tratare ale erorilor
specifice.
Pag.73
TEHNICI DE COMPILARE
aciune
Stare
id
0
1
2
3
4
5
6
7
8
9
s3
e3
s3
r4
s3
s3
e3
r1
r2
r3
e1
s4
e1
r4
e1
e1
s4
r1
r2
r3
e1
s5
e1
r4
e1
e1
s5
s5
r2
r3
(
s2
e3
s2
r4
s2
s2
e3
r1
r2
r3
goto
)
e2 e1
e2 acc
e2 e1
r4 r4
e2 e1
e2 e1
s9 e4
r1 r1
r2 r2
r3 r3
E
1
6
7
8
Fig. 3.31. Tabel de analiz pentru gramatica (3.14) cu rutine de tratare a erorilor.
Rutinele de tratare a erorilor au urmtoarele semnificaii:
e1: apelat n strile 0, 2, 4 i 5 cnd se atepta un operand, id sau (; n locul
acestora, un operator + sau , sau sfritul intrrii a fost gsit. Pune un id fictiv pe
stiv i apoi adaug starea 3. Afieaz diagnosticul lipsete un operand.
e2: apelat n strile 0, 1, 2, 4 i 5 cnd se gsete o parantez ). terge ) din
intrare. Afieaz diagnosticul parantez neechilibrat.
e3: apelat n strile 1 i 6 cnd se atepta un operator i un id sau ) au fost
gsite. Pune + pe stiv i apoi adaug starea 4. Afieaz diagnosticul lipsete un
operator.
e4: apelat n starea 6 cnd este gsit sfritul intrrii n locul unui operator sau
). Pune ) pe stiv i apoi adaug starea 9. Afieaz diagnosticul lipsete ).
Cu intrarea eronat id + ) analizorul se comport ca n figura 3.32.
Stiv
Intrare Mesaj de eroare i aciune
0
id+)$
0id3
+)$
0E1
+)$
0E1+4
)$
parantez neechilibrat
0E1+4
$
0E1+4id
3
0E1+4E7
0E1
$
$
Pag.74
TEHNICI DE COMPILARE
Yacc compiler
y.tab.C
intrare
b.out
C compiler
b.out
ieire
Fig. 3.33.
Fiierul translate.y conine specificaiile Yacc. Compilatorul Yacc (care poate
fi apelat n Unix prin comanda yacc translate.y) transform fiierul translate.y ntr-un
program C numit y.tab.c, care este reprezentarea unui analizor sintactic n limbajul C.
Folosind compilatorul C programul este transformat ntr-un program executabil, bout. Programul poate fi compilat n C mpreun cu o subrutin yylex(), scris de
utilizator sau produs de un generator de analizor lexical, Lex. Subrutina yylex()
trebuie s furnizeze urmtorul token din irul de intrare, la fiecare apelare a sa.
Utilizatorul YACC-ului pregtete o specificaie a procesului de intrare, ce
include reguli ce descriu structura de intrare, codul de executat la recunoaterea acestor
reguli i o rutin pentru a efectua procesul de intrare. Clasa specificaiilor acceptate e
foarte general: gramatici LALR(1) cu reguli fr ambiguiti.
YACC-ul este scris ntr-un C portabil, aciunile i rutinele de ieire de asemenea, i
multe din conveniile sintactice ale YACC-ului urmeaz C-ul.
O parte important a procesului de intrare este realizat de analizorul lexical, care
citete fluxul de intrare, recunoate structurile de nivel inferior, token-ii, i i comunic
analizorului sintactic. Din motive istorice, o structura recunoscut de analizorul lexical
se numeste simbol terminal, iar o structura recunoscut de analizorul sintactic simbol
non-terminal. Pentru a evita confuziile, terminalele vor fi referite ca token-i.
Intrarea poate s nu corespund specificaiilor i n aceste cazuri YACC-ul nu
reusete s produc un analizor sintactic. De exemplu, specificaiile pot fi contradictorii
sau ele pot cere un mecanism de recunoatere mai puternic dect cel disponibil n
Pag.75
TEHNICI DE COMPILARE
YACC. n prima situaie e vorba de erori, iar cea de-a doua poate fi n general corectat
fcnd analizorul lexical mai puternic sau rescriind o parte din regulile gramaticale.
3.4.2.Specificaii de baz
Identificatorii ce apar n specificatii refera fie token-i fie simboluri non-terminale.
YACC-ul cere ca numele de token-i s fie declarate. n plus, adesea e de dorit s se
includ analizorul lexical ca parte a fiierului de specificaii, care poate s includ i alte
programe.
Un fiier cu specificaii const din 3 seciuni separate prin %% :
declaratii
%%
reguli
%%
programme C
unde A reprezint un nume de non-terminal, iar CORP reprezint o secven de zero sau
mai multe nume i literale. Dou puncte, i punct i virgula, sunt punctuaii YACC.
Numele pot avea orice lungime i pot fi alctuite din litere, punct, "underscore",
i cifre, dar trebuie s nceap cu o liter. Literele mici sunt distincte de cele mari.
Numele folosite n corpul unei reguli gramaticale pot reprezenta token-i sau nonterminale.
Un literal const dintr-un caracter inclus ntre apostroafe. Ca n C, "backslash"-ul, e un
caracter "escape". Toate secvenele "escape" din C sunt recunoscute: \' n' = "newline",
'\r' = "return", '\'' = apostrof, '\\' = "backslash", '\t' = "tab", '\b' = "backspace", '\f' =
"formfeed", '\xxx' = xxx n octal. Caracterul NUL ('\0' sau 0) nu trebuie folosit niciodat
n regulile gramaticale.
Daca exist mai multe reguli gramaticale cu aceeai parte stng, se poate folosi
bara vertical '|' pentru a evita rescrierea. n plus, punct i virgula de la sfiritul unei
reguli pot fi eliminate cnd urmeaz o bar vertical. Astfel regulile:
A:BCD;
A:EF;
A:G;
TEHNICI DE COMPILARE
Sfritul intrrii e semnalat de un token special, numit marcator de sfrit ("endmarker"). Dac token-ii citii pna la "end-marker" formeaz o structur ce se potrivete
cu simbolul de start, dup ce e ntlnit "end-marker"-ul analizorul revine la apelantul
su i accept intrarea.
Dac marcatorul de sfrit e "vzut" n orice alt context atunci exist o eroare.
Analizorul lexical are sarcina de a returna "end-marker"-ul. n mod normal acesta
este o valoare de I/E ca "end-of-file" sau "end-of-record".
Pentru a obine valori returnate de aciuni precedente sau de analizorul lexical, se pot
folosi pseudo-variabilele: $1, $2, ... , care refer valorile returnate de componentele din
partea dreapt a regulei curente, citind de la stnga la dreapta. Astfel, pentru:
A:BCD;
TEHNICI DE COMPILARE
Valoarea returnat de aceasta regula e de obicei valoarea lui expr dintre paranteze.
Acest lucru poate fi precizat astfel:
expr : '(' expr ')'
{ $$=$2;}
Implicit, valoarea unei reguli e valoarea primului sau element, $1. Astfel, regulile
gramaticale de forma:
A : B;
TEHNICI DE COMPILARE
yylval=c-'0';
return(DIGIT);
}
{printf(%d\n, $1);}
{ $$ = $1 + $3; }
Pag.79
TEHNICI DE COMPILARE
|
term
;
term :
term * factor
|
factor
;
factor :
( expr )
|
DIGIT
;
%%
yylex() {
int c;
c = getchar();
if(isdigit(c)) {
yylval = c - 0;
return DIGIT;
}
return c;
}
{ $$ = $1 + $3; }
{$$ = $2; }
n ipoteza c se folosete fiierul realizat n Lex, yy.lex.c, atunci programul este identic
cu programul anterior cu excepia ultimei seciuni:
Exemplul 3.29. Varianta II
%{
#include <ctype.h>
%}
%token DIGIT
%%
line :
expr \n
;
expr :
expr + term
|
term
;
term :
term * factor
|
factor
;
factor :
( expr )
|
DIGIT
;
%%
#include lex.yy.c
{printf(%d\n, $1);}
{ $$ = $1 + $3; }
{ $$ = $1 + $3; }
{$$ = $2; }
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
3.4.7. Precedena
Exist o situaie n care regulile de rezolvare a conflictelor date mai sus nu sunt
suficiente, i anume n analiza expresiilor arimtetice. Multe dintre construciile folosite
pentru expresii aritmetice pot fi n mod natural descrise prin noiunea de nivele de
precedent ale operatorilor, mpreuna cu informaie legat de asociativitatea stng sau
dreapt. Deci, se pot folosi gramatici ambigue, dar cu reguli de dezambiguizare
potrivite, pentru a crea analizoare mai rapide i mai uor de scris dect cele construite
din gramatici neambigue. Reguli gramaticale de forma:
expr : expr OP expr
i
expr : UNARY expr
scrise pentru toi operatorii unari i binari dorii, formeaz baza specificaiilor n acest
caz. Se creaz o gramatic foarte ambigu, cu multe conflicte. Ca reguli de
dezambiguizare, utilizatorul specific precedena tuturor operatorilor i
asociativitatea operatorilor binari. Aceast informaie este suficient pentru ca
YACC-ul s rezolve conflictele de analiz n acord cu aceste reguli, i s construiasc
un analizor care realizeaz precedenele i asociativitile dorite.
Precedenele i asociativitile sunt ataate token-ilor n seciunea de declaraii,
printr-o serie de linii ce conin un cuvnt cheie YACC: % left, %right sau % nonassoc,
urmat de o lista de token-i. Toi token-ii de pe aceai linie vor fi tratai ca avnd acelai
nivel de preceden i asociativitate. Liniile trebuie date n ordinea cresctoare a
precedenei (a puterii de legare). Astfel regulile:
% left '+' '-'
% left '*' '/'
descriu precedena i asociativitatea celor 4 operatori aritmetici. Plus i minus sunt
asociative la stnga i au precedena mai mic dect stea i "slash", i ele asociative la
stnga. %right e folosit pentru a descrie asociativitatea dreapt pentru operatori, iar
%nonassoc e folosit pentru a descrie operatori cum ar fi .LT. din FORTRAN, care nu
poate asocia cu el nsui. Astfel:
A .LT. B .LT. C
este ilegal. De exemplu, specificaia:
%right '='
%left '+' '-'
%left
%%
expr :
|
|
|
|
|
;
'*' '/'
expr '=' expr
expr '+' expr
expr '-' expr
expr '*' expr
expr '/' expr
NAME
TEHNICI DE COMPILARE
'+' '-'
'*' '/'
expr '+' expr
expr '-' expr
expr '*' expr
expr '/' expr
'-' expr %prec '*'
NAME
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
Dup cum s-a menionat, token-ul vzut imediat dup simbolul "error" e token-ul
de intrare la care a fost depistat eroarea. Uneori, acest lucru poate fi nepotrivit. De
exemplu, o aciune de acoperire a unei erori ar putea s ia asupra sa responsabilitatea de
a gsi locul corect pentru reluarea analizei. n acest caz, token-ul "look-ahead"
precedent trebuie ters. Instructiunea: yyclearin; , efectueaz acest lucru. S
presupunem ca aciunea dup eroare era de a apela o rutin de resincronizare sofisticat,
furnizat de utilizator, care a ncercat s avanseze intrarea la nceputul urmtoarei
instruciuni valide. Dup ce aceast rutin a fost apelat, urmtorul token returnat de
yylex() este probabil primul token dintr-o instruciune legal. Vechiul token, ilegal,
trebuie nlturat i starea de eroare trebuie resetat. Acestea potfi realizate astfel:
stat : error { resync();
;
yyerrok;
yyclearin; }
Aceste mecanisme sunt relativ dure, dar permit o refacere simpl a analizorului
din multe erori. Mai mult, cnd este necesar, utilizatorul poate obine controlul pentru a
se ocupa de aciunile de eroare cerute de alte poriuni din program.
3.5. REZUMAT
Analizorul sintactic are ca intrare irul de uniti lexicale obinut n faza de
analiz lexicala, iar ieirea este rspunsul nu, dac irul de intrare nu este corect din
punct de vedere sintactic, sau da, n ipoteza c irul de intrare este corect. n acest
ultim caz analizorul sintactic construiete i un arbore sintactic din irul unitilor
lexicale, arbore pe care l transmite analizurului semantic.
Analiza sintactic poate s fie de tip top-down (descendent) atunci cnd arborele
sintactic este construit pornind de la rdcin spre rezultat, sau de tip bottom-up
(ascendent), atunci cnd arborele sintactic este construit pornind de la rezultat spre
rdcin.
n esen analizorul sintactic este un automat push-down nedeterminist, pentru c
regulile sintactice ale limbajelor de programare verificate de acest analizor sunt reguli
independente de context i deci implementarea este destul de dificil. Se pot construi i
analizoare deterministe pentru anumite gramatici ale limbajelor de programare i anume
pentru gramatici de tip LL(k) i LR(k). n acest capitol au fost prezentai algoritmi de
analiz top-down determinist (analiza predictiv recursiv i nerecursiv) pentru
gramatici de tip LL(1), i algoritmi de analiz bottom-up pentru gramatici de tip LR.
S-au prezentat i problemele de implementare ale algoritmilor i problemele de
tratare a erorilor, la fiecare clas de algoritmi n parte, iar n final s-a prezentat
generatorul analizor sintacticYACC. Acest generator, care poate s apeleze la rndul lui
analizorul lexical construit de LEX, permite realizarea unui analizor sintactic ntr-un
mod foarte simplu.
Pag.85
TEHNICI DE COMPILARE
Exerciii
1. Construii o gramatic pentru expresii pentru fiecare din limbajele urmtoare:
a. Pascal;
b. C;
c. Java.
2. Considerai gramatica:
S( L ) a
LL, S S
a. Care sunt terminalele i care sunt neterminalele din gramatic;
b. Gsii arborele de sintax pentru urmtoarele secvene:
i.
(a,a)
ii.
(a,(a,a))
iii. (a,(a,a))
iv.
(a,((a,a),(a,a)))
c. Construii o derivaie la stnga i una la dreapta pentru fiecare din
secvenele de la 2.b.
3. a. Eliminai recursia stng din gramatica de la exerciiul 2.
b.Construii un analizor predictiv pentru gramatica de la 3.a.
4. Considerai gramatica:
SAS b
ASA a
a. Construii o colecie de itemuri LR(0) pentru aceast gramatic.
b. Construii o tabel de analiz de tip SLR pentru aceast gramatic.
5. Scriei un program Yacc care are ca intrare o expresie aritmetic iar ca ieire va
furniza arborele sintactic al expresiei regulate.
6. Scriei un analizor sintactic Yacc pentru expresii aritmetice.
Pag.86
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
independente de analiza sintactic. Una din aceste verificri care nu este direct este
verificarea tipurilor.
Un verificator de tipuri cerceteaz dac tipul unei construcii este cel ateptat ntrun context anume. Astfel se execut urmtoarele verificri:
Se verific dac numele i valorile sunt folosite n concordan cu regulile
limbajului;
Detecteaz conversiile de tip implicite, insernd o succesiune de
instruciuni acolo unde este necesar aceasta conversie.
De exemplu, operatorul aritmetic mod din Pascal necesit operanzi ntregi, deci
trebuie verificat dac operanzii unei operaii mod sunt ntregi. Similar, trebuie
verificat dac referina este aplicat unui pointer, indexarea este fcut unui tablou,
funciile definite de utilizator sunt aplicate numrului i tipului corect de argumente,
etc.
Un alt exemplu l constituie limbajul FORTRAN, unde variabilele a cror nume
ncepe cu una dintre literele I,J,K,L,M,N, sunt considerate implicit de tipul ntreg,
restul fiind implicit de tip real. n afar de aceste convenii implicite mai pot exista i
declaraii de tip explicite care modific convenia.
n PASCAL toate variabilele sunt declarate anterior utilizrii lor, deci aici se
verific dac sunt corect utilizate. i n limbajul PASCAL i n C exist un sistem de
tipuri mai complicat. Pe lng faptul ca se permite utilizarea tipurilor declarate
(integer, real, character, label, i altele) se pot construi noi tipuri (tabele
multidimensionale de variabile de un anumit tip sau structuri cu elemente de tipuri
arbitrare). Putem avea de asemenea pointeri pentru variabile de tipuri arbitrare. n C se
pot aplica reguli de derivare a noi tipuri n mod recursiv. Astfel, de exemplu, se poate
defini un array de array de funcii care returneaz valori, valori ce reprezint pointeri
pentru ntregi.:
Int ( *func) ( ) [100] [20];
Dei limbajul C are un sistem complex de tipuri, el nu necesit reguli
complicate de verificare a tipurilor. De exemplu tipurile argumentelor unei funcii nu
trebuiesc definite anterior i astfel este posibil s aplelm o funcie cu alte tipuri de
argumente dect se atepta iniial. Sistemul de tipuri este astfel conceput nct s
ajute programatorul s evite erorile semantice i nu s foreze comportarea lor. Astfel
de limbaje se numesc limbaje cu restricii de tip slabe.
n contrast cu acestea exist aa numitele limbaje cu restricii de tip tari, cum
ar fi ALGOL68 i ADA. n principiu astfel de limbaje previn programatorul la
apariia oricrei erori semantice datorate tipului. Dac astfel de sisteme de tipuri
sunt suficient de flexibile, compilatoarele pot deveni foarte mari i complexe, nu
numai din punct de vedere al compilrii, dar i din punct de vedere al timpului de
execuie.
Implementarea verificrii tipurilor se poate face n dou moduri:
Verificarea static a tipurilor, care se face n timpul compilrii;
Verificarea dinamic a tipurilor, care se realizeaz n timpul execuiei
programului. Aceasta este mai puin eficient dect cea static, dar dac un
limbaj permite determinarea tipurilor n faza de execuie atunci se va face
Pag.88
TEHNICI DE COMPILARE
Regul semantic
E.tip : integer
E.tip : real
E.tip : rectip(id.tip)
E.tip : dac E1.tip : integer i E2.tip : integer
atunci integer
altfel dac E1.tip : integer i E2.tip : real
atunci real
altfel dac E1.tip : real i E2.tip : integer
atunci real
altfel dac E1.tip : real i E2.tip : real
atunci real
altfel tip_eroare
TEHNICI DE COMPILARE
TEHNICI DE COMPILARE
i1000 i deci cteva sute de mii de operaii main sunt necesare pentru meninerea
tabelei; aceasta nu constituie neaprat o problem, dar dac n i i sunt multiplicate cu
10, costul se multiplic cu 100 i timpul de lucru cu tabela de simboluri poate ncetini
compilatorul.
cp
poz
ultim
20
stare
ws
32
210
TEHNICI DE COMPILARE
Pag.92
TEHNICI DE COMPILARE
(5.1)
(5.2)
Pag.93
TEHNICI DE COMPILARE
id1 := temp3
analizor lexical
id1 := id2 + id3 * 40
analizor sintactic
:=
+
id1
id2
*
id3
num(40)
analizor semantic
:=
+
id1
id2
*
id3
IntToReal(40)
TEHNICI DE COMPILARE
generarea codului
MOVF id3, R2
MULF #40.0, R2
MOVF id2, R1
ADDF R2, R1
MOVF R1, id1
id
id
id
num 40
5.1.2.Optimizarea codului
Faza de optimizare a codului ncearc s mbunteasc codul intermediar, n
special prin micorarea numrului de instruciuni. Astfel, codul intermediar din (5.2)
devine
temp1 := id3 * 40.0
id1 := id2 + temp1
(5.3)
TEHNICI DE COMPILARE
Exist compilatoare care aloc o mare parte din activitate acestei faze, aa
numitele compilatoare cu optimizare. n plus, exist metode de optimizare simple, care
mresc viteza de execuie a programului destinaie fr a ncetini prea mult viteza
compilatorului.
id3, R2
#40.0, R2
id2, R1
R2, R1
R1, id1
(5.4)
TEHNICI DE COMPILARE
BIBLIOGRAFIE
1. Aho A.V., Sethi ,Ullman D.J., The theory of parsing translation and
compiling, Addison -Wesley Reading, Massachusetts, 1986.
2. Bennett J.P., Introduction to compiling techniques, Mc Grow Hill, London,
1990.
3. Gries D., Compiler construction for digital computers, John Wiley, NewYork, 1971.
4. Hopcroft J.E., Ullman D.J., Formal language and their relation to automata ,
Addison - Wesley Reading, Massachusetts, 1969.
5. Livovschi L. ... Bazele informaticii, Ed. Did. si Ped., Bucuresti 1987.
6. Marcus S. Gramatici si automate finite,Ed. Academiei Romnia , 1964.
7. Orman G. Limbaje formale, Ed. Tehnic, Bucureti, 1982.
8. Paun Gh. Probleme actuale n teoria limbajelor formale , Ed. t. i
Enciclopedic, Bucureti,1984.
9. Marinescu D.Limbaje formale i teoria automatelor, Ed. Univ.
Transilvania, Braov, 2003.
10. Jalobeanu, C., Marinescu D.Bazele teoriei calculului: Limbaje Formale si
Automate, Ed. Albastra, Cluj-Napoca, 2006.
11. Marinescu D.Tehnici de compilare, Ed. Univ. Transilvania, Braov, 2004.
12. Salomaa A., Formal languages, Academic Press, New-York, 1973.
13. Simovici D., Limbaje formale i tehnici de compilare, Ed. Did. si Ped.,
Bucureti , 1978.
14. Tremblay J.P., Sorenson P.G., The Theory and Practice of Compiling, Mc.
Grow-Hill, New-York, 1985.
15. Holub A.I. Compiler Construction in C, Prentice Hall International, 1989.
Pag.97