Sunteți pe pagina 1din 45

FACULTATEA DE ELECTRONICA, TELECOMUNICATII SI TEHNOLOGIA INFORMATIEI

Compilatoare
Tema de casa Sisteme de Operare

Prof. coordonator:Doctor Inginer Stefan Stancescu


Agape Alexandra
Altiparmac Andreea
Grigoras Andra
433A

CUPRINS:
Agape Alexandra 433A:
1.Introducere i istorie............................................................................................ 4
1.1 Tipuri de compilatoare.................................................................................. 5
1.2Design-ul compilatorului................................................................................ 6
1.2Procesul de compilare.................................................................................... 7
1.4Structur general......................................................................................... 9
2.Descrierea BNF (Backus-Naur Form) a gramaticii unui limbaj...........................12
Exemplu 1...................................................................................................... 12
Exemplu 2 definirea unei cifre.....................................................................13
Exemplu 3 definirea unui numar intreg.......................................................13
2.1Descrierea EBNF (Extended BNF).................................................................13
Grigoras Andra 433A:
3.Analiza lexical.................................................................................................. 16
3.1 Token........................................................................................................... 16
3.2 Gramatica lexical...................................................................................... 17
3.3 Scanner-ul................................................................................................... 18
3.4 Evaluator-ul................................................................................................. 18
3.5 Generatorul de lexer................................................................................... 19
4. Analiza sintactica............................................................................................. 20
4.1 Parser.......................................................................................................... 20
4.2 Tipuri de parser........................................................................................... 22
5.Arborele sintactic.............................................................................................. 23
5.1 Aplicarea n compilatoare............................................................................23
5.2 Proiectarea unui arbore sintactic.................................................................24
5.3 LL parser..................................................................................................... 26
Exemplu......................................................................................................... 27
5.4 LR parser..................................................................................................... 28
2

Altiparmac Andreea-Mihaela 433A:


6.Generalitati....................................................................................................... 31
7.Introducere Lex................................................................................................. 32
7.1Sursa Lex...................................................................................................... 34
7.2Expresii regulate.......................................................................................... 34
Exemple de expresii regulate:........................................................................35
7.4Actiuni Lex.................................................................................................... 36
7.5Definitii sursa Lex......................................................................................... 37
Exemplu de program in Lex........................................................................... 38
8.Introducere Yacc................................................................................................ 39
8.1Specificatii Yacc............................................................................................ 39
8.2Actiuni Yacc.................................................................................................. 40
8.3Analiza lexicala............................................................................................ 41
Exemplu Yacc................................................................................................. 42
Bibliografie........................................................................................................... 44

AGAPE ALEXANDRA

1.Introducere i istorie
Un compilator este un program (sau un set de programe) care
transform codul surs scris ntr-un limbaj de programare (limbaj surs)
ntr-un alt limbaj (limbaj int, care au adesea un format binar cunoscut
sub numele de codul obiect).

Programul
surs

Compila
tor

Obiectivul
programului

Mesaje de eroare

Limbajul surs este ntotdeauna un limbaj de nivel superior, n


comparaie cu codul main, limbajul de asamblare fiind cel mai puin
compatibil limbaj(asamblorul fiind un caz special de compilator care
traduce limbajul de asamblare n codul main). Limbajele de nivel
superior sunt cele mai complexe, nu numai pentru c acestea cresc nivelul
de abstractizare ntre codul surs i codul main rezultat, ci pentru c
creterea complexitii este necesar pentru a formaliza aceste structure
abstracte.
Limbajul int este n mod normal un limbaj de nivel sczut (cum ar
fi limbajul de asamblare), scris cu abrevieri oarecum criptice pentru
instruciunile main, n acest caz rulnd, de asemenea, un limbaj de
asamblare pentru a genera codul main final. Dar unele compilatoare pot
genera direct codul main pentru un computer real sau virtual, de
exemplu, byte-code pentru Java Virtual Machine.
Cele mai multe compilatoare traduc codul surs ntr-un limbaj de
nivel nalt fa de codul obiect sau limbajul main, care pot fi executate
direct de ctre un calculator sau o main virtual. Cu toate acestea,
traducerea de la un limbaj de nivel sczut la un nivel nalt este, de
asemenea, posibil; acest lucru este, n mod normal, cunoscut c un
decompilator n cazul n care se reconstruiete un program de limbaj de
nivel nalt, care ar fi generat programul de limbaj de nivel sczut. Exist,
de asemenea, compilatoare care traduc de la un limbaj de nivel nalt la
altul (compilatoare cross), sau, uneori, la un limbaj intermediary care are
4

nc nevoie de o prelucrare ulterioar; acestea sunt cunsocute sub numele


de cascaders.
Compilatoarele de ieire, aa numitele obiecte care conin practice
codul main cu informaii despre numele i amplasarea de pucte de
intrare i apeluri externe (pentru funcii care nu sunt cuprinse n obiect).
Un set de fiiere obiect, care nu a fost nevoie s aparin aceluiai
compilator, cu condiia c compilatoarele folosite s aib un format de
ieire comun, poi fi legate mpreun pentru a cre executabilul final, care
poate fi rulat direct de ctre un utilizator.
Mai multe compilatoare experimentale au fost dezvoltate n anii
1950, dar echipa FORTRAN condus de John Backus de la IBM a fost
cunoscut c fiind cea care a introdus primul compilator complet n 1957.
COBOL a fost un limbaj care a fost compilat pe mai multe arhitecturi, n
1960.
Ideea de compilare a prins repede, iar cele mai multe dintre
principiile de design de compilare au fost dezvoltate n anii 1960.
Un compilator este el nsui un program scris ntr-un limbaj de
implemetare. Vechile compilatoare au fost scrise n limbaje de asamblare.
n timpul anilor 1990, un numr mare de compilatoare i instrumente de
dezvoltare ale compilatoarelor au fost dezvoltate pentru toate tipurile de
limbaje, fcnd parte att din proiectul GNU, ct i din alte iniiative opensource.

1.1 Tipuri de compilatoare


Un compilator poate produce un cod destinat s ruleze pe acelai
tip de calculator i cu un acelasi sistem de operare. Acesta este denumit
ca fiind un compilator native-cod. Alternativ, poate produce un cod
destinat s ruleze pe o platforma diferit i poart denumirea de
compilator cross. Compilatoarele cross sunt foarte utile atunci cnd
platforma hardware este nou.
Compilatorul sursa la sursa este un tip de compilator care are un
limbaj de nivel nalt. De exemplu, un compilator paralel va lua un program
de limbaj nalt ca i intrare i apoi va transfroma codul i l va adnota cu
nsemnri de cod paralel (exemplu OpenMP).
Compilatorul cross poate fi considerat un program de cutare de
baze de date. Acesta doar nlocuiete irurile de caractere din surs cu
codul binar dat. Nivelul acestui cod binar poate varia; de fapt, unele
5

compilatoare FORTH pot compila programe care nici mcar nu au nevoie


de un sistem de operare.
CompilatorulIncremental - funciile individuale pot fi compilate
ntr-un mediu run-time, care include, de asemenea, funcii de interpretat.
Compilatorul Just-n-time - cererile sunt livrate n bytecode, ce
sunt compilate n codul main nativ chiar nainte de execuie.
CompilatorulRetargetable - un compilator care poate fi relativ
uor de modificat pentru a genera codul pentru diferite arhitecturi de CPU.
Codul obiect produs de acesta este adesea de calitate mai mic dect cel
produs de un compilator dezvoltat special pentru un procesor. Aceste tipuri
de compilatoare sunt de asemenea i compilatoare cross. GCC este un
exemplu.

1.2Design-ul compilatorului
n trecut, compilatoarele au fost mprite n mai multe moduri
(passes) pentru a economisi spaiu. Un pass n acest context, este o
rulare a compilatorului prin codul surs al programului care trebuie s fie
compilat, avnd ca rezultat obinerea complet a datelor interne ale
compilatorului. La final, compilatorul poate elibera spaiul de date intern
rezultat. Metoda de compilaremultipass a fost o metod de compilare
utilizat n acel moment i din cauza memoriilor mici ale calculatoarelor
gazd n raport cu codul surs i cu datele.
Multe compilatoare moderne au un design comunin dou etape.
Front end-ul translateaz limbajul surs ntr-o reprezentare intermediar. A
doua etap este back end-ul, care funcioneaz ca o reprezentare pe plan
intern pentru a produce un cod n limbajul de ieire. Front end-ul i back
end-ul pot funciona ca passes diferite, sau front end-ul poate apela
back end-ul ca o subrutin.
Aceast abordare accentueaza complexitatea, separnd preocuprile
front end-ului, care graviteaz de obicei n jurul semanticii limbajului,
verificrii erorilor, de preocuprile back end-ului care se concentreaz pe
ieire, ceea ce este att eficient ct i corect. De asemenea, are avantajul
de a permite utilizarea unui singur back end pentru mai multe limbaje
surs i permite n mod similar utilizarea de diferite back end-uri pentru
obiective diferite.
Anumite limbaje, datorit design-ului limbajului i a anumitor reguli,
introduc n declararea variabilelor i alte obiecte utilizate, precum i
declararea de proceduri executabile anterior referinei, ce sunt capabile de
6

a fi compilate ntr-un singur pass. Limbajul de programare Pascal este


bine cunoscut pentru aceast capacitate i, de fapt, multe compilatoare
Pascal sunt scrise n limbajul Pascal din cauza specificaiilor rigide ale
limbajului i capacitii de a folosi un singur pass pentru a compila
programe care au folosit limbajul Pascal.

1.2Procesul de compilare

Figure 1

La nivelul cel mai nalt, compilarea este mprit n mai multe pri:

Analiza lexicala (tokenizing)

Token: o secventa de caractere tratata ca o singura unitate.


Exemple:
Cuvinte rezervate (ex begin, end, struct, if etc.)
Cuvinte cheie (integer, true etc.)
7

Operatori (+, &&, ++ etc)


Identificatori (nume de variabile, nume de procedura, parametri)
Constante (numerice,sir de caractere)
Semne de punctuatie (:, , etc.)

Analiza sintactica (parsing)


Tip de verificare
Generare de cod

Orice compilator are unele cerinte esentiale, care sunt, probabil, mai
stricte decat la alte programe:

Orice program valid trebuie sa fie tradus in mod corect (nicio


traducere incorecta nu este permisa);
Orice program invalid trebuie sa fie respins si sa nu fie tradus;

Vor exista, inevitabil, programe care nu pot fi traduse ca urmare a


dimensiunii sau complexitii lor n raport cu hardware-ul disponibil, de
exemplu problema cauzata de dimeniunea memoriei. Compilatorul poate
avea, de asemenea, unele tabele cu dimensiune fix care plaseaz limite
cu privire la ceea ce poate fi compilat (unele definiii lingvistice plaseaz
limite mai mici n ceea ce privete limitele unor tabele, pentru a se asigura
c programele de dimensiuni rezonabile/complexitate poti fi compilate) .
Exista, de asemenea, unele cerinte care pot fi exclusive:

Erorile trebuie raportate in termenii programului sursa sau a


programului;
Pozitia la care eroarea a fost detectata ar trebui indicata; daca
eroarea actuala a avut loc probabil mai devreme, atunci ar
trebui sa fie, de asemenea, furnizate anumite semne care
indica aceasta cauza;
Compilarea ar trebui sa fie rapida;
Programul tradus ar trebui sa fie rapid ;
Programul tradus ar trebui sa fie de dimensiune mica;
In cazul in care limbajul sursa are anumite standard nationale
sau internationale:
- intregul standard ar trebui implementat;
- orice restrictii sau limite ar trebui sa fie bine si clar
documentate;
- in cazul in care au fost puse in aplicare extensii ale
standardelor:
- aceste extensii ar trebui sa fie documentate ca
atare;
- ar trebui sa fie o cale de a opri aceste extensii;
8

Exista, de asemenea, anumite cerinte controversate pe care ar


trebui sa le luam in considerare:

Erorile detectate atunci cand programul tradus ruleaza ar trebui sa


fie raportat la programul sursa original (de exemplu numarul de
linie) ;
Erorile detectate atunci cand programul tradus este executat, ar
trebui sa fie inclusa divizarea prin 0, epuizarea memoriei, utilizarea
unui indice/index care este prea mare sau prea mic, tentativele de
utilizare a unei variabile nedefinite etc.

1.4Structur general
Lista de mai jos prezinta in detaliu sarcinile ndeplinite de ctre front
end i back end. De reinut faptul c sarcinile nu sunt efectuate ntr-o
anumit ordine, aa cum sunt prezentate mai jos in Figure2 i vor fi
discutate mai n detaliu n capitolele urmtoare.

Figure 2

Front end
o Analiza lexicala - converteste caracterele intr-o secventa
de caractere criptice(token);
o Analiza sintactica cauta secventele valide ale
secventelor criptate (token);
9

o Analiza semantica verifica sensul secventei criptate


(token);
o Optimizari la nivel mondial/la nivel inalt;
Middle end
o Efectueaza optimizari, inclusiv eliminarea codului
nefolositor sau imposibil de gasit si propagarea de valori
constante, relocarea de calcul intr-un loc mai putin
frecvent executat (de ex. dintr-o bucla), sau
specializarea de calcul bazat pe context;
o Genereaza un al IR pentru back end;
Back end
o Optimizari locale;
o Inregistreaza alocarea;
o Optimizare peephole. Aceasta este o metoda foarte
des folosita si este utilizata pentru a curata codul. De
obicei aceasta fereastra este o secventa de instructiuni
care urmeaza una dupa alta (dar nu neaparat). La fel ca
si celelalte optimizari ale compilatoarelor, optimizarea
peephole trebuie utilizata in mod repetat pentru a
obtine un efect maxim;
o Generarea codului;
o Programarea instructiunii;

Figure 3

10

O compilare tipica poate consta din urmatoarele etape dupa cum arata
Figure3:

In primul rand, front end-ul executa si creeaza un model de


BIP-EMF;
Apoi filtrele din middle end sunt executate la randul lor.
Rezultatul este un model de BIP-CEM, eventual modificat;
In cele din urma, toate back end-urile sunt executate la randul
lor. Rezultatele lor sunt rezultatele de compilare.
The front end

Aceast parte este responsabil pentru citirea datelor introduse de


utilizator (de ex codul surs BIP i argumentul n linia de comand) i
transformarea acestora ntr-o reprezentare intermediar, care va fi
utilizat pentru alte pri ale compilatorului. Actualul front-end conine un
parser pentru limbajul BIP i un meta-model al BIP-ului care descrie
reprezentarea intermediar. Un exemplu de model BIP reprezentat de
meta-modelul BIP este numit BIP-EMF (deoarece aceste este un model BIP
exprimat folosind tehnologia Eclipse Modeling Framework (EMF)).
The middle end
Middle end-ul gzduiete toate transformrile BIP la BIP. Acest lucru
acioneaz pe modelul BIP-CEM prin intermediul unor operaii:

Modificari ale arhitecturii (de ex aplatizare);


Simplificari petri net ( reea Petri=graf orientat bipartit, ale
crui noduri sunt locuri sau tranziii);
Colectii de date;

In prezent, compilatorul nu are nicio astfel de operatie: middle endul este gol.
The back end
Back end-ul preia modelul BIP-EMF i i este permis doar s citeasc
cel mai probabil un cod surs ntr-un alt limbaj (de ex, C, C++, etc), sau
chiar n BIP. n prezent, principalul back end utilizat este back end-ul C++,
care produce cod C++ potrivit pentru motorul standar (standar engine).
Mai multe back end-uri pot fi utilizate simultan; de exemplu, ar putea fi
11

necesar pentru a obine o versiune BIP de intrare, dup ce au fost aplicate


cteva optimizri, mpreun cu versiunea C++ corespunztoare acesteia.
Design-ul compilatorului interzice back end-ului s interacioneze ( atunci
cnd exist mai multe back end-uri de executat, compilatorul nu
precizeaz n ce ordine vor fi executate sau dac execuiile vor fi n paralel
sau nu).
Aproape toate aspectele limbajului surs sunt gestionate de front
end. Este posibil s existe mai multe front end-uri pentru diferite limbaje
de nivel nalt i un back end comun care face cea mai mare parte din
optimizare. Aproape toate aspectele dependente de dispozitiv sunt
gestionate de ctre back end. Este posibil s aib diferite back end-uri
pentru diferite calculatoare, astfel nct compilatorul s poat produce
coduri pentru calculatoare diferite.
Front end-ul este n mod normal controlat de procesul de analiz
sintactic. Dup cum este necesar, codul de analiz sintactic va apela o
rutin care efectueaz analiza lexicala i returneaz urmtorul dispozitiv
criptat (token). n timpul analizei sintactice, rutinele semantice
corespunztoare sunt apelate i efectueaz controale semantice relevante
i/sau adaug informaii la reprezentarea pe plan intern.

2.Descrierea BNF (Backus-Naur Form) a


gramaticii unui limbaj
n informatic, BNF (Backus Normal Form sau Backus-Naur Form)
este una dintre cele dou tehnici de notare principale n gramatic, de
multe ori folosita pentru a descrie sintaxa limbajului utilizat n cacul, cum
ar fi limbaje de programare independente, formate de documente, seturi
de instruciuni i protocoale de comunicare ; cealalt tehnic de scriere a
gramaticii independente este tehnic van Wijngaarden. Ele sunt aplicate
ori de cte ori este nevoie de descrieri exacte ale limbajului : n
specificaiile limbajului, n cri de teorie a limbajului de programare etc.
BNF folosete un mic set de simboluri pentru a descrie o secvena
gramaticala. Autorii de orice limbaj de programare (de exemplu C++,
Visual Basic) trebuie s noteze secvenele gramaticale ale limbajului
respectiv. Acetia folosesc adesea notaia BNF pentru a descrie aceste
secvene. Autorul compilatorului citete apoi acest document i face n aa
fel nct programul compilatorului s se conformeze cu el.
Fiecare secventa in BNF are urmatoarea structura :
12

Name(simbol non-terminal) ::= expansion(expresie care


contine simboluri terminale si non-terminale unite prin secventiere si
alegere)
Un simbol terminal poate fi simbolul + , function, sau integer.
Unele dintre notatiile BNF includ :
o <> folosit pentru a cuprinde un element sintactic ; fiecare nume din
BNF este inconjurat de aceste paranteze, oriunde s-ar afla el ;
o ::= inseamna este definit de sau consta din ;
o | cand este plasat intre doua elemente inseamna o alegere SAU
intre ele ;
o {} acoladele inseamna valoarea zero sau mai multe repetari ale
continutului ;

Exemplu 1
Un programator este pe cale s dezvolte o nou baz de date. Cu
toate acestea, alte persoane vor scrie alte pri ale programului i vor
trebui s tie secvenele gramaticale care vor fi utilizate pentru a realiz o
adres potal. Programatorul principal poate scrie secvenele gramaticale
ntr-un document pe care alii trebuie s-l urmeze.
<adresa-postala> ::= <nume> : <numele-strazii> : <codulpostal>
Aceasta se poate traduce o adresa postala este compusa din nume,
numele strazii si codul postal (cu o semicoloana care le separa). Astfel,
orice programator care doreste sa foloseasca codul postal ca parte din
modulul lor, trebuie sa urmeze aceasta secventa. Daca nu, o eroare de
sintaxa va fi generata.
Simbolul <nume> are urmatoare secventa :
<nume> ::= <prenume> , {numele-mijlociu} , <numele de
familie>
Aceasta se traduce : numele este definit de prenume, urmat de o
virgula, numele mijlociu, urmat de o virgula si numele de familie.

Exemplu 2 definirea unei cifre


13

<cifra> ::= 0|1|2|3|4|5|6|7|8|9


Asa ca o cifra este orice numar cuprins intre 0 si 9 inclusiv

Exemplu 3 definirea unui numar intreg


<numar-intreg> ::= <cifra> | <cifra><numar-intreg>
Se poate oberva c simbolul <numr-ntreg> apare de dou ori n
secvena. Aceast se numete o declaraie recursiv. O declaraie
recursiv face uz de ea nsi, c parte a definiiei acesteia. n acest caz
secvena se traduce : un numr ntreg const dintr-o singur cifr sau o
singur cifr urmat de un alt numr ntreg. Aa c putem observ c
acest lucru permite unui numr ntreg s aib orice dimensiune.

2.1Descrierea EBNF (Extended BNF)


EBNF este folosit pentru a realiza o descriere formal a unui limbaj
formal care ar putea fi un limbaj de programare. EBNF este o extensie a
descrierii BNF. Descrierea EBNF a fost dezvoltat de Niklaus Wirth. EBNF
este un cod care exprim gramatica unui limbaj formal. Un EBNF este
format din simboluri terminale i reguli de producie non-terminale, care
sunt restriciile ce reglementeaz modul n care simbolurile terminale pot
fi combinate ntr-o secvena. Exemplele de simboluri terminale includ
caractere alfanumerice, semne de punctuaie i caractere white space.
EBNF defineste regulile de productie in cazul in care secventele de
simboluri sunt atribuite la un non-terminal.
cifra excluzand 0 = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
cifra

= "0" | digit excluzand zero ;

Bara verticala reprezinta o alternativa, iar simbolurile terminale sunt


inchise cu ghilimele, urmate de ; ca si caracter terminal. Prin urmare, o
cifra este un 0 sau o cifra excluzand cifra 0, care poate fi 1 sau 2 sau 3 si
asa mai departe pana la 9.
O secventa de productie poate include, de asemenea, o secventa de
terminale sau non-terminale, fiecare separate prin virgula :
unsprezece
trei sute unu

= "1", "1" ;
= "3", "0", "1" ;
14

patru sute usprezece

= "4", unsprezece ;

unsprezece mii trei sute unu = unsprezece, trei sute unu ;


O optiune poate fi reprezentata prin paranteze patrate []. Tot ceea
ce este stabilit intre parantezele patrate poate fi prezentat doar o data,
sau deloc.
numar-intreg = "0" | [ "-" ], numar natural ;
Prin urmare, un numar intreg este un zero (0) sau un numar natural
care poate fi precedat de un semn minus optional.

Tabel de simboluri
Folosinta
Definitie
Concatenare
Final
Alternanta
Optiune
Repetitie
Grupare
Sir de caractere
Sir de caractere
Comentariu
Secventa speciala
Exceptie

Notatie
=
,
;
|
[]
{}
()

(**)
?... ?
-

Orice gramatica definita in EBNF poate fi,de asemenea, definita si in


BNF, desi reprezentarile in BNF sunt,in general, mai lungi. BNF utilizeaza
simbolurile (<,>,|, ::=) pentru sine, dar nu include ghilimelele intre siruri
terminale. In EBNF terminalele sunt strict inchise in ghilimele ( sau
). Parantezele (<>) pentru non-terminale pot fi omise. Sintaxa BNF
poate avea doar o secventa intr-o singura linie, in timp ce in EBNF
caracterul ; marcheaza sfarsitul unei secvente.

15

GRIGORAS ANDRA

3.Analiza lexical
n informatic, analiza lexical reprezinta procesul de conversie al unei secvene de
caractere ntr-o secven de token-uri (iruri de caractere semnificative). Un program sau o
funcie care efectueaz analiza lexical este numit un analizor lexical, lexer, tokenizer sau
scanner, dei "scanner" este, de asemenea, folosit pentru prima etap a unui lexer. Un lexer
este, n general, combinat cu un parser, care s analizeze mpreun sintaxa de limbaje de
programare, cum ar fi n compilatoare pentru limbaje de programare, dar i parsere HTML n
browsere web, printre alte exemple.
Strict vorbind, un lexer este el nsui un fel de parser ,sintaxa limbii fiind mprita n
dou buci: sintaxa lexical (structura de cuvnt), care este procesat de lexer; i structura de
fraz, care este procesat de (la nivel de fraz) parser. Sintaxa lexical este, de obicei, un
limbaj obinuit, al crui atomi sunt caractere individuale, n timp ce sintaxa de fraz este, de
obicei, un limbaj liber de context, a crui atomi sunt cuvinte (token-uri produse de lexer). n
timp ce aceasta este o separare comun, n mod alternativ, un lexer poate fi combinat cu
analizorul n scannerless cu parsare.
Un lexer formeaz prima faz a interfeei compilatorului n prelucrarea modern, i se
face n general ntr-o singur trecere pass.
Un lexer n sine poate fi mprit n dou etape: scanerul, care segmenteaz secvena
de intrare n grupuri i le clasific n clase simbolice; i evaluatorul, care transform
caracterele de intrare n valoari procesate.

3.1 Token
Un token este un ir de una sau mai multe caractere care este semnificativ ca grup.
Procesul de formare a token-urilor de la un flux de intrare de caractere se numete tokenizare.
Token-urile sunt identificate pe baza regulilor specifice ale unui lexer. Unele metode
folosite pentru a identifica token-uri includ: expresii regulate, secvene specifice de caractere
cunoscute ca un flag, caractere specifice de separare numite delimitatoare, precum i definirea
explicit de un dicionar. Caracterele speciale, inclusiv semnele de punctuaie, sunt utilizate n
mod obinuit de ctre lexer pentru a identifica token-uri, din cauza utilizrii lor n mod natural
n scris i n programare.
Token-urile sunt adesea clasificate n funcie de coninutul de caracter sau de context
n fluxul de date. Categoriile sunt definite de normele de lexer. Acestea de multe ori implica
elemente gramaticale ale limbii utilizate n fluxul de date. Limbajele de programare clasific
de multe ori token-urile ca identificatori, operatori grupati dup simboluri sau dupa tipul de
date. Limbile scrise clasific de obicei token-urile ca substantive, verbe, adjective sau semne
de punctuaie. Categoriile sunt utilizate pentru post-procesare a token-urilor, fie prin analizor
sau fie prin alte funcii n cadrul programului.
Un analizor lexical, n general, nu face nimic cu combinatiile de simboluri, sarcina
este lsat pentru un parser. De exemplu, un analizor lexical tipic recunoate parantezele
token-urilor, dar nu face nimic pentru a se asigura c fiecare "(" corespunde cu ")".
Considerm aceast expresie n limbajul de programare C:
suma = 4 + 6;
Tokenizata i reprezentat de tabelul de mai jos:
16

Lexeme
sum
=
4
+
6
;

Tip token
identificator
operator de atribuire
ntreg literal
operator de adunare
ntreg literal
sfritul declaraiei

Token-urile sunt adesea definite prin expresii regulate, care sunt nelese de ctre un
generator de analizor lexical, cum ar fi lex. Analizorul lexical (fie generate automat de un
instrument ca lex, sau artizanale), citete ntr-un flux de caractere, identific lexemele din flux
i le clasific n token-uri. Aceasta se numete "tokenizing". n cazul n care lexer-ul gsete
un simbol invalid, se va raporta o eroare.
Dupa tokenizing urmeaz parsarea. De acolo, datele pot fi ncrcate n structurile de
date de uz general, interpretate sau compilate.

3.2 Gramatica lexical


Specificatiile limbajului de programare adesea include un set de reguli , printer care si
gramatica lexicala, ce definete sintaxa lexical. Sintaxa lexicala este, de obicei, un limbaj
obinuit , cu regulile gramaticale formate din expresii regulate ; ele definesc setul de secvene
posibile de caractere care sunt folosite pentru a forma simboluri individuale. Un lexer
recunoate iruri de caractere , i pentru fiecare tip de ir gsit in programul lexical ia o
aciune, cele mai multe pur i simplu produc un token .
Dou categorii importante lexicale comune sunt spaiul alb i comentariile . Acestea
sunt , de asemenea, definite n gramatica i prelucrate de ctre lexer , dar sunt n general
eliminate (nu produc niciun token) i considerate nesemnificative , cel mult separ dou
token-uri ( ca n cazul n care if x n loc de ifx ) . Acestea sunt cele mai importante. n primul
rnd , regulile limbajelor care delimiteaz blocurile cu indentare , spaiu iniial este
semnificativ , deoarece determin structura de bloc , i este n general tratat la nivelul de
lexer . n al doilea rnd , n unele utilizri non- compilator de lexer , comentariile trebuie s
fie pstrate. n anii 1960 , n special pentru ALGOL , spaiul i comentariile au fost eliminate ,
ca parte din faza de reconstrucie a liniei ( faza iniial a interfaei unui compilator ) , dar
aceast faz separat a fost eliminat, iar acestea sunt n prezent gestionate de lexer .
Tokenizarea este procesul de a delimita i, eventual, clasifica seciuni dintr-un ir de
caractere de intrare. Token-urile rezultate sunt apoi trecute la o alt form de prelucrare.
Procesul poate fi considerat o sub-sarcina de parsare la intrare.
De exemplu :
The quick brown fox jumps over the lazy dog.
irul nu este segmentat implicit de spatii, cum ar face un vorbitor de limba englez.
Intrarea bruta, de 43 de caractere, trebuie s fie mprita n mod explicit n 9 token-uri cu un
anumit delimitator de spaiu .
Token-urile ar putea fi reprezentate n XML:
<sentence>
<word>The</word>
17

<word>quick</word>
<word>brown</word>
<word>fox</word>
<word>jumps</word>
<word>over</word>
<word>the</word>
<word>lazy</word>
<word>dog</word>
</sentence>
Sau in :
sentence
(word The)
(word quick)
(word brown)
(word fox)
(word jumps)
(word over)
(word the)
(word lazy)
(word dog))

3.3 Scanner-ul
n prima etap, scannerul, se bazeaz de obicei pe o main (FSM). Ea codeaza in
acesta, informaii cu privire la posibilele secvene de caractere care pot fi coninute n oricare
dintre semen (cazuri individuale ale acestor secvene de caractere sunt cunoscute ca lexeme).
De exemplu, un simbol ntreg poate conine orice secven de caractere numerice cifre. n
multe cazuri, primul caracter non-spaiu pot fi utilizate pentru a deduce tipul de simbol pe
care urmeaz i caracterele de intrare ulterioare sunt apoi procesate una la un moment dat
pn cnd ajunge la un caracter care nu este acceptat de setul de caractere pentru acel token.
n unele limbi, regulile de create de lexem sunt mult mai complicate i pot implica
backtracking peste caractere citite anterior. De exemplu, n C, un singur caracter "L" nu este
suficient pentru a distinge ntre un identificator care incepe cu "L" i un ir de caractere la
nivel literal.

3.4 Evaluator-ul
Un lexem este doar un ir de caractere cunoscut, de un anumit tip ( de exemplu , un
ir literal , o secven de litere ) . Cu scopul de a construi un token , analizorul lexical are
nevoie de o a doua etap , evaluatorul , care trece peste caracterele lexem pentru a produce
o valoare . Tipul de lexem combinat cu valoarea sa este ceea ce constituie n mod
corespunztor un semn , care poate fi dat la parser . Unele token-uri , cum ar fi parantezele nu
au ntradevr valori , i astfel funcia de evaluator pentru acestea poate ntoarce nimic : este
nevoie doar de tip . n mod similar , uneori, evaluatorii pot suprima un lexem n ntregime ,
ascunzndu- se de parser , care este util pentru spaiu i comentarii . Evaluatorii de
identificatori sunt de obicei simpli. Evaluatorii pentru literele intregi pot face parte dintr-un sir
sau singure.
De exemplu, n codul surs al unui program de calculator, irul
18

net_worth_future = (assets - liabilities);


poate fi convertit n urmtorul fluxul de token-uri lexicale (reamintim ca spaiu este suprimat
i caractere speciale nu au nici o valoare) :
NAME net_worth_future
EQUALS
OPEN_PARENTHESIS
NAME assets
MINUS
NAME liabilities
CLOSE_PARENTHESIS
SEMICOLON
Dei este posibil i, uneori, necesar, din cauza restriciilor de acordare a licenelor
pentru interpretoarele existente sau n cazul n care lista de token-uri este mica, de a scrie un
lexer de mn, lexer-urile fiind de multe ori generate de instrumente automate. Aceste
instrumente accept, n general, expresii regulate care descriu token-uri permise n fluxul de
intrare. Fiecare expresie regulat este asociata cu o regul de producie n gramatica lexicala a
limbajului de programare care evalueaz daca lexemele se potrivesc cu expresia regulat.
Aceste instrumente pot genera cod surs care poate fi compilat i executat.
Expresiile regulate reprezint modele ale caracterelor din lexeme. De exemplu,
pentru limba engleza de baza, numele token-ului poate fi orice caracter alfabetic din limba
englez sau o subliniere, urmat de orice numr de instane de orice caracter ASCII
alfanumeric sau un caracter de subliniere. Acest lucru ar putea fi reprezentat de irul compact
[a-zA-Z_] [a-zA-Z_0-9] *. Acest lucru nseamn "orice caracter de la a la z sau de la A la Z
sau de la 0 la9.
Expresiile regulate i mainile FSM care le genereaz nu sunt suficient de puternice
pentru a gestiona modele recursive, cum ar fi "n paranteze de deschidere, urmate de o
declaraie, urmate de n paranteze de nchidere." Ele nu sunt capabile de a menine scorul i
verificarea c n este acelasi pe ambele pri - dac nu avei un set finit de valori admise pentru
n. Este nevoie de un parser cu drepturi depline pentru a recunoate astfel de modele n
generalitatea lor. Un parser poate mpinge parantezele pe o stiv i apoi s ncerce s le scoata
i a vedea dac stiva este goal la sfrit.
Instrumentul de programare Lex i compilatorul sunt concepute pentru a genera cod
pentru analizorul lexical rapid, bazat pe o descriere formal a sintaxei lexicale. Nu este, n
general, considerat suficient pentru aplicaii cu un set complex de reguli lexicale i cerine
severe de performan; de exemplu, compilatorul GNU Collection (GCC) utilizeaz litere
scrise de mn.

3.5 Generatorul de lexer


Lexer-urile sunt de multe ori generate de un generator de lexer, analog generatoarelor
de parser, i astfel de instrumente de multe ori vin mpreun. Cel mai stabilit este lex, asociat
cu generatorul yacc parser.
Aceste instrumente au randament de dezvoltare foarte rapid, ceea ce este deosebit de
important n dezvoltarea timpurie, pentru a obine un lexer de lucru. n plus, acestea ofer de
multe ori caracteristici avansate, cum ar fi pre i post condiii care sunt greu de programat
manual. Cu toate acestea, lexer-erele generate automat pot fi lipsite de flexibilitate, i, astfel,
ar putea necesita unele modificri manuale sau un lexer scris complet manual.
Lista de generatoare Lexer:
- ANTLR - Poate genera analizoare lexicale i interpretoare.
- DFASTAR - Genereaz DFA n C + +.
19

- Flex - variant alternativ a clasic "lex" (C / C + +).


- JFlex - O rescriere a JLex.
- Ragel - O main i lexer generator n C, C + +, C #, Objective-C, D, Java, Go i Ruby.
Generaoare unicod:
o JavaCC - JavaCC genereaza analizoare lexicale scrise n Java.
o JLex - Un generator de analizor lexical pentru Java.
o Quex - Un generator rapid universal analizor lexical pentru C i C + +.

4. Analiza sintactica
Parsarea sau analiz sintactic este procesul de a analiza o serie de simboluri, fie n
limbaj natural sau n limbaje de programare, n conformitate cu regulile unei gramatici
formale.

4.1 Parser
Un parser este o component software care preia datele de intrare (frecvent text) i
construiete o structur de date - de multe ori un fel de arbore parser parser tree, copac
sintatic abstract sau alt structur ierarhic - oferind o reprezentare structural de intrare, de
verificare pentru sintaxa corect n proces. Parsarea poate fi precedata sau urmata de alte
msuri, acestea pot fi combinate ntr-un singur pas. Parser este adesea precedat de un analizor
lexical separat, care creeaz token-uri din secvena de caractere de intrare; n mod alternativ,
acestea pot fi combinate n parsare scannerless . Interpretoarele poate fi programate manual
sau pot fi generate automat sau semi-automat, de ctre un generator de parser. Parsarea este
complementar templating-ului, care produce formatul de ieire. Acestea pot fi aplicate n
diferite domenii, dar de multe ori apar mpreun, cum ar fi perechea scanf / printf, sau stagiile
compilatorului de intrare (parsing front-end) i de ieire (generarea codului de final).
Intrarea la un parser este de multe ori de tip text ntr-un limbaj de calculator, dar poate
fi, de asemenea, de text ntr-o limb natural sau de date textuale mai puin structurate, n
cazul n care, n general, numai anumite pri ale textului sunt extrase. Interpretoarele variaz
de la funcii foarte simple, cum ar fi scanf, la programe complexe, cum ar fi interfaa a unui
compilator C + + sau analizorul HTML a unui browser web. O categorie importanta de
parsarea simpla, se face folosind expresii regulate, n cazul n care o expresie regulat
definete un limbaj regulat, iar apoi motorul de expresie regulata genereaza automat un parser
pentru aceea limb, care s permit potrivirea exact i extragerea de text. n alte contexte
expresiile regulate sunt ,n schimb, utilizate nainte de parsare, ca pas a lexicului a crui ieire
este apoi utilizata de ctre parser.
Utilizarea de interpretoare variaz n funcie de intrare. n cazul limbajelor de date, un
parser este adesea vzut ca facilitand citirea unui program fiier , cum ar fi citirea n HTML
sau text XML; aceste exemple sunt limbaje de markup. n cazul limbajelor de programare, un
parser este o component a unui compilator, care analizeaz codul surs de la un limbaj de
programare pentru a crea o form de reprezentare interna; parser-ul este un pas cheie n baza
unui compilator. Limbajele de programare tind s fie specificate n termeni de un context
liber gramatical determinist deoarece interpretoarele rapide i eficiente pot fi scrise pentru ei.
Pentru compilatoare, parsarea se poate face ntr-o singur trecere sau mai multe treceri.
Gramaticile independente de context sunt limitate n msura n care acestea pot
exprima toate cerinele unui limbaj. Informal, motivul este c memoria unui astfel de limbaj
este limitata. De exemplu, n Python urmtorul cod este este unul valid sintactic:
x=1
20

print(x)
Codul de mai jos este din punct de vedere sintactic valabil n ceea ce privete
gramatica de context, obinndu-se un arbore sintactic cu aceeai structur ca i cel anterior,
dar este din punct de vedere syntactic, invalid n ceea ce privete gramatica sensibil la
context, care impune ca variabilele s fie iniializate naintea folosirii:
x=1
print(y)
Mai degrab dect s fie analizat n faza de parsare, acest lucru este prins prin
verificarea valorilor din arboreal sintaxa, prin urmare, ca parte din analiza semantic: sintaxa
sensibila la context este, n practic, de multe ori mai uor de analizat ca semantica.
Urmtorul exemplu demonstreaz cazul comun a parsarii unui limbaj cu dou niveluri
de gramatic: lexicale i sintactice. Prima etap este generarea token-ului, sau analiza lexicala,
prin care fluxul de caractere de intrare este mprit n simboluri semnificative definite de o
gramatica de expresii regulate. De exemplu, un program pentru calculator ar cauta la intrare,
cum ar fi "12 * (3 +4) ^ 2" i mprirea acesteia n token-urilor 12, *, (, 3, +, 4,), ^, 2, fiecare
dintre acestea este un simbol semnificativ n contextul unei expresii aritmetice. Lexer-ul ar
conine norme care s se spun c caracterele *, +, ^, ( i ) marcheaz nceputul unui nou
token, token-urile astfel lipsite de sens, cum ar fi "12", "*" sau "(3" nu vor fi generate.
Urmtoarea etap este parsarea sau analiz sintactic, care verific faptul c tokenurile formeaz o expresie admisibila. Acest lucru se face de obicei cu referire la o gramatica
de context care definete recursiv componente care pot alctui o expresie i ordinea n care
acestea trebuie s apar. Cu toate acestea, nu toate normele care definesc limbajele de
programare poate fi exprimate prin gramatici independente de context , de exemplu tip
valabilitate i declaraie corespunztoare de identificare. Aceste reguli pot fi exprimate n
mod oficial cu gramatici de atribute.
Faza final este pars-ingul semantic sau de analiz, care lucreaza la implicaiile
expresiei doar validate i luarea msurilor corespunztoare. n cazul unui calculator sau
interpret, aciunea este de a evalua exprimarea sau programul, un compilator, pe de alt parte,
ar genera un fel de cod. Gramaticile atribut poate fi de asemenea utilizaet pentru a defini
aceste aciuni.

21

Figure 4

4.2 Tipuri de parser


Sarcina parser-ului este, n esen, de a determina dac i cum intrarile pot fi derivate
din simbolul de start al gramaticii. Aceasta se poate face n esen, dou moduri:
Parsarea de sus n jos (top-down) - analiz poate fi privit ca o ncercare de a gsi
in stnga cele mai multe derivatii de la o intrare-flux de cutare pentru
arborii. Token-uri sunt consumate de la stnga la dreapta.
Parsarea de jos in sus (bottom-up) - un parser poate ncepe cu intrarea i s
ncerce s-o rescrie la simbolul de pornire. Intuitiv, parser-ul ncearc s gseasc
elementele cele mai de baz, apoi elementele care conin acestea, i aa mai
departe.
Exemple de parsere:
Parser de sus n jos
Unele dintre analizatorilor care folosesc parsing de sus-jos includ:
o Parser recursive-descendent
o LL parser (Left-to-right, Leftmost derivation)
o Earley parser
Parser de jos n sus
o Parser cu prioritate
o BC (bounded context) parser
22

o LR parser (Left-to-right, Rightmost derivation)


Parsere pentru dezvoltare de software
ANTLR
Bison
Coco/R
GOLD
JavaCC
JParsec
Lemon
Lex
Parboiled
Parsec
ParseIT
Ragel
SHProto (FSM parser language)[7]
Spirit Parser Framework
Syntax Definition Formalism
SYNTAX
XPL
Yacc

5.Arborele sintactic
n informatic, un arbore sintactic abstract (AST), sau pur i simplu arborele sintactic,
este o reprezentare a structurii sintactice abstracte, de cod surs, scris ntr-un limbaj de
programare. Fiecare nod al arborelui denot un constructor care apare n codul surs. Sintaxa
este "abstracta", nu reprezint fiecare detaliu care apare n sintaxa real. De exemplu,
gruparea parantezelor sunt implicite n structura arborescent, i o construcie sintactic ca o
expresie dac-condiie atunci pot fi notate cu ajutorul unui singur nod cu dou ramuri.
Arborii sintactici sunt adesea construiti de ctre un parser in timpul traducerii codului
surs i a procesului de compilare. Odat construit, informaii suplimentare se adaug la
acesta prin prelucrare ulterioar.

5.1 Aplicarea n compilatoare


Arborii sintactici abstracti sunt structuri de date utilizate pe scar larg n
compilatoare, datorit proprietii lor de a reprezenta structura de cod de program. Un AST
este de obicei rezultatul fazei de analiz sintactica a unui compilator. De multe ori servete si
ca o reprezentare intermediar a programului prin mai multe etape necesare unui compilator,
i are un impact puternic asupra produciei finale a compilatorului.
Fiind produsul etapei de analiz sintactica a unui compilator, AST are cteva
proprieti care sunt de nepreuit pentru urmtoarele etape ale procesului de compilare. n
comparaie cu codul surs, un AST nu include anumite elemente, cum ar fi semnele de
punctuaie neeseniale i delimitatori ( punct i virgul, paranteze, etc). O diferen mai
important este ca AST pot fi editati i mbuntiti cu proprieti i adnotri pentru fiecare
23

element care le conine.Astfel de editari i adnotari sunt imposibile cu codul surs al unui
program, deoarece ar implica modificarea acestuia. n acelai timp, un AST conine de obicei
informaii suplimentare despre program, datorit etapelor succesive ale analizei de
compilator. Un simplu exemplu de informaii suplimentare prezent ntr-o AST este poziia
unui element n codul surs. Aceste informaii sunt utilizate n caz de eroare n cod, pentru a
notifica utilizatorul de locaia erorii.
Nevoia de AST vine de la natura inerenta a limbajelor de programare i documentaia
lor. Limbajele sunt adesea ambigue. Pentru a se evita aceast ambiguitate, limbajele de
programare sunt adesea specificate cu o gramatica independenta de context (CFG). Cu toate
acestea, exist de multe ori aspecte ale limbajelor de programare pe care o CFG nu poate
exprima, dar sunt parte a limbii i sunt documentate n caietul de sarcini. Acestea sunt detalii
care necesit un context pentru a determina validitatea i comportamentul lor. De exemplu,
dac un limbaj permite noi tipuri s fie declarate, un CFG nu poate prezice numele acestor
tipuri, nici modul n care acestea ar trebui s fie utilizate. Chiar dac o limb are un set
predefinit de tipuri, folosirea corect presupune, de obicei, unele contexte. Java ofer un
exemplu excelent, n cazul n care operatorul "+" este att plus numeric i concatenarea
sirurilor de caractere.
Dei exist alte structuri de date implicate n mecanismele interioare ale unui
compilator, AST ndeplinete o funcie unic. n prima etap, etapa de analiz de sintax, un
compilator produce un arbore parse. Acest arbore parse poate fi utilizat pentru a efectua
aproape toate funciile unui compilator prin translaie ndreptat spre sintax. Dei aceast
metod poate duce la un compilator mai eficient, este mpotriva principiilor de inginerie
software de scriere i meninerea programelor. Un alt avantaj pe care AST il are fata de un
arbore parse este dimensiunea, n special nlimea mai mic i numrul mai mic de
elemente.

5.2 Proiectarea unui arbore sintactic


Cnd proiectam un AST trebuie s fim contieni de funcionalitatea pe care
compilatorul o va atepta. Aa cum am menionat mai nainte, nu putem stoca declaraiile de
program n forma surs. n acelai timp, declaraiile trebuie s pstreze tipurile i locaia
lor.Ordinea de declaraii executabile trebuie s fie reprezentate n mod explicit i bine
definit. Asignare are nevoie pentru a stoca identificatorul care va pstra valoarea
atribuit. Aceste cerine pot fi folosite pentru a proiecta structura de date de folosit. Este
cunoscut faptul c unele operaiii vor fi ntotdeauna constituite din dou elemente, cum ar fi
adunarea a 2 numere. Ca rezultat, un AST trebuie s fie, de asemenea, suficient de flexibil i
rapid pentru a permite adaos rapid de cantiti arbitrare ale copiilor. O alt cerin majore de
proiectare pentru un AST este c ar trebui s fie posibila s se unparseze un AST n surs
form de cod, care este suficient de asemntor cu originalul i a crei executare este
suficient de similara cu executarea programului reprezentat de AST.
Datorit complexitii cerinelor pentru un AST i complexitatea de ansamblu a unui
compilator, este benefic s se aplice principiile de dezvoltare de software de sunet. Una
dintre acestea este de a utiliza modele de design dovedite a mbunti modularitatea i
uurina de dezvoltare. Datorit faptului c diferitele operaii nu au neaprat diferite tipuri,
este important de a avea o ierarhie de clase nod sunet. Acest lucru este crucial n crearea i
modificarea AST ca compilatorul sa progreseze. Deoarece compilatorul face cteva traversri
ale arborelui pentru a determina corectitudinea sintactica, este important s se fac
traversarea arborelui intr-o operaie simpl. De cnd ajunge la fiecare nod, compilatorul
execut un set specific de operaiuni, n funcie de tipul de nod, are sens s utilizeze modelul
vizitatorilor.
24

AST este folosit intensiv n timpul de analiz semantic, n cazul n care se fac
controalele de compilare pentru utilizarea corect a elementelor de program i limba. De
asemenea, n timpul analizei semantice compilatorul genereaz tabelele de simboluri pe baza
AST. O traversare complet a arborelui permite s se verifice corectitudinea
programului. Dup verificarea corectitudinii, AST servete ca baz pentru etapa de generare
de cod. Acesta este adesea cazul n care AST este utilizat pentru a genera "reprezentarea
intermediara" "(IR)" pentru generarea de cod numit uneori un limbaj intermediar.
Exemplu: un arbore sintactic abstract pentru codul de mai jos pentru algoritmul lui Euclid:
while b 0
if a > b
a := a b
else
b := b a
return a

Figure 5

25

Parse tree vs. syntax tree


Parse tree: Reprezint sintaxa concret a unui program
Syntax tree: Reprezint sintaxa abstract a unui program (semantica)
Exemplu:
Program : a + b * c
Gramatica :
EE*E
|E+E
| id

Figure 6

5.3 LL parser
LL parser este un parser de sus n jos pentru un subset al gramaticilor independente de
context. El analizeaz informaiile primite de la stnga la dreapta i construiete o derivare
stnga a frazei.
Un parser LL este numit un parser LL(k), n cazul n care se folosete k token-uri
pentru parsarea unei propoziii. n cazul n care exist o astfel de parsare pentru o anumit
gramatic i se poate analiza fraza fr backtracking atunci aceasta se numete o gramatica
LL(k).
Gramaticile LL, n special LL(1), sunt de mare interes practic, pentru ca interpretoare
pentru aceste gramatici sunt uor de construit i mai multe limbaje de calculator sunt
concepute pentru a fi LL(1). Interpretoare LL sunt interpretoare pe baz de tabele, similare
cu interpretoare LR. Gramaticile LL pot fi caracterizate ca tocmai cele care pot fi analizate
26

de ctre un parser predictive - un parser cu coborre recursiv fr backtracking - i acestea


pot fi cu uurin scrise de mn.
Construirea unei tabele LL(1)
Cu scopul de a umple tabela de parsare, trebuie sa se stabileasca ce regula gramaticala ar
trebui s aleag parserul dac vede un neterminal A pe partea de sus a stivei i un simbol pe
un flux de intrare. Este uor s deducem c o astfel de regul ar trebui s fie de forma A w
i c limbajul corespunztor lui w ar trebui s aib cel puin un ir ncepnd cu a. n acest
scop vom defini primul set (First Set) de w, scris ca Fi (w), ca un set de terminale care pot fi
gsite la nceputul unor iruri n w, in plus dac irul gol aparine, de asemenea, la w. Avnd
n vedere o gramatica cu regulile A1 w1, ..., An wn, putem calcula Fi (wi) i Fi (Ai)
pentru fiecare regul.
Din pcate, primul set nu este suficient pentru a calcula tabelul de parsare. Acest lucru
se datoreaz faptului c o partea dreapta w al unei reguli ar putea fi n cele din urm rescrisa
cu un ir gol. Astfel parser-ul ar trebui s utilizeze, de asemenea, regula A w dac este n
Fi (w) i sa vada daca pe intrarea este symbol dupa care ar putea urma A. Prin urmare, de
asemenea, avem nevoie de un Follow-set pentru A, scris ca Fo (A), care este definit ca un
ansamblu de terminale a ca si cand ar exist un ir de simboluri Aa care pot fi derivate de
la simbolul de start.
Acum putem defini exact ce reguli vor fi incluse pentru tabelul de parsare. Dac T [A,
a] denot intrarea n tabelul de neterminal A i terminalul a, atunci T [A, a] conine regula A
w dac i numai dac a este n Fi (w) sau este n Fi (w) i a este n Fo (A).
n cazul n care tabelul conine cel mult o regul n fiecare dintre celulele sale, atunci
parserul va ti ntotdeauna care regula trebuie s foloseasc i, prin urmare, se poate analiza
iruri fr backtracking. Gramatica din acest caz este numit gramatic LL(1).

Exemplu
S -> XY | YX
X -> a b
Y -> b a
FIRST sets:
FIRST(X) FIRST(S)
FIRST(Y) FIRST(S)
a FIRST(X)
b FIRST(Y)
Dupa ce rezolvam constrangerile obtinem :
FIRST(X) = {a}
FIRST(Y) = {b}
FIRST(S) = {a,b}
Construirea tabelei :
Pentru fiecare terminal t First() facem T[A, t] = A ->
T
a
b
S

XY

ab

YX

ba

27

5.4 LR parser
LR parser sunt un tip de interpretoare de jos n sus (bottom-up), care se ocup eficient
de limbajele independente de context n timp liniar. Interpretoarele LR adesea sunt generate
mecanic de o gramatic formal pentru limbaj de un generator de parser. Ele sunt foarte larg
folosite pentru prelucrarea limbajelor calculatoarelor, mai mult dect alte tipuri de parsere.
Numele LR este un acronim. L nseamn c parser-ul citete textul de la intrare ntr-o
singura direcie fr copierea de rezerv; citirea se face, de obicei, de la stnga la dreapta pe
fiecare linie, i de sus n jos liniile fiierul de intrare. R nseamn c parser-ul produce o
derivare inversat din dreapta. Numele LR este adesea urmat de un calificativ numeric, ca
LR(1) sau, uneori, LR(k). De obicei k (numarul de simboluri) este 1 i nu este menionat.
Numele LR este adesea precedat de alte calificri, SLR i LALR.
LR parsere sunt deterministe; ele produc o interpretare corect fr presupuneri sau
ntoarceri, n timp liniar. Acest lucru este ideal pentru limbajele calculatoarelor. Dar
interpretoare LR nu sunt potrivite pentru limbajele umane pentru care este nevoie de metode
mai flexibile.
Numele LR vine de la forma de parsare inventat de Donald Knuth. LR parser poate
manipula o gam mai mare de limbaje i gramatici dect interpretoarele cu prioritate sau cele
sus-jos (LL). Aceasta deoarece LR parser ateapt pn cnd a vzut intreaga instanta a unor
modele gramaticale. Un parser LL trebuie s decid sau s ghiciceasc ceea ce se vede mult
mai devreme, adic dup ce l-a vzut doar cel mai din stnga simbol intrare . LR este, de
asemenea, mai bun la raportarea de erori. Detecteaz erorile de sintax a fluxului de intrare
ct mai repede posibil.
Exemplu: A*2 + 1
Pasul
Stiva
0
goala
1
id

Unparsed
A*2+1
*2+1

Shift/Reduce
shift
Value id

Value

*2+1

Products Value

3
4
5

Products
Products*
Products*int

*2+1
2+1
+1

shift
shift
Value int

Products*Value

+1

Products Products*Value

Products

+1

SumsProducts

8
9
10

Sums
Sums+
Sums+ int

+1
1
eof

shift
shift
Valueint

11

Sums+ Value

eof

ProductsValue

12

Sum+Products

eof

SumsSums+Products

13

Sums

eof

done

La pasul 6 se aplic o regul a gramaticii cu mai multe pri : ProductsProducts*Value

28

Figure 7

Majoritatea parser-elor LR au o tabel. Codul de program a parser-ul este o bucl


simpl, care este aceeai pentru toate gramaticile i limbajele. Cunotinele de gramatic i
implicaiile sale sintactice sunt codificate n tabele de date numit tabele parse. Aceste tabelele
arat cum pentru a calcula urmtoarea stare este nevoie doar de starea curent i de simbolul
urmtor.
Tabele parse LR sunt bidimensional. Fiecare stare a parser-ului LR(0) are propriu rnd.
Fiecare viitor simbol posibil are propria coloan. Celule necompletate declaneaz mesajele
de eroare de sintax.
Tabela pentru exemplu de mai sus:

29

Cele din stanga au fost parsate, iar cele din dreapta sunt n asteptare.

ALTIPARMAC ANDREEA-MIHAELA
30

6.Generalitati
Lex si Yacc au fost dezvoltate prima oara in laboratoarele Bell in anul
1970.Primul creat a fost Yacc ,de catre Stephen C. Johnson, apoi urmand Lex-ul,
care a fost dezvoltat de catre Mike Lesk si Eric Schmidt.Ambele au fost
standardizate ca utilitati ale UNIX-ului.
In zilele noastre, se poate afirma faptul ca exista cel putin doua versiuni de
Lex si Yacc dezvoltate pentru calculatoarele cu MS DOS si OS/2. MKS (Mortice
Kern Systems Inc.), editorii kit-ului MKS, au facut ca produsele Lex si Yacc sa fie
valabile si pentru calculatoarele care contin compilatoare C.
Sursa(source) Lex-ului este formata dintr-un tabel de expresii regulate i
fragmente de programe corespunztoare. Tabelul estetranslatatintr-un program
ce citete un flux de intrare, copiindu-l la un flux de ieire. Ca oricetip de ir astfel
generat,este recunoscut fragmentul de program corespunztor pentru a fi
executat. Recunoaterea de expresii este efectuat de ctre un automat finit
determinist generat de catre Lex. Fragmentele de program scrise de ctre
utilizator sunt executate n ordinea n care apar expresiile regulate
corespunztoare n fluxul de intrare.
Yacc este folosit ca un instrument general pentru descrierea intrarii unui
program. Utilizatorul Yacc specific structurilor sale de intrare , mpreun cu
codul,cum sa fie invocate fiecare astfel de structur pentru a fi recunoscute la
randul lor. Yacc traduce o astfel de secventa ntr-o subrutin care se ocup de
procesul de intrare; deseori, este convenabil i adecvat de a avea cea mai mare
parte a fluxului de control din aplicaia utilizatorului manipulatade aceast
subrutin.

7.Introducere Lex
Lex este un generator de program conceput pentru prelucrarea lexicala a
fluxurilor de intrare de caractere. Acesta are un nivel nalt, continand probleme
orientate catre potrivirea sirurilor de caractere, i mai contine si un program ntrun limbaj de uz general, care recunoate expresii regulate. Expresiile regulate
sunt specificate de utilizator n secventelesursei(source) de date Lex. Codul Lex
recunoate aceste expresii ntr-un flux de intrare i separ fluxul de intrare n
siruri de caractere care se vor potrivii expresiilor date. La graniele dintre sirurile
de caractere, sunt executate seciuni de programe furnizate de ctre utilizator.
Fiierul surs Lex asociaz expresiile regulate i fragmentele de
program.Fragmentul corespunzator va fi executat ca fiecare expresie ce apare n
intrarea programului scris de ctre Lex.
31

Lex nu este un limbaj complet , ci mai degraba un generator ceaduce o


caracteristic nou a limbajului. Aceasta caracteristica noua poate fi adugata n
diferite limbaje de programare , numite `` limbaje gazd . '' La fel ca limbajele de
uz general ce pot produce codul necesarrularii pe hard-uridiferite , Lex poate
scrie codul n diferite limbaje gazd . Limbajul gazd este utilizat pentrucodul de
iesire generat de Lex , precum i pentru fragmentele adugate de ctre utilizator.
De asemenea, sunt furnizate si bibliotecile compatibile pentru
diferitelelimbajegazd . Acest lucru face ca Lex sa fie adaptabil la medii diferite
i la utilizatori diferiti . Fiecare aplicaie poate fi direcionata ctre o combinaie
de hardware i limbaj gazd adecvate pentru fundalul utilizatorului .
Lex transforma expresiile i aciunile utilizatorului (sursa) n limbaj de uz
general gazd; programul generat este numit yylex. Programul yylex va
recunoate expresiile ca un stream i va efectua aciunile specificate pentru
fiecare expresie, precum se afiseaza si in imaginea de mai jos Figure8 :

Figure 8

Exemplu de program simplu in Lex:

Acest program copiaza intrarea lui standard in iesirea lui standard.Putem


spune ca se comporta mai degraba ca si comanda cat din UNIX fara niciun
argument. Lex va genera automat codul in C necesar citirii fisierelor de intrare si
in unele situatii, ca si in cazul acesta, scrierii fisierelor de iesire.
Lex poate fi utilizat pentru transformri simple sau pentru analiza i
colectarea statisticilor la nivel lexical.Acesta poate fi folosit i caun generator de
parser pentru a efectua etapa de analiz lexicala. Yacc scrie interpretoare care
accept o clas mare de gramatici independente de context, dar are nevoie de
un analizor de nivel inferior pentru a recunoate tokenurile de intrare.
32

Astfel, o combinaie de Lex si Yacc este adesea necesara. Atunci cnd este
utilizat ca un preprocesor pentru un generator de parser, Lex este folosit pentru a
partajastreamul de intrare, iar generatorul parser are datoria de a atribui
structura pieselor rezultate. Fluxul de comand ntr-un astfel de cazeste
prezentat n figura de mai jos, Figure9. Programe suplimentare, scrise de alte
generatoare, pot fi adugate cu uurin la programele Lex.

Figure 9

Lex genereaz un automat finit din expresiile regulate din sursa .


Automatul este utilizat cu scopul de a economisi spaiu.Rezultatul consta intr-un
analizor rapid. n special, timpul necesar caun program Lex s recunoasc i s
partajeze un flux de intrare este proporional cu lungimea sa de intrare. Numrul
de secvente Lex sau complexitatea acestora nu este importanta n determinarea
vitezei. Dimensiunea automatului finit nu creste cu numrul i complexitatea
secventelor.

7.1Sursa Lex
Forma generala a unei surse Lex este urmatoarea:

In general, definitiile si subrutinele sunt omise. Al doilea %% este optional,


dar primul este obligatoriu si este folosit pentru a marca inceperea secventelor
(rules).
n programul Lex de mai sus, secventele (rules) reprezint decizii de
control ale utilizatorului; acestea sunt cuprinsa intr-un tabel, n care coloana din
33

stnga conine expresiile regulate si coloana din dreapta conine aciuni,


fragmente de programe care urmeaz s fie executate atunci cnd expresiile vor
fi recunoscute. Totusi, poate sa apara o regula individuala ca sa caute irul string
n fluxul de intrare isa tipreasca mesajul `` found keyword INT'' ori de cte ori
apare. n acest exemplu, limbajul procedural gazd este C i din biblioteca C,
functia printf care este folosit pentru a imprima un ir. Sfritul expresiei este
indicat de primul caracter gol sau tab. Dac aciunea este formata doar dintr-o
singur expresie C, aceasta poate fi utilizata doar n partea dreapt a liniei; n
cazul n care este compusa, sau dureaz mai mult de o linie, aceasta ar trebui s
fie nchisa ntre paranteze.

7.2Expresii regulate
O expresie regulata reprezinta un model descriptiv ce foloseste un limbaj
meta, prin care in general, se descriu anumite modele alese. Caracterele
folosite in acest meta limbaj fac parte din codul ASCII folosit si in UNIX . Aceste
caractere sunt urmatoarele:
. marcheaza fiecare character singur cu exceptia caracterului de rand nou \n;
* - reprezinta zerourile sau mai multe copii ale expresiei anterioare;
[] - o clasa de caractere care marcheaza orice caractere aflat intre paranteze.
Daca primul character este ^, atunci vamarca oricare character mai putin cel
aflat intre paranteze;
^ - marcheaza inceputul unei linii noi ca un prim caracter al expresiei. Folosit si
ca negatie intre [];
$ - marcheaza sfarsitul liniei ca un ultim caracter al expresiei;
{} indica de cate ori modelul anterior este folosit pentru a fi marcat folosind un
numar sau doua;
\ - folosit pentru a indica un sfarsit; (exemplu : \n)
+ - marcheaza una sau mai multe erori ale expresiei ulterioare;
? marcheaza una sau mai multe erori ale expresiei ulterioare;
| - marcheaza fie urmatoarea expresie, fie ca care urmeaza acesteia;
/ - marcheaza expresia ulterioara dar doar daca este urmata de expresia viitoare;
() grupeaza o serie de expresii regulate intr-o singura expresie;

34

Exemple de expresii regulate:

[0-9] reprezinta un numar de biti; [0-9]+ va fi folosit pentru a defini un


numar intreg si vom folosi un singur digit. [0-9]* - prin aceasta scriere nu se va
folosi niciun digit in plus. Putem apoi sa extindem aceasta expresie pentru
numere zecimale : mai intai, vom dori ca primul numar sa fie zecimal si vom
folosi ca ultimul caracter sa fie digit [0-9]*\.[0-9]+. Acest model va afisa 0.0, 3.5, .
54321 etc.

Exemplu : ( vezi http://dinosaur.compilertools.net/lex/)

Acest program va afisa :

7.4Actiuni Lex
Atunci cnd o expresie este marcata, Lex execut aciunea
corespunztoare. Exist o aciune prestabilit, care const in copierea intrarii la
ieire.Acest lucru se realizeaz pe toate irurile potrivite. Se poate considera c
aciunile reprezinta ceea ce se face n loc de copierea intrarii la ieire; astfel, n
general, o regul care poate copia poate fi omisa. De asemenea, o combinaie de
caractere ce este omisa de la reguli i care apare ca intrare, este favorizata
pentru a fi printata la iesire.
Una dintre cele mai simple lucruri care se pot face tinand cont de cele de
mai sus,este de a ignora intrarea ca si declararea in C cu null. O regul
frecventacare doreste ca cele trei caractere de spatiere (blank, tab, i newline) sa
fie ignorate este urmatoarea:
35

n aciuni mai complexe, utilizatorul va dori de multe ori s tie textul


propriu-zis care corespunde unor expresii cum ar fi [a-z] +. Astfel, Lex atribuie
acest text ntr-o matrice de caractere externe numita yytext. Astfel o regula de
printare a numelui gasit va printa numele in yytext precum urmeaza :

Aceasta instructiune estefoarte generala si foarte utilizata incat poate fi


scrisa si sub forma urmatoare:

Uneori, este mai convenabil s se cunoasc sfritul a ceea ce a fost gasit;


prin urmare Lex ofer, de asemenea, un numar yyleng . Pentru a conta att
numrul de cuvinte cat i numrul de caractere n cuvintele de la intrare,
utilizatorul poate scrie [a-zA-Z] + {cuvinte + +; caractere + = yyleng;}
ceacumula in chars numrul de caractere din cuvintele recunoscute. Ultimul
caracter din ir pot fi accesate astfel:
;
Exemplu: Se ia n considerare un limbaj care definete un ir ca un set de
caractere ntre ("), i prevede c pentru a include un" ntr-un ir trebuie s fie
precedat de o \. Expresia regulat care se potrivete este urmatoarea: ( vezi
http://dinosaur.compilertools.net/lex/)

;
Apelul la functia yymore (), va duce la urmtoarea parte a irului. Ultima
instructiune de ncheiere a irului ar trebui s fie luat din codul `` normal user
processing.
n plus fa de aceste rutine, Lex permite, de asemenea, acces la rutinele
I / O pe care le utilizeaz cum ar fi : input()- returneaza urmatorul caracter de
intrare; output(c) scrie caracterul c la iesire; unput(c) pune caracterul c din
nou la intrare pentru a fi citit mai tarziu de catre functia input().
O alt bibliotec de rutin Lex pe care utilizatorul o foloseste contine
functia yywrap (), care este apelata de fiecare dat cnd Lex ajunge la un fiier
end-of. Dac yywrap returneaz un 1, Lex continu cu wrapup normal la captul
de intrare. Uneori, este convenabil de a asigura mai multe intrari pentru a ajunge
la o nou surs. In acest caz, utilizatorul ar trebui s ofere o yywrap care

36

aranjeaz noi intrari i ntoarce 0. Aceasta instruiete Lex pentru a continua


procesarea.Implicit yywrap ntoarce ntotdeauna 1.

7.5Definitii sursa Lex


Sursa Lex arata in felul urmator :

Lex este folosit pentru a transforma toate instructiunile intr-un


program.Orice surs care nu a fost interceptat de Lex este copiata n programul
generat. Exist treiclase de astfel de surse dupa cum urmeaza:
1)

2)

3)

Orice linie care nu este partea unei reguli Lex sau aciune care
ncepe cu un tab gol este copiat n programul degenerat al Lex. O
astfel de surs de intrare, nainte de primul delimitator %%, va fi
extern la orice funcie n cod; n cazul n care apare imediat dup
primul %%, apare ntr-un loc adecvat pentru declaraii de functii
scrise de Lex care conine aciuni. Acest lucru trebuie sa arate ca
fragmentele de program, i ar trebui s precead prima regul
Lex. Ca un efect secundar, liniile care ncep cu un gol sau tab, i
care conin un comentariu, sunt transmise prin intermediul
programului generat. Acest lucru poate fi folosit pentru a include
comentarii n oricare sursa Lex sau codul generat.
Orice instructiune cuprinsa intre liniile care conin doar% { si %}
este copiat ca in modelul de mai sus. Delimitatorii sunt stersi.
Aceasta informatie permite introducerea de text cum ar fi
declaraii preprocessor care trebuie s nceap n coloana 1, sau
linii de copiere, care nu arata ca programele.
Tot ce urmeaza dupa al treilea delimitator %% , indifferent de
formate, etc, este copiat dup ieirea Lex.

DefiniiileLexsunt datenainte de primul %% delimitator. Orice linie n


aceast seciune care nu este coninuta ntre% i{%}, i fiind n coloana 1,
trebuie sa defineasca siruri de caractere de substituie. Formatul de astfel de linii
reprezinta traducerea numelui i face ca irul dat sa fie o traducere asociata cu
numele. Numele i traducerea trebuie s fie separate decel puin un gol sau tab,
i numeletrebuie s nceap cu o liter. Traducerea poate fi numita de ctre
sintaxa:{nume}. Folosirea{D} pentru cifre i{E} pentru un cmp exponent,
deexemplu, s-ar putea folosi pentru recunoasterea numerelor:

37

Exemplu de program in Lex

In urmatorul exemplu de program scris in Lex, vom considera copierea unui


fiier de intrare la care se va aduga 3 la fiecare numr pozitiv divizibil cu 7:
( vezi http://dinosaur.compilertools.net/lex/ si Manualul A compact Guide to Lex
and Yacc scrisa de Thomas Niemann )

Regula[0-9] +recunoate iruri de cifre; apoi convertete cifrele n binar i


stocheaz rezultatul nk. Operatorul % este folosit pentru a verifica dac este
divizibil cu7; dac este, acesta este incrementat cu 3. Se incrementeaz valoarea
absolut a tuturor valorilor negative divizibile cu 7 Pentru a evita acest lucru, se
mai adaug cteva reguli dup cum urmeaza:

Siruri de caractere numerice care conin un``.'' sau precedate de o liter


vor fi preluate de ctre unul din ultimele dou reguli. If-else-ul a fost inlocuit de o
expresie in C pentru a economisi spaiu; forma b:? C nseamn``if a then b else
c.

8.Introducere Yacc

38

Yacc este folosit in general,pentru a impune structura deintrare a unui


program. Utilizatorul Yacc pregtete o secventa a procesului de intrare; aceasta
include la randul ei,rutine care descriu structura de intrare i o rutin de nivel
sczut pentru a face intrarea de baz. Yacc genereaz apoi o funcie pentru
controlul procesului de intrare.
Yacc este scris ntr-un dialect portabil C , precum i aciunile, i ieirea de
subrutin, sunt tot n C. Mai mult dect att, multe dintre conveniile sintactice
aleYacc urmeaz limbajul C.
Ca exemplu, o regula gramaticala poate fi considerata urmatoarea:

Aici, data, MONTH_NAME, ziua DAY, si anul YEAR reprezint structuri de


interes n procesul de intrare; probabil, MONTH_NAME, DAY si YEAR sunt
definiten alt parte.Virgula``,'' este inclusa n ghilimele simple; acest lucru
implic faptul c virgula este facuta s apar literalmente n intrare. Punctul i
virgula servesc doar ca semne de punctuaie n regul, i nu au nici o
semnificaien controlul de intrare.

O parte important a procesului de intrare este realizat de ctre


analizorul lexical. Aceasta rutina citeste fluxul de intrare, recunoscnd structurile
de nivel inferior, i comunic aceste token-uri pentru parser.

8.1Specificatii Yacc
Numele se refer fie la tokenuri, fie la simboluri neterminale. Yacc necesit
nume de tokenuri care urmeaz s fie declarate ca atare. Este adesea de dorit
s se includ analizorul lexical ca parte a fiierului specificaie; poate fi util s se
includ alte programe, de asemenea. Astfel, fiecare fiier este format din trei
seciuni: declaraiile, secventele gramaticale, precum i programele. Seciunile
sunt separate printr-un dublu la suta``%%''.(Procentul ``%'' este folosit n
general n specificatiile YACC ca o instructiune de iesire.)

Seciunea declaraiei poate fi goala. Mai mult dect att, n cazul n care
seciunea de programe este omisa, al doilea % % poate fi omis, de asemenea.
Totusi, cea mai mica definitie a unui program in Yacc poate fi:
39

Golurile, filele, iliniile noi sunt ignorate cu excepia faptului c acestea


nu pot aprea n nume sau in simbolurie cu caractere rezervate. Comentarii pot
aprea ori de cte ori un nume este legal; acestea sunt nchise n/*. ..*/, la fel
ca n C i PL/I.
Seciunea reguli ruleseste formata din una sau mai multe reguli
gramaticale. O regul gramaticala are forma urmatoare:
A reprezint un nume de neterminal, i BODY reprezint o
secven de zero sau mai multe nume i litere.
Un literal const dintr-un caracter nchis ntre ghilimele simple. Ca i n
C, backslash``\'' este un caracter de iesire, i toate iesirile C sunt recunoscute.
Caracterul NUL L('\ 0' sau0), nu ar trebui s fie utilizat n reguli gramaticale.

n cazul n care exist mai multe reguli gramaticale pe partea stng,


bara vertical|'' poate fi utilizata pentru a evita rescrierea partii stngi. n plus,
punctul i virgula de la sfritul unei reguli poate fi abandonata nainte de o
bar vertical.

se poate scrie astfel:

8.2Actiuni Yacc
O aciune este o declaraie arbitrara in C, i, ca atare, poate face intrarea
i ieirea, apel ul de subprograme, i poate modifica vectori i variabilele
externe. O aciune este specificata de ctre una sau mai multe declaraii,
nchise n acolade{ i}.(vezi
http://dinosaur.compilertools.net/yacc/index.html )
40

Pentru a facilita comunicarea uoar ntre aciuni i parser, declaraiile


de aciuni sunt modificate uor.Simbolul ``$'' este folosit ca un semnal pentru
Yacc.
Pentru a returna o valoare, aciunea stabilete n mod normal
pseudovariabilele`` $$'' la o anumit valoare. De exemplu, o aciune care nu
face nimic, dar returneaza valoarea 1 este urmatoarea:

Pentru a obine valorile returnate de aciunile anterioare i analizorul


lexical, aciunea poate utiliza pseudo-variabilele $ 1,$ 2,. .., care se refer la
valorile returnate de ctre componente n partea dreapt a unei reguli, citind
de la stnga la dreapta.
n multe aplicaii, iesirea nu se face direct de catre aciuni; mai degrab,
o structur de date, cum ar fi un parse tree, este construit n memorie, i
transformrile sunt aplicate la acesta nainte ca iesirea sa fie generata. Arborii
sintactici sunt deosebit de uor de construit, avand rutine pentru a construi i
a menine structura dearbore dorit. De exemplu, s presupunem c exist o
funcie nod C, scrisa ,astfel c apelul creeaz un nod cu eticheta L, i
urmaii N1 i N2, i returneaz indexul nodului nou creat. Apoi,arborele
sintactic poate fi construit prin furnizarea de aciuni, cum ar fi:

8.3Analiza lexicala
Utilizatorul trebuie s furnizeze un analizor lexical pentru a citi fluxul de
intrare i de a comunica token-uri parserului. Analizorul lexical este o funcie
de prim rang-ntreg numit yylex. Funcia returneaz un ntreg, adica un
numr de tokenuri, care reprezint un semn de citire. Dac exist o valoare
asociat cu acel simbol, acesteia i-ar trebui atribuita o variabila externa
yylval.
Parser-ul i analizorul lexical trebuie s convin cu privire la aceste
numere simbolice, pentru ca aceasta comunicare dintre ele s aib loc.
41

Numerele pot fi alese de ctre Yacc, sau alese de utilizator. n ambele cazuri,
mecanismul in C :``#define'' este folosit pentru a permite analizorului lexical
de a returna aceste numere simbolice. De exemplu, s presupunem c numele
simbol DIGIT a fost definit n specificatiileYacc. Poriunea relevant a
analizorului lexical ar putea arata ca mai jos: (vezi
http://dinosaur.compilertools.net/yacc/index.html )

Intenia este de a returna un numr token de DIGIT, i o valoare egal


cu valoarea numeric a cifrei.n cazul n care codul analizorului lexical este
plasat n seciunea de programe a fiierului de specificatii, cifra de identificare
va fi definita ca numrul de tokenuri asociat cu cifra simbolic.

Exemplu Yacc

Exemplul urmator va consta in descrierea unui program pentru calculatorul


de buzunar. Acesta contina 26 de registre, numite de la a la z putand efectua
adunari, scaderi, inmultiri si impartiri, precum si operatii logice &, | si %. Acest
program va exemplifica cum calculatorul de buzunar poate rezolva erorile si cum
poate folosi ambiguitatile. (vezi Manualul Lex and Yacc scrisa de John R.Levine,
Tony Mason si Doug Brown).
%{
# include<stdio.h>
# include<ctype.h>
int
int

regs[26];
base;

%}
%start

list

42

%token

DIGIT

%left
%left
%left
%left
%left

'|'
'&'
'+' '-'
'*' '/'
UMINUS

%%

/*

LETTER

'%'
/*

list :
|
|

beginning

supplies
of

precedence

rules

/* empty */
list stat '\n'
list error '\n'
{
yyerrok;

section

for

unary

minus

*/

*/

;
stat :

expr

{
LETTER
{

printf( "%d\n", $1 );
'=' expr
regs[$1] = $3; }

;
expr :

'('

expr
{
expr '+'
{
expr '-'
{
expr '*'
{
expr '/'
{
expr '%'
{
expr '&'
{
expr '|'
{
'-' expr
{
LETTER
{
number

|
|
|
|
|
|
|
|
|
|
;
number

')'
$$ =
expr
$$ =
expr
$$ =
expr
$$ =
expr
$$ =
expr
$$ =
expr
$$ =
expr
$$ =

$2;

$1

$3;

$1

$3;

$1

$3;

$1

$3;

$1

$3;

$1

&

$3;

$$

$1 | $3; }
%prec UMINUS
- $2; }

$$

regs[$1];

DIGIT
{
$$ = $1;
base
number DIGIT
{
$$ = base * $1

($1==0)

$2;

10;

;
%%

/*

yylex() {
*/

start program

*/

/* rutina de analiza lexicala */


/* returneaza LETTER cu litere mici,
/*
/*

yylval = 0

prin

returneaza DIGIT cu litere mici, yylval = 0 prin


celelalte caractere sunt returnate imediat */

25
9

*/

43

int

c;
while(
/*

(c=getchar())

cnu mai este gol

if( islower( c ) )
yylval = c - 'a';
return ( LETTER );
}
if( isdigit( c ) )
yylval = c - '0';
return( DIGIT );
}
return( c );
}

==

' '

{/*

skip la goluri

*/

*/

Bibliografie

Compilers: Principles, Techniques, and Tools , Alfred V. Aho, Monica S. Lam, Ravi
Sethi, and Jeffrey D. Ullman
Compiling with C# and Java, Pat Terry
44

Compiler Construction, Niklaus Wirth


Improving Abstract Syntax Tree based Source Code Change Detection, Wrsch,
Michael.
Understanding source code evolution using abstract syntax tree matching, Neamtiu,
Iulian; Foster, Jeffrey S.; Hicks, Michael.
Parsing Techniques - A Practical Guide 1st Ed. web page of book includes
downloadable pdf.
http://cs.stackexchange.com/questions/43/language-theoretic-comparison-of-ll-and-lrgrammars
http://dinosaur.compilertools.net/lex/
http://dinosaur.compilertools.net/yacc/index.html
Cartea Lex and Yacc scrisa de John R.Levine, Tony Mason si Doug Brown :
http://books.google.ro/books?
id=fMPxfWfe67EC&printsec=frontcover&hl=ro#v=onepage&q&f=false
Cartea A compact Guide to Lex and Yacc scrisa de Thomas Niemann :
http://books.google.ro/books?
id=fMPxfWfe67EC&printsec=frontcover&hl=ro#v=onepage&q&f=false

Bibliografia figurilor:

Figure 1..http://labs.cs.upt.ro/labs/lft/html/LFT00.htm

Figure2.............http://en.wikibooks.org/wiki/Introduction_to_Programming_
Languages/Compiled_Programs

Figure3........... http://wwwverimag.imag.fr/TOOLS/DCS/bip/doc/latest/html/compiler-enginespresentation.html

Figure4............ http://en.wikipedia.org/wiki/File:Parser_Flow.gif

Figure5.....http://en.wikipedia.org/wiki/File:Abstract_syntax_tree_for_
Euclidean_algorithm.svg
Figure7.............http://en.wikipedia.org/wiki/File:ShiftReduce_Parse_Steps_f
or_A*2%2B1.svg

Figure8............. http://dinosaur.compilertools.net/lex/index.html(cut
picture)

Figure9............. http://dinosaur.compilertools.net/lex/index.html(cut
picture)

45