Forma de Învățământ : zi [632252]
Universitatea din Oradea
Facultatea de Ingineri e Electrică și Tehnologia Informaț iei
Programul de studiu: Calculatoare
Forma de Învățământ : zi
PROIECT DE
DIPLOM Ă
Coordonator științific:
Conf. dr. ing Ovidiu Constantin Novac Absolvent: [anonimizat]
2018
Universitatea din Oradea
Facultatea de Inginerie Electric ă si Tehnologia Informaț iei
Programul de studiu: Calculatoare
Forma de Învățământ : zi
Analizor Lexical în Java
Coordonator științific:
Conf. dr. ing Ovidiu Constantin Novac Absolvent: [anonimizat]
2018
UNIVERSITATEA DIN ORADEA
FACULTATEA de Inginerie Electrică și Tehnologia Informației
DEPARTAMENTUL Calculatoare și Tehnologia Informației
TEMA ANALIZOR LEXICAL ÎN JAVA
Proiectul de finalizare a studiilor alstudent: [anonimizat]
1). Tema proiectului de finalizare a studiilor A nalizor lexical în Java
2). Termenul pentru predarea proiectului 5 septembrie 2018
3). Elemente inițiale pentru elaborarea proiectului de finalizare a studiilor : Java
4). Conținutul proiectului de finalizare a studiilor Introducere, Noțiuni generale,
Limbaje regulate și automate finite, Descrierea aplicației software, Concluzii,
Bibliografie
5). Locul de documentare pentru elaborarea proiectului: Biblioteca
Universi tății din Oradea, laboratoarele Facultății de Inginerie Electri că
și Tehnologia Informației
6). Data emiterii temei 1 octombrie 2017
Coordonator științific:
Conf. Dr. Ing. Novac Ovidiu Constantin
0
Cuprins
Capitolul I. Introducere ………………………….. ………………………….. ………………………….. …………………. 1
Capitolul II. Noțiuni generale ………………………….. ………………………….. ………………………….. …………. 2
II.1 Compilatorul ………………………….. ………………………….. ………………………….. ………………………. 4
II.2 Limbaju l JAVA ………………………….. ………………………….. ………………………….. …………………….. 6
II.3 Noțiunea generală de limbaj ………………………….. ………………………….. ………………………….. .. 12
Capitolul III Limbaje regulate si automate finite ………………………….. ………………………….. …………… 13
III.1 Limbaje regulate și expresii regulate ………………………….. ………………………….. ………………… 14
III.2 Sisteme tranziționale ………………………….. ………………………….. ………………………….. ………… 15
III.3Analiza lexicală ………………………….. ………………………….. ………………………….. …………………. 17
III.4Analizorul lexical pentru limbajul Java ………………………….. ………………………….. ……………….. 20
Capitolul IV Descrierea aplicatiei software ………………………….. ………………………….. ………………….. 27
Capitolul V Concluzii ………………………….. ………………………….. ………………………….. …………………… 32
Bibliografie ………………………….. ………………………….. ………………………….. ………………………….. …… 33
1
Capitolul I. Introducere
Am ales această temă pentru a-mi dezovlta și satisface nevoia de conoaștere pentru
limbajul Java, acesta fiind cel ma i utilizat limbaj de programare , folosit în multiple do menii,
de la aplicații software uzuale precum plugin -urile pentru platoformele de streaming până la
bazele creării sitemului de operare Android care la ora actual ă ocupă peste 80% din piața
mobilă .
Această lucrare are ca și scop principal ilustrarea importanței procesului de analiză
lexical desfășurat pe 3 etape.
ETAPA1: Descrierea unităților lexicale cu ajutorul expresiilor regul ate
Definițiile riguroase a unităților lexicale sunt luate de la sursă oficială și manual se vor
construi expresii regulate pentru fiecare caz întâlnit.
ETAPA2: Corespunzător expresiilor obținem automatele f inite deterministe și se realizează
sistemul tran zițional
ETAPA3: Ultima etapă presupune programare efectivă a analizatorului lexical.
Se vor prezenta noțiuni generale care aparțin domeniului precum noțiunea de
compilator, limbaj, analiză lexicală, dar și specializate precum expresi i regulate, sistem
tranzițional precum și informații despre limbajul de programare Java. Această lucrare
ilustrează analiza lexicală generală din punct de vedere al limbajului de programare Java. Se
va pune foarte mult acce ntul pe exemple relevante Java.Versiunea d e Java studiata este SE
7.Sunt prezentați pașii necesari pentru crearea unei aplicații de analiză lexicală . Programul
analizează un fișier de intrare și afișează valoarea semantică și lexicală a unităților detectate .
2
Capitolul II . Noțiuni generale
Un limbaj de programare este un limbaj care are drept scop descrierea unor procese de
prelucrare a anumitor date și a structurii acestora care se realizează în general cu ajutorul unui
sistem de calcul. Limbajele se pot clasifica în limbaje de nivel superior sau inferior.
Există în prezent un număr mare de limbaje de programare de nivel înalt sau evoluate
care se caracterizează printr -o anumită naturalețe, în sensul că descrierea procesului de
prelucrare cu ajutorul limbajului este apropiată de descrierea natu rală a procesului respectiv.
Dintre limbajele de acest tip cu o anumită răspândire în momentul de față menționăm
limbajul JAVA. Precizăm că delimitarea domeniului de aplicabilitate al unui limbaj de
programare nu are un caracter absolut. De aceea o clasifi care sau o anumită ierarhizare a
limbajelor de programare utilizate în momentul de față nu numai că este dificilă, dar nu ar
avea nici un beneficiu deoarece foarte des apar limbaje noi și diverse.
O altă clasă importantă de limbaje, sunt limbajele de asamb lare, sau de nivel inferior,
ale căror caracteristici depind de sistemul de calcul considerat. În general, fiecare sistem de
calcul (sau tip de sistem de calcul), are propriul său limbaj de asamblare; de exemplu,
limbajul procesoarelor INTEL se numește ASE MBLER. Instrucțiunile unui limbaj de
asamblare corespund cu operațiile simple ale sistemului de calcul iar stocarea datelor în
memorie este realizată direct de utilizator la nivelul locațiilor de memorie. Există de
asemenea anumite pseudo -instrucțiuni sau directive referitoare la alocarea memoriei,
generarea datelor, segmentarea programelor, și altele, precum și macroinstrucțiuni care
permit generarea unor secvențe tipice de program sau accesul la bibliotecile de subprograme.
În afară de limbajele evoluate și de limbajele de asamblare, există numeroase limbaje
specializate, numite uneori și de comandă, care se referă la o anumită clasă de aplicații.
Menționăm de exemplu limbajul pentru sisteme expert JESS, limbajele utilizate în cadrul
software -ului ma temati c Maple, și multe altele.
În general însă astfel de limbaje nu sunt considerate limbaje de programare
propriu -zise. Un program redactat într -un limbaj de programare poartă denumirea de program
sursă.
Fiecare sistem de calcul, în funcție de partic ularitățile sale, are un anumit limbaj
propriu, numit cod mașină, acesta fiind singurul limbaj înțeles de procesorul sistemului. Un
astfel de limbaj depinde de structura procesorului, de setul de instrucțiuni, de posibilitățile de
adresare, etc. Un program redactat în limbajul cod mașină al sistemului de calcul îl numim
program obiect. Procesul de transformare al unui program sursă în program obiect se numește
compilare sau translatare. De obicei termenul de compilare este utilizat numai în cazul
limbajelor evoluate, în cazul limbajelor de asamblare fiind utilizat termenul de asamblare.
3
Compilarea (respectiv asamblarea) este efectuată de un program al sistemului numit
compilator (asamblor). De multe ori compilatoarele nu produc direct program obiect, ci un
text intermediar apropiat de programul obiect, care în urma unor prelucrări ulterioare devine
program obiect. De exemplu, compilatoarele sistemelor de operare DOS produc un fișier text
numit obiect (fișiere cu extensia obj) care în urma unui proces numit ed itare de legături și a
procesului de încărcare în memorie devine program obiect propriu -zis, numit program
executabil (fișiere cu extensia exe). Există posibilitatea cuplării mai multor module de
program realizate separat sau provenite din limbaje sursă di ferite, rezultând în crearea unor
biblioteci de programe în formatul intermediar și utilizarea lor în alte programe.
Un program sursă poate fi executat de către sistemul de calcul direct, fără
transformarea lui prealabilă în program obiect. În acest caz, p rogramul sursă este prelucrat de
un program al sistemului numit interpretor. Acesta încarcă succesiv instrucțiunile
programului sursă, le analizează din punct de vedere sintactic și semantic apoi le execută sau
efectuează anumite operații auxiliare. O astf el de interpretare pură a unui program sursă este
totuși rar întâlnită.Cel mai adesea interpretoarele transformă mai întâi programul sursă într -un
format intern și crează anumite tabele cu informații, apoi textul intern este preluat de o
componentă pentru interpretare apoi executat. Faza de generare a textului intermediar este în
esență asemănătoare cu procesul de compilare iar formatul intern este asemănător, astfel
interpretarea și compilarea sunt comune.
4
II.1 Compilatorul
În ceea ce urmează voi prezenta unele principii și tehnici de compilare pentru limbaje de
nivel înalt, definite global sau parțial cu gramatici generative Chomsky de tipul doi sau trei;
iar limbajul de referință va fi limbajul JAVA.
Procesul de compilare este un proc es relativ complex și se bazează pe operații care au un
anumit caracter de autonomie. Din aceste motive procesul de compilare este de obicei
descompus în mai multe subprocese sau faze, fiecare fază putând fii considerată de sine
stătătoare. În principiu ac este faze sunt parcurse secvențial (pot să existe și anumite excepții)
iar programul sursă este transformat succesiv în formate intermediare. Se poate considera că
fiecare fază primește de la faza precedentă un fișier cu programul prelucrat de faza
respect ivă.Se prelucrează fișierul respectiv și se furnizează un fișier de ieșire, iarăși într -un
format intermediar, acesta fiind utilizat în faza următoare. Există cinci faze de compilare
principale: analiza lexicală, analiza sistactica, generarea formatului in termediar, generarea
codului, optimizarea codului și două faze auxiliare: tratarea erorilor și tratarea tabelelor.
Figura II.1 [1]
5
În continuare vom prezenta fiecare fază pe scurt insistând doar asupra analizei
lexicale.
Analiza lexicală are ca obiectiv principal determinarea unităților lexicale ale unui
program, furnizarea codurilor acestora și detectarea eventualelor erori lexicale. Pe lângă
aceste operații de bază, la analiza lexicală se mai pot efectua anumite operații auxiliare
precum: elimi narea blank -urilor (dacă limbajul permite utilizarea fără restricții a acestora),
ignorarea comentariilor, diferite conversiuni ale unor date (care se pot efectua la această
fază), completarea tabelelor compilatorului. Este posibil ca analiza lexicală să s e realizeze
global, pentru întregul program, caz în care analizorul lexical furnizează un fișier de ieșire cu
programul în format unitar și cu tabelele de informații necesare. Cel mai adesea însă
analizorul lexical tratează o anumită secvență de program sa u lucrează chiar ca un
subprogram. În acest ultim caz, analizorul lexical este o rutină a analizorului sintactic care
este apelată succesiv pentru a furniza câte o unitate lexicală.
Analiza sintactică determină unitățile de text pentru care se pot genera f ormatul
intermediar și efectuează analiza lexicală a programului. Aceasta este faza principală a unui
compilator, deseori celelalte fiind apelate de aceasta pentru a se putea efectua părți din faza
aceasta. Tot aici se definitivează tabelele de informații și se realizează prelucrarea er orilor.
Generarea formatului intermediar transformă programul într -o formă numite
intermediară de la care se obține relativ ușor programul obiect. Această fază este strâns legată
de proiect ant în sensul formatului intermediar ales de proiectant și a metode i de implementare
aleasă de el.
Generarea codului este faza care generează codul obiect. În această fază se obține
programul în limbaj de asamblare obținut din codul sursă scris în limbaj evoluat. În mod
normal această fază s e bazează pe rutine generatoare de cod co respunzător diverselor unități.
Optimizarea codului presupune îmbunătățirea codului obiect pentru a se obține un
program cât mai eficient din punct de vedere al memoriei și timpului de prelucrare. Cel mai
des se eli mină memoria redundantă și se optimizează ciclurile.
Tratarea erorilor și tratarea tabelelor influențează performanța din punct de vedere al
timpului de execuție și se elimină nișt e erori sintactice din program [1].
6
II.2 Limbajul JAVA
Java este un limbaj de programare concurent orientat obiect pe bază de clase dezvoltat
special pentru a avea cât mai puține dependențe de implementare posibil. Are intenția de a
permite programatorilor să scrie o data și să ruleze oriunde (awrite once,run
anywhere”;WOR A), adică codul care rulează pe o platformă nu trebuie recompilat pentru a
rula pe altă platformă
.
Figura II.1 [3]
Aplicațiile java sunt compilate în așa fel încât pot rula pe orice Java Virtual Machine
(JVM) indiferent de arhitectura sistemului de calcul. Începând din 2012 java este cel mai
popular limbaj de programare, mai ales în rândul aplicațiilor de web client -server, având
peste 10 milioane de utilizatori. Limbajul este derivat din limbaje de nivel inferior C și C++
dar are mai puține facilită ți de nivel inferior decât acestea. Compilatoarele Java, mașinile
virtuale Java și bibliotecile de clase implementate original și luate ca sisteme de referințe
pentru alte implementări și modificări au fost dezvoltate în 1991 de către Sun și au fost
lansat e pentru prima dată în 1995.
Părinții fondatori James Gosling, Mike Sheridan și Patrick Naughton au inițiat în
iunie 1991 proiectul limbajul de programare Java. A fost dezvoltat inițial pentru televiziunea
prin cablu dar a fost prea avansat la vremea aceea pentru acea industrie. Denumirea inițială a
fost“Oak” (stejar ) dată după stejarul din fața biroului lui Gosling apoi a fost redenumit în
“Green” mai apoi in “Java” după denumirea cafelei pe care se presupunea că beau în cantități
mari creatorii limbajului . Gosling dorea să implementeze o mașină virtuală și un limbaj care
respectă un stil deja familiar de C/C++, [3].
7
Existau 5 caracteristici principale î n crearea limbajului Java:
1. simplu,orientat obiect și familiar
2. robust și sigur
3. neutru din punct de vedere al arhitecturii și portabil
4. performan țe mari
5. interpretabil, pe threaduri și dinamic
Versiunile java majore si datele lans ărilor:
JDK 1.0 (Ianuare 21, 1996)
JDK 1.1 (Februarie 19, 1997)
J2SE 1.2 (Decembrie 8, 1998)
J2SE 1.3 (Mai 8 , 2000)
J2SE 1.4 (Februarie 6, 2002)
J2SE 5.0 (Septembrie 30, 2004)
Java SE 6 (Decembrie 11, 2006)
Java SE 7 (Iulie 28, 2011)
O caracteristică a platformei Java este portabilitatea ceea ce înseamnă că programele
scrise în limbajul java trebuie să ruleze în mod similar indiferent de sistemul de operare sau
configurația hardware a sistemului de calcul pe care rulează.Această caracteristică este
menținută prin compilarea codului sursă java într -o reprezentare intermediară numită Java
Bytecode.Reprezentarea res pectivă este analoagă reprezentării cod mașină dar este parsata
către mașina virtuală java care aceasta este specifică sistemului de calcul pe care este menit să
ruleze.
Java nu aparține nici unui standard ANSI sau ISO deci aparține unui standard “de
facto ” însemnând că a devenit în sine un limbaj standard dominând piața din cauza liberii
alegeri a utilizatorilor.Implementarea oficială este împărțită în 2 distribuții:
Java Runtime Enviroment (JRE) ce con ține componentele necesare pentru a rula
programe Java si este menit utilizatorilor aplica țiilor.
Java Development Kit (JDK) ce con ține componentele necesare dezvolt ării programelor
si este menit programatorilor de Java.
Sintaxa Java este în principal derivată din C++, dar spre deosebire de aceasta,
limbajul Java a fost construit aproape exclusiv ca fiind orientat obiect.Totul se scrie într -un
fișier clasă și este văzut ca fiind obiect cu excepția tipurilor de date primitive care nu sunt
văzute ca clase pentru a facilita performanța programului. Spre deosebire de C++, Java nu
suportă supraîncărcarea operatorilor sau moșteniri multiple pentru clase. Acest lucru
simplifică limbajul și ajută în prevenirea unor potențiale erori și a design -ului anti -model
(anti-pattern). Java folose ște metodele de coment arii asem ănătoare lui C++. Exist ă 3 stiluri
diferite de com entarii,[3]:
8
– coment ariu pe o singur ă linie marcat cu / /,coment ariu pe mai multe linii care se
marcheaz ă cu deschiderea /* și se închide cu */ , și stilul de comentare javadoc
care se deschide cu /** și se închide cu */.Stilul Javadoc de comentare permite utilizatorilor
să ruleze executabilul Javadoc pentru a compila documentație pentru program.Exemple:
Figura II.2
Exemplul “Hello world”:
La deschiderea unui program se genereaz ă tradiționalul program Hello Java. Acesta se poate
scrie astfel:
class HelloWorldApp{
public static void main(String[] args){
System.out.println(“Hello World!”);
}
}
Pentru a compara acest limbaj de programare cu altele s ă analiz ăm acest exemplu.
Fișierele sursă trebuie să fie numite după clasa publică pe care o conțin urmate de sufixul
.java,de exemplu, HelloWorldApp.java. Aceasta este pentru început compilat în formatul
bytecode folosind un compilator Java și se produce un fișier numit HelloWorldApp.class.
Doar după acest pas se poa te executa sau lansa. Fișierul sursă java poate să conțină doar o
clasă publică, dar poate să conține oricâte clase care au alt acces și oricâte clase interioare
publice.
O clasă care nu este publică poate fi stocată în orice fișier .java. Compilatorul va genera un
fișier clasă pentru orice clasă definită în codul sursă. Numele fișierului clasă este dat de
numele clasei urmat de extensia “.class”. Pentru generarea fișierelor de clasă, clasele anonime
9
sunt tratate ca și cum numele lor ar fi dat de concatenarea numelui clasei în care sunt înscrise,
caracterul $ și a unui număr întreg [3].
Cuvântul cheie public arată că o funcție poate fi apelată și în alte clase sau că o clasă poate fi
folosită de alte clase care nu corespund aceleiași ierarhii. Ier arhia claselor este legată de
numele directorului în care fișierul .java este localizat.
Cuvântul cheie static în fața unei metode indică o funcție statică care este asociată numai
clasei și nu cu orice instanță specifică a clasei. Doar metodele statice po t fi invocate sau
apelate fără a se referi la un obiect. Metodele (funcțiile) statice nu pot accesa vreun membru
nestatic al unei clase.
Cuvântul cheie void indică faptul că funcția main nu returnează nici o valoare către apelant.
Dacă un program java se t ermină cu un cod de eroare, acesta trebuie să apeleze
funcția System.exit() în mod explicit.
Cuvântul main nu reprezintă un cuvânt cheie în limbajul Java, este pur și simplu funcția pe
care lansatorul java o apelează pentru a îi da controlul programului scr is, adică pentru a îi
începe rularea. Clasele Java care rulează în medii specifice precum applet -uri sau Enterprize
JavaBean nu necesită funcția main. Un program Java poate să conțină mai multe clase care
conțin funcția main iar în acest caz mașinii virtua le îi trebuie ilustrat explicit de la care clasă
să înceapă rularea.
Funcția main trebuie să accepte o mulțime (un array) de obiecte de tip String (mulțime de
caractere). Prin convenție, este numit args deși se poate folosit orice identificator. Începând
cu Java 5, funcția main poate folosi și argumente variabile care permit invocarea metodei cu
un număr arbitrar de argumente string . De exemplu:
public static void main(String… args)
Efectul acestei declarații este identic din punct de vedere semantic cu d eclarația normală,
adică se acceptă tot o mulțime de obiecte de tip array, dar permite o sintaxă alternativă pentru
crearea și p arsarea mulțimii de tip string.
Lansatorul java lansează Java prin încărcarea în memorie a unei clase specificată în linia de
comandă sau dată ca și atribut într -un fișier JAR și apelarea metodei public static void
main(String[]). Programele stand -alone (aplicații de sine stătătoare) trebuie să prezinte în
mod explicit funcția aceasta. Parametrul String[] args este o mulțime de o biecte de tip string
care conține orice argumente parsate clas ei. Parametri pentru main sunt de obicei par sade
folosind linia de comandă.
Afișarea face parte din librăriile standard Java. Clasa System definește un câmp public static
numit out. Obiectul out este o instanță a clasei a €œPrintStream”și prezintă multe metode de
afișarea a datelor către ieșirea standard, inclusiv “println(String)” care mai adaugă la finalul
Stringului și caracterul de linie nouă .
10
Stringul “Hello,world!” este convertit la un obiec t de tip String de c ătre compilator [3].
Java este un limbaj foarte folosit la ora actuală. Un exemplu bun este dat de Google și
Android Inc. care au ales să folosească Java ca și sistemul de bază pentru crearea sistemului
de operare Androi d. Deși sistem ul de operare a telefoanelor este construit pe baza kernelului
linux care este scris în C, Androidul folosește Java pentru aplicațiile dezvoltate.Gaikai
folosește plugin -ul de Java browser pentru a face streaming de demo -uri de jocuri pentru PC.
Gaikai îns eamnă în japoneză “ocean deschis” și este un serviciu pe bază de cloud computing
care prin intermediul internetului transmite date către utilizatori. La ora actuală a apărut doar
un competitor în acest domeniu “On-live” [21].
Figura II.3 [3]
Figura II.4 [21]
11
Bibliotecile de clase Java se compun din bytecodurile compilate a codurilor sursă create de
implementarea JRE pentru a facilita dezvoltarea aplicațiilor Java . Exemple:
Bibliotecile principale (core libraries) conț in:
biblioteci de colectie care implementeaza structurile de date precum dictionare, liste
etc.
biblioteci de procesare a fisierelor XMLpentru parsare, transforrmare, validare etc.
biblioteci pentru securitate
biblioteci de internationalizare si localizare
Bibliotecile de integrare permit creatorilor de aplicații să comunice cu sisteme externe :
Java Database Connectivity (JDBC) pentru
accesarea bazelor de date
Java Naming and Directory Interface (JNDI)
pentru cautare si descoperirea obiectelor dupa
nume
RMI si CORBA pentru crearea aplicatiilor
distribuite
JMX pentru evidentierea si monitorizarea
aplicatiilor (pentru management) [3]
.
Figura II.4 [3]
Biblioteci pentru interfa ța utilizator care includ:
Abstract Window Kit (AWT) care include componente pentru grafica utilizator (GUI)
si mijloacele necesare pentru manipularea lor
SWING care este construit in AWT dar contine implementari a widgeturilor AWT
biblioteci pentru tratarea semnalelor audio
Din punc t de vedere al documentației Javadoc este un sistem de documentație care este foarte
vast și folosit de mulți creatori de aplicații Java. Acest sistem este creat de Sun Microsystems
și le oferă programatorilor un sistem bine organizat pentru documentarea a plicațiilor lor .
Corporatia Sun a creat 4 distribuții a sistemului Java, fiecare specializată pe un anumit mediu
de rulare a aplica țiilor:
Acestea sunt prezentate în figura II.4.
Java Card – smartcarduri
Java Platform, Micro Edition – medii cu resurse limitate
12
Java Plat form, Standard Edition – stații de lucru obiș nuite
Java Platform, Enterprise Edition (Java EE) – mediul de internet si enterprise .
II.3 Noțiunea generală de limbaj
Noțiunea de limbaj poate fi definită pe multe domenii precum mat ematică, logică,
informatică, lingvistică. Noi vom trata un limbaj cu definiții generale și operații din punct de
vedere al informaticii și vom introduce atât concepte generale cât și axate pe studiul nostru.
Un alfabet este o mulțime finită și nevidă iar elementele le vom numi litere sau
simboluri.Un cuvânt peste alfabet este un șir de litere din alfabet. Un limbaj peste alfabet este
o mulțime de cuvinte peste alfabet.Pentru a înțelege mai ușor conceptul nostru de cuv ânt și
alfabet vom ilustra exemple simple . Un exemplu de alfabet este mulțimea de caractere
{Q,W,E} iar un cuvânt peste acest alfabet (care se poate genera) este de exemplu
QWEWQEW. De aici se înțe lege că un alfabet conține câteva caractere iar un cuvânt este o
înșiruire a caracterelor.O ob servație ar fi faptul că în funcție de proprietățile limbajului acesta
s-ar putea să accepte și elementul vid, adică cuvântul care nu are nici un caracter.
Limbajele sunt mulțimi de cuvinte, deci asupra lor se pot aplica operații cu mulțimi
sau operații sp ecific teoriei limbajelor form ale. Operațiile uzuale definite sunt binare:
concatenare, intersecție, reuniune, diferență, câtul la dreapta și unare: complementul
limbajului, închiderea Klein și inversul unui limbaj. Nu vom aprofunda operațiile deoarece nu
sunt necesare pentru cazul nostru. Vom studia doar limbajul Java, nu și crearea lui sau
compunerea cu alte limbaje.
Dacă limbajul nostru este o mulțime finită atunci el poate fi descris ilustrând efectiv
cuvintele. Dacă este o mulțime infint ă atunci poate fi descris punând în evidență structura
cuvintelor .
Un limb aj poate fi specificat formal în mai multe moduri:
șiruri produse de o anumită gramatică
șiruri notate prin expresii regulate
șiruri acceptate de un automate finite
Există două procedee generale p entru definirea unui limbaj: generativ prin generarea
tuturor cuvintelor limbajului și analitic bazat pe determinarea apartenenței unui cuvânt la
limbaj [2].
13
Capitolul III Limbaje regulate si automate finite
Automatele finite sunt mecanisme pentru recunoașterea limbajelor finite. Un automat
finit are 2 componente importante:banda de intrare și dispozitivul de comandă .Pe banda de
intrate sunt înregistrate simbolurile alfabetului de intrare și se generează cuvinte.Dispozitivul
de comandă se află în pe rmanență într -o anumită stare care aparține schemei de stări .
Automatul finit funcționează în pași discreți .Un pas presupune citirea unui caracter
de pe banda de intrare și schimbarea stării dispozitivului de comandă în mod corespunzător.
Automatul se o prește când se ajunge la finalul bandei de intrare, astfel obținându -se o stare
finală .
Din punct de vedere matematic un automat finit este un sistem
unde:
Un automat finit se poate bloca doar dacă în starea actuală se citește un caracter de pe
banda de intrare și folosind acest caracter și starea noastră rezultatul este vid.
Dacă bazându -se pe starea actuală și pe caracterul obținut din banda de intrare,
dispozitivul de comandă poate rezulta 0 sau 1 stare atunci automatul se numește determinist,
altfel s e numește nedeterminist.
Pentru a înțelege cât mai ușor aceste două concepte v om explica nedeterminismul
printr -un exemplu simplu. Suntem în starea A. Dacă cu caracterul W se poate ajunge în două
stări B și C fără restricții atunci automatul este nedeterminist (AFN). Dacă se poate ajunge
într-o singură stare doar, atunci automatul este determinist (AFD) .
Figura III.1 [2] Figura III.2 [2]
14
III.1 Limbaje regulate și expresii regulate
Expresiile regulate sunt cuvinte peste un vocabular împreună cu op eratori i| (sau) ·(produs) și
*(închidere ).
Construirea unei reguli se face astfel:
(1) xsi∅sunt expresii regulate;
(2) pentru orice a∈V , cuvântul a este o expresie regulată ;
(3) daca R si S sunt expresii regulate, atunci ( R|S) (R · S ) si (R*) sunt expresii regulate;
(4) orice expresie regulată se obține prin aplicarea de un număr finit de ori a regulilor (1) -(3).
În domeniul științific, o expresie regulată (abreviat regex sau regexp) este o secvență
de caractere de text, dintre care unele sunt considerate metacaractere cu sens simbolic, altele
au sensul lor literal. Aceste concept a apărut în anii 1950 când Kleene a formalizat descrierea
unui limbaj regulat și s -a obișnuit cu programele date de Unix pentru procesarea de text.
Pentru a înțelege acest concept vom da un exemplu. O întrebuințare foarte simplă a
expresiilor regulate este în editoare text pentru a afla da că același cuvânt este scris în mai
multe moduri.De exemplu ambele cuvinte serialize și serialise sunt corecte și pentru a nu
scrie două verificări se va expune expresia seriali[s|z]e.
Un procesor de expresii regulate procesează o expresie regulată ca fiind o gramatică
expusă în limbaj formal.O dată cu varietatea limbajelor a apărut și o varietate de sintaxe și
gramatici, astfel apărând diferite sisteme de expresii regulate. Procesorul de expresii
compilează codul primit (expresiile) și folosind rezulta tul analizează textul țintă, parsândul
pentru a analiza substringuri (părți din textul țintă) astfel aflând care sunt membre a l
limbajului.
Expresiile regulate sunt foarte folositoare în domeniul informaticii încât utilizatorii
frecvenți susțin că ar trebu i se devină un standard pentru toți. La ora actuală sistemele de
expresii regulate au evoluat la punctul de două standarde pentru gramatici și sintaxă, unul
simplist “basic” si unul mai complex “extended”. Exist ă expresii regulate “moderne” care
dezvoltă drastic domeniul, care sunt asemenatoare pe mai multe platforme și aplicații.
Expresiile regulate sunt folosite și nativ în unele limbaje de programare. Unele limbaje au un
sistem direct implementat iar altele au nevoie de biblioteci suplimentare. Perl și Ruby au
expresiile regulate implementate direct în limbaj iar Java, C++ și Python necesită biblioteci
specializate [1].
Originea expresiilor regulate se află în teoria automatelor și teoria limbajelor formale,
mai exact în pattern matching (potrivirea model elor), prin studierea modelelor
computaționale (automate) și căutarea metodelor de descriere și clasificare a limbajelor
ilustrate formal. În anul 1950 matematicianul Stephen Cole Kleene a descris modelele
folosind notația sa matematică numită “seturi regu late”. Prima implementare a pattern
matching -ului a fost limbajul SNOBOL care se baza pe structuri asemănătoare cu expresiile
regulate. Expresiile regulate în sensul lor au apărut prima dată în formatul unui program când
15
Ken Thompson a construit notațiile lui Kleene folosindu -le ca un mijloc de potrivire a
modelelor în fișiere text .
Expresiile regulate specifică seturi de caractere. Se preferă definirea unor reguli decât unor
liste de caractere. Unei expresii regulate îi putem asocia un anumit limbaj peste V iar în cazul
acesta spune că expresia regulată desemnează acel limbaj. Pentru a înțelege mai ușor vom
ilustra un exemplu mic:
Pentru a recunoaște următoarele cuvinte “qr” “qwr” “qer” este necesară următoarea
expresie: q(w|e?)r
Sunt furnizate diverse opera ții și notații necesare pentru a construi o expresie regulată. Vom
ilustra câteva:
| sau
() grupare – pune în evidență ordinea de aplicare a operatorilor
? – nici una sau doar o secvență
* închid ere – de la nici unul la repetiț ii inf inite a secvenț ei respe ctive
[a-b] caractere aflate în raza respectivă [1]
III.2 Sisteme tranziționale
Un sistem tranzițional este un sistem de forma
unde:
Exemplu: ∑={𝑠0,𝑠1,𝑠2} , I={0,1}, ∑={𝑠0,𝑠1} 0 ,∑={𝑠2} 𝑓 iar funcția și relația de tranziție
este următoarea :
f 𝑠0 𝑠1 𝑠2
0 {𝑠1} {𝑠2} {𝑠0}
1 ∅ {𝑠0,𝑠1} {𝑠0,𝑠2}
𝛿={(𝑠0,𝑠1),(𝑠2,𝑠1)}
Se poate construi acum diagrama de stări completă :
16
Figura: III.3 [1]
Un sistem tranzițional explicat cel mai simplu este prin prezentarea tabelului de
tranziție. În funcție de starea actuală și caracterul introdus vom obține o nouă stare. Astfel se
parcurge tabelul până se ajunge la o stare finală.
Fiind dată o expresie regulată putem întotdeauna construi un sistem tranzițional care
recunoaște cuvântul reprezentat de expresia re spectivă. Această construcție se face în mod
recurent folosind diagrama de stări. O consecință a folosirii sistemului tranzițional este că
limbajul studiat și implicit și expresii vor fi obligatoriu regulate.
Un exemplu pentru expresia q(w|e?)r este dat de sistemul:
Figura III.4 [1]
17
III.3 Analiz a lexicală
Procesul de analiză lexicală este o fază a procesului de compilare în care se determină
unitățile lexicale (cuvinte, atomi) ale unui program sursă, se furnizează codurile interne ale
acestora și se detectează eventualele erori lexicale. Analizorul lexical mai poate efectua o
serie de operații auxiliare precum: eliminarea blank -urilor, ignorarea comentariilor, diferite
conversiuni ale unor date, completarea tabelelor compilatorului, gestiunea liniilor textului
sursă.
O unitate lexicală (Lexic = vocabular) este o secvență de caractere din textul sursă
care are o anumită valoare logică. Definiția riguroasă a unităților lexicale ale unu i limbaj de
programare particular se dau la definirea efectivă a limba jului de programare. În mod uzual
unitățile lexicale alese sunt: cuvinte cheie, identificatori, constante, opera tor și delimitatori .
Unitățile lexic ale se pot împă rți în două categorii : simple si compuse (atributive).
Orice limbaj rezonabil poate fi folosit pentru implementarea unui analizor lexical.
Unitățile lexicale se pot specifica cu ajutorul limbajelor regulate, deci folosind gramatici de
tipul 3 sau expresii regulate. Ambele specificații conduc la construirea de automate finite
echivalente care sunt ușor programabile.
Pentru a înțelege ce este un analizor lexical va trebui să punem bazele a ceea ce este
un compilator, cod sursă, aplicație finală și limbaj de programare. Acești termeni sunt fixați
anterior iar pe baza acestora vom continua să dez voltăm asu pra subiectului și vom folosi unde
este necesar câteva exemple .
Unitățile pot fi împărțite în două categorii din punct de vedere al analizei lexicale și a
modului de prelucrare:
1) Unități lexicale simple care nu conțin atribute suplimentare, de exemplu: cuvinte
cheie și operatori.
2) Unități lexicale compuse care conțin atribute suplimentare, de exemplu
identificatori și constante. Atributele sunt informații specifice precum tipul identificatorului
sau al constantei (intreag, real, etc.). Este ob ligatorie specificarea tipului unei date încă din
începutul programului deoarece structura programului obiect sau reprezentare a internă
depinde de acest tip.
Reprezentarea internă a unităților lexicale se face în funcție de categoria lor. Cele
simple sunt reprezentate printr -un cod specific (de exemplu număr întreg). Unitățile lexicale
compuse se reprezintă prin cod și informația (de natură semantică) corespunzătoare. În acest
caz unitatea lexicală se reprezintă intern printr -un cod urmat de o referință înt r-un table [2] .
Informația conținută de tabel ar putea fi pentru constante: cod, tip, valoare, iar pentru
identificatori: cod, tip, indicator de inițializare.
18
De obicei compilatoarele utilizează tabele pentru stocarea atributelor (tabel de
constante, tabel de variabile, tabel de etichete, etc.).
Este posibil ca o clasă de unități lexicale simple să se reprezinte printr -un cod unic și
un atribut pentru distingerea lor în cadrul clasei. Lista unităților lexicale și defi niția riguroasă
a acestora se dă la proiectarea compilatorului.
Analizorul primește textul sursă și produce unitățile lexicale în codificare internă.
Pentru o înțelegere mai ușoară vom lua un exemplu simplu și vom arăta unitățile recunoscute
în codificare internă, dar le vom expun e într -o manieră mai simplă .
{
if(a>10)
a=a/10;
return a;
}
Unele unități lexicale se repeta în cod dar în tabel le vom scrie doar o dată .
Cod Unitate lexicala Atribut
{ DESCHIDERE
ACOLADE
if CUVANT CHEIE if
( DESCHIDERE
PARANTEZE
a IDENTIFICATOR referinta
> OPERATOR
10 CONSTANTA referinta
) INCHIDERE
PARANTEZE
= OPERATOR
/ OPERATOR
return CUVANT CHEIE return
; OPERATOR
} INCHIDERE ACOLADE
Tabelul asociat unitatilor lexicale [xy][2].
Codul nostru va produce deci următorul șir de unităț i lexicale:
[DESCHIDERE ACOLADA]
[CUVANT CHEIE,if][DESCHIDERE PARANTEZE][IDENTIFICATOR,a][OPERATOR]
[CONSTANTA,10][INCHIDERE PARANTEZE]
[IDENTIFICATOR,a][OPERATOR]
[IDENTIFICATOR,a][OPERATOR][CONSTANTA,10][OPERATOR]
[CUVANT CHEIE,return][IDENTIFICATOR,a][OP ERATOR]
[INCHIDERE ACOLADE]
19
În lista reprezentărilor interne de mai sus, identificatorii, constantele și cuvintele cheie
au atribute suplimentare care sunt de fapt niște adrese relative sau referințe în tabel.
Majoritate limbajelor de programare conțin și secvențe de text care nu sunt
considerate unități lexicale,dar au acțiuni specifice, de exemplu comentariile și directivele de
preprocesare. Trebuie menționat că înaintea analizei lexicale prepriu -zisă, se preproceseazaă
textul unde se tratează de obicei comentariile, directivele de preprocesare și secvențe care
depinde strict de limbaj și de compilator. Specificarea unităților lexicale, adică definirea
riguroasă se face de către proiectantul limbajului. O posibilitate de descriere este limbajul
natural:
“Un identificator este o secvenț ă nelimitată de litere Java și cifre Java,dar prima
trebuie să fie o literă Java. O literă Java conține caraterele ASCII pentru litere mari și litere
mici precum și caracterele _ (liniuța jos) și $ (semnul dolar) din motive i storice. Cifrele Java
includ caracterele ASCII de la 0 pana la 9”
Un analizor lexical poate fi implementat în orice limbaj rezonabil. Analizorul nostru
se bazează pe expresii regulate luate de pe site -ul oficial al limbajului Java, astfel realizarea
efectivă se va rezuma la simularea funcționării unui automat finit.
Varianta de programare aleasă atașează o secvență de program la fiecare stare a
automatului. Dacă starea nu este stare finală atunci secvența citește următorul caracter din
textul sursă și găsește următorul arc din diagrama de stări. Depinzând de rezultatul căutării se
transferă controlul altei stări sau se returnează eșec (posibilă eroare lexicală).Dacă starea este
finală atunci se apelează secvența de returnare a codului unității lexical e și instalare a unității
lexicale în tabelelul compilatorului.
Pentru simplificarea implementării se caută următoarea unitate lexicală prin
încercarea succesivă a diagramelor corespunzătoare fiecărei unități lexicale într -o ordine
prestabilită. Eroarea lex icală se semnalează doar atunci când toate încercările se încheie cu
eșec. De obicei textul sursă conține și secvențe ce se pot descrie tot cu ajutorul expresiilor
regulate, dar care nu sunt unități lexicale (de exemplu comentariile). Aceste sec vențe nu vo r
genera cod lexical [2].
Pentru a evita apariția unor caractere necunoscute în textul sursă se consideră și
limbajul ce constă din toate simbolurile ASCII. Astfel, indiferent de unde începe analiza
textului, programul de analiză lexicală găsește o potrivi re cu o descriere, specificația fiind
completă.
Există posibilitatea ca mai multe secvențe cu aceeași origine să corespundă la diferite
descrieri ale unităților lexicale sau să fie comune p ână la un anumit punct. Se consideră
unitate lexicală cel mai lung șir ce se potrivește unei descrieri (longest match rule). Dacă sunt
două reguli care se potrivesc la același șir de lungime maximă atunci se consideră o prioritate
asupra descrierilor (rule priority).
20
De exemplu, în cazul următor ,i if if8 , unita țile lexica le delimitate vor f ii identificator,
ifcuvânt cheie, if8identificator.
Regulă de prioritate se aplică pentru potrivirea lui if cu identificator și cuvânt cheie,
cuvânt cheie fiind mai prioritar. Pentru depistarea celei mai lungi potriviri, din punct de
vedere al programării analizorului, vom fi foarte atenți la alegerea separatorilor unităților
lexicale pentru a fi siguri că se ajunge la stare finală doar în punctul dorit .
Cea mai costisitoare operațiun e (ca timp de procesare) din analiza lexicală este
ignorarea comentariil or. Primele generatoare automate de analizoare lexicale și sintactice au
apărut în anii ’70 și au fost incluse în sistemul de operare Unix.
Etapele realizării unui analizor lexical:
ETAPA1: Descrierea unităților lexicale cu ajutorul e xpresiilor regulate
Definițiile riguroase a unităților lexicale sunt luate de pe sursă oficială și manual se vor
construi expresii regulate pentru fiecare caz întâlnit.
ETAPA2: Corespunzător expresiilor obținem automatele finite deterministe și
construim sistemul tranzițional
ETAPA3: Ultima etapă presupune programare a efectivă a analizatorului
Automatul finit obținut are stările finale asociate cu clasele de cuvinte recunoscute . Se
asociază acțiuni stărilor finale, corespunzător definițiilor unităților lex icale (de exemplu
pentru constante numerice se generează reprezentarea internă și se memorează în tabelul
compilatorului). [2]
III.4 Analizorul lexical pentru limbajul Java
ETAPA 1
Conform celor menționate mai sus vom începe să definim unitățile lexicale folosind
expresii regulate .
Programele Java sunt scrise folosind setul de caractere Unicode, dar acestea sunt
convertite în caractere ASCII. Caracterele Unicode sunt cea mai mare convenție de notație a
literelor și cifrelor la nivel global. Deoarece sunt m ulte limbi existente, fiecare cu setul lor de
caractere acest Unicode le poate reprezenta pe toate indiferent de program, platformă sau
limbă. Caracterele ASCII (AMERICAN STANDARD CODE for INFORMATION
INTERCHANGE) reprezintă de fapt primele 128 de caracter e Unicode UTF -16. Acestea sunt
suficiente pentru a analiza un cod sursă Java cu excepția unor mici ocazii .
Codul sursă al programului nostru se împarte pe rânduri și apoi este analizat rând cu
rând. Se poate face acest pas din cauza prezenței caracterului ASCII “terminator de sir”.
21
Acest caracter putem spune ca este mai special. În funcție de sistemul de operare avem
situații în care dăm doar de caracterul ‘\n’ (newline) sau ‘ \r’ (linereturn), sau chiar de ambele
caractere una dupa alta (‘ \r’‘\n’) care sunt luate ca fiind doar un terminator de linie și nu
două . Terminator de sir va avea deci urmatoarea expresie terminator={‘ \n’| ‘\r’| ‘\r \n’} .
În acest punct vom întâlni în codul de analizat următoarele caractere:
I. spații albe
II. comentarii
III. tokenuri care se împart în următoarele :
1. identificator
2. cuvâ nt cheie
3. literal
4. separator
5. operator
Spațiile albe și comentariile sunt ignorate de analizorul lexical, dar noi le vom defini pentru a
le putea recunoaște. Tot ceea ce nu este spațiu alb sau comentariu este un token [19].
Un token este un simbol terminal al gramaticii sintactice în java. Spațiile albe și comentariile
mai servesc ca și separatori pentru token -uri care dacă sunt adiacente se analizează diferit și
se iau ca fiind unul singur. Un exemplu este “x – = 3” rezul tă în {identificator, operator,
operator,literal} iar “x -= 3” rezultă în {identificator,operator,literal}, [7].
I. Spatiile albe sunt caracterele ASCII care reprezinta spatiu, tab orizontal, caracterul
formfeed si terminatoarele de linie:
‘ ’ – spatiu
\t – tab orizontal
\f – formfeed
\n , \r , \r\n – terminatorii de linie
Expresia reg ula este ‘ ’| \t|\f|\n|\r dar luând în calcul și împărțirea pe râ nduri le anterioare este
suficient ă doar expresia ‘ ’| \t|\f .
II. Comentariile sunt de doua tipuri, iar din punct de vedere al priorității se aplică regula de
primul venit primul servit:
/*…*/ comentariu tradiț ional
// comentariu de linie
Expresia regulată pentru comentarii devine /* (orice caract er)* */ | // (orice caracter în
afară de terminator de lini e)* ( \n|\r)
III.Tokenurile se împart î n mai multe categorii. Primul este identifica tor dar pentru a ințelege
mai uș or aces t concept de identificator vom începe cu cuvinte cheie ș i cu doi literali [5].
22
2.Limbajul Java are 50 de cuvinte cheie. Un cuvânt chei e este o secvență de caracte re
care nu poate fi folosit ca ș i identificator. Acestea sunt:
abstract continue for new switch
assert default if package synchronized
boolean do goto private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while
Expresia regulă a cuvintelor cheie este de forma abstract|continue|…|while .
Cuvintele “const” si “goto” sunt considerate cuvinte cheie deși nu sunt folosite la ora
actuală. Cuvintele “true” si “false” par a fi cuvinte cheie dar defapt sunt literali booleeni.În
mod asemănător, cuvântul null nu este cuvânt cheie ci este literal nul. Acești literali îi vom
explica în detaliu mai jos [11].
Un identificator este o secvență nelimitate de litere Java și numere Java, dar obligatoriu
trebuie să înceapă cu o literă Java. Un identificator nu are voie să fie cuvânt cheie, literal
boolean sau literal nul [6 ].
Din cauza acestei definiții a trebuit să explicăm cuvintele cheie, literalii booleeni și literalul
nul.
Literele Java sunt caracterele [a -z] , [A -Z] ș i din motive istorice semnul _
(underscore) ș i $ (semnul dolar). Cifrele Java sunt caracterele [0 -9]
Expresia regulată pentru identificator este: ([A -Z]|[a -z]|_|$)([A -Z]|[a -z]|_|$|[0 -9])*
3. Un literal este reprezentarea în cod sursă a unui tip de dată primitiv. Literalii se
împart în 6 categorii :
a. literal î ntreg
b. literal î n virgula flotant ă
c. literal boolean
d. literal caracter
e. literal ș ir de caractere (string)
f. literal nul [8].
a.Un literal întreg poate să reprezinte un numar întreg î n bazele 10,2,8,16. Toți literalii întregi
pot să aibă la final litera “L” sau “l” (L mic), deci formula urmatoare le vine ataș ată la final
(L|l)? :
23
În baza 10 numerele sunt de forma: 0|( [1 -9](_|[0 -9])*[0 -9])?
În baza 2 numerele sunt de forma: 0(b|B)[0 -1]((_|[0 -1])*[0 -1])?
În baza 8 numerele sunt de forma: 0[0 -7]((_|[0 -7])*[0 -7])?
În baza 16 numerele sunt de forma: 0(x|X)[0 -9,a-f,A-F]((_|[0 -9,a-f,A-F])*[0 -9,a-f,A-F])?[13].
b.Un literal în virgulă flotantă se poate exprima în baza 10 sau î n baza 16. Expresiile regulate
care l e corespund sunt:
În baza 10: 0|( [1 -9](_|[0 -9])*[0 -9])? . ( [0 -9](_|[0 -9])*[0 -9])? e (+| -)?( [0 -9](_|[0 -9])*[0 -9])?
(f|F|D|d)?
În baza 16: 0(x|X)[0 -9,a-f,A-F]((_|[0 -9,a-f,A-F])*[0 -9,a-f,A-F])? . [0 -9,a-f,A-F]((_|[0 -9,a-f,A-
F])*[0 -9,a-f,A-F])? e [0 -9,a-f,A-F]((_|[0 -9,a-f,A-F])*[0 -9,a-f,A-F])? (p|P)? [14].
c. Literalul boolean are doar 2 valori posibile “true” sau “false”. Expresia regulată va fi
true|false [15].
d. Un literal caracter este ilustrat printr -un caracter sau o secventa unicode de iesire
incapsulata in simbolul apostrof ‘. Expresia regulată este:
’ (caracter) | \((b|t|n|f|r|”|’| \)?|[0-7|([0 -7][0-7])|([0 -3][0-7][0-7])][16].
e.Un literal string acceptă orice input chiar ș i vid. Se deschide și se inchide cu ”.Trebuie să
fim atenț i doar ca un literal string se poate î ntinde doar pe un rand . Expresia reg ulată este
“(orice)” [17].
f. Literalul nul are o valoare doar care este referință nulă ilustrată prin cuvantul null. Expresia
regulata este: null [18].
4.Exist ă 9 separatori :
{ } [ ] ( ) ; , .
Expresia regulata este: {|…|. [9].
5.Operatorii sunt urmatorii î n numar de 37:
= >< ! ~ ? :
== <= >= != && || ++ –
+ – * / & | ^ % <<>>>>>
+= -= *= /= &= |= ^= %= <<= >>= >>>= [10].
24
Expresia regulată o vom scrie pe anumite categorii în funcție de caracterul cu care ne intră în
sistem, vom întâlni câteva situații :
= =?
<<? =?
>>? >? =?
! =?
& (&|=)?
| (‘|’|=)?
+ (+|=)?
– (-|=)?
* =?
/ =?
^ =?
% =?
la care mai adaug ăm |~|?|: [10]
ETAPA 2
Folosind ex presiile regulate obținute vom construi sistemul nostru tranzițional necesar pentru
recunoașterea cuvintelor.
Menționăm că anterior implementării efective a acestui pas programul nostru va
împărți textul sursă pe linii. Doar după acest pas analizorul nostru lexical va intra în acțiune.
Vom folosi o varia bilă pentru a ține minte linia și coloana pe care ne aflăm în textul nostru
astfel având libertatea de deplasare înapoi acolo unde este caz ul. Pentru a putea crea tabelul
compilatorului este suficient să știm doar tipul unităților noastre lexicale.
Starea de început este starea 0, stările finale sunt marcate cu cerc dublu încercuit .
25
[III.5][1]
Starea A reprezintă comentarii
Starea B reprezintă cuvinte cheie
Starea C reprezintă identificatori
Starea D reprezintă literali
Starea E reprezintă separatori
Starea F reprezintă operatori
Starea G reprezintă spaț ii albe
Alegerea stării spre care se deplasează automatul nostru va depinde strict de primul
caracter. O primă observație este că automatul nostru se va bloca dacă se îndreaptă spre o
stare finală dar p e parcurs nu poate să treacă într -o altă stare. Pentru a r ezolva această
problemă vom crea o funcție care va obliga automatul să itereze toate drumurile pentru a
26
ajunge la stări finale. Când se ajunge la stare a finală se va returna un rezultat și apoi se v a
reseta sistemul la starea 0, acesta fiind pregătit să analizeze următoarea unitate lexicală .
ETAPA 3
Această etapă este realizată în mod practic mai jos cu explicații de rigoare, decizii de
implementare și resurse folosite.
În situația cuvintelor cheie se citește cuvântul și se compară cu cel din memorie.Pentru a
simplifica acest proces se încarcă în memoria unui tabel de dispersie (hashtable) toate
cuvintele cheie și se compară cu acestea.
În situația separatorilor procesul de identificare a unității le xicale este simplu. Se compară
caracterul introdus cu toate separatoarele și se alege cel bun.
În situația operatorilor este necesar să introducem niște stări intermediare pentru a facilita
stările terminale cu o potențială continuare.
In cazul caracterulu i ‘>’ acesta este t erminal daca nu este urmat de încă un caracter ‘>’ astfel
obținându -se operatorul ‘>>’ și nu doi operatori ‘>’.
Vom dezvolta aceste situații mai jos în explicarea aplicației pentru că este mai ușor de înțeles
din implementarea practică.
Tabelul de dispersie este o clasă Java din pachetul util folosită pentru a mapa chei unor
valori. Orice obiect nenul poate fi folosit ca și cheie și ca și valoare. În implementarea aceasta
s-au ales cheile de tip string și va lorile de un tip definit de noi [1].
27
Capitolul IV Descrierea aplicatiei software
Prin software , soft sau rare ori și „logicial” se înțelege un system de programe
pentru calculatoare incluzând procedurile lor de aplicare, sistem furnizat odată cu
calculatorul respectiv sau creat ulterior de către utilizator sau și cumpărat din comerț. Prin
contrast, cuvântul hardware desemnează partea fizică a calculatorului sau a sistemului
informatic respectiv. În general, pentru a funcționa, un sistem informatic are nevoie de
ambele componente, în plus și de datele care trebuiesc prelucra te. Uneori și aceste date sunt
considerate a face parte din software.
Componenta software poate include toată gama de produse de programare, uzual formată
din sistem de operare , drivere și programe de aplicație. În anumite cazuri speciale părți din
software se înglobează din construcție în hardware – prin folosirea de circuite integrate
preprogramate.
În unele domenii, prin software se înțeleg în primul rând datele cu care lucrează aparatele sau
calculatoarele, cum ar fi imaginile digitalizate, sunete și piese muzicale, jocurile pentru
calcu lator, filme digitalizate, clipuri video și multe alte date asemănătoare. În caz extrem,
până și purtătorii fizici de date sau "mediile" sunt considerate a fi "software", ca de exemplu
discurile optice de tip CD și DVD , casetele video VHS și miniDV , casetele audio [22].
IV.1. P roces software
l Un set de activități care au ca și obiectiv dezvoltare și evoluția software -ului.
l Activitățile generice în toate procesele software sunt :
• Specificație – ceea ce trebuie ca sistemul să îndeplinească și constrângerile de
dezvoltare
• Dezvoltare – producerea sistemului software
• Validare – verificarea modului în care aplicația îndeplinește ceea ce clientul
dorește
• Evoluție – modificarea software -ului în concordanță
cu modificarea cerințelor [20].
IV.2 Ap licațiile software
Aplicațiile software sunt acele programe informatice care necesită un sistem de operare
pentru a putea fi utilizate. Aplicațiile software nu pot accesa direct componentele hardware ,
ci doar prin intermediul funcțiilor puse la dispoziție de sistemul de operare .
Aplicațiile software pot fi programe informatice de calcul tabelar sau editare de texte (Office,
Open Office), de editare video, de r edare multimedia, de navigare pe Internet (Internet
explorer, Chrome, Firefox), de control al proceselor tehnologice ( SCADA ).
Aplicațiile software sunt create în diverse limbaje de programare cu ajutorul mediilor de
dezvoltare – IDE (Integrated Development Environment) puse la dispoziție gratuit sau
contracost.
28
De exemplu, pentru programarea aplicațiilor software pentru sistemul de operare Android se
utilizează limbajul de programare Java [24].
Diag rama de clase a programului
Figura IV.1
Acum vom exp lica clasele una câte una , iar la final ilustrăm un test și rezultatul obținut .
Clasa Run
Clasa principală este clasa Run, aceasta are funcția main. Ca un prim pas se
inițializează resursele necesare prin apelarea constructorilor SymbolTable și
LexicalAnalyzer. Se citește numele fișierului care va fi analizat lexical într -o variabilă apoi se
apelează funcția de fixare a fișierului din LexicalAnalyzer. Se reapelează funcția de găsire a
unităților lexicale și de afișare a acestora (afișarea se face în clasa LexicalAnalyzer) până la
obținerea unității lexicale care marchează finalul fișierului ilustrată prin tipul FINISH [23].
Rezultatul programului va fi o afișare a valorii semantice a unitățile lexicale detectate
urmat de tipul unității lexicale .
29
Clasa Symbol
Este folosită ca și template pentru a ne crea propriul nostru tip de dată.Acesta va ține minte în
ordine tipul unității lexicale,pe ce linie se află, pe ce po ziție se află,valoarea semantică a
unității lexicale.
Constructorul este explicit și construiește un obiect de tipul declarat [23].
Clasa SymbolTable
Anterior acestei clase se află o enumerare TokenType. În acest mod declarăm toate tipurile de
unități lexi cale posibile:
RESERVEDWORD – cuvâ nt rezervat
SEPARATOR – separator
OPERATOR – operator
ID – identificator
BOOLEANLITERAL – literal boolean
NULLLITERAL – literal nul
STRINGLITERAL – literal ș ir de caractere (string)
CHARACTERLITERAL – literal caracter
INTE GERLITERAL – literal î ntreg
FLOATLITERAL – literal în virgulă lotanta
WHITESPACE – spațiu alb
FINISH – tipul returnat când se termină de analizat textul sursă
Această clasă are declarat un tabel de dispersie. În constructor sunt scrise manual
toate cuvintele cheie, separatorii, operatorii, literalii booleeni și literalul nul care sunt
introduse apoi în tabel.Cheia introdusă în tabel este un string care este identic cu valoarea
semantică a simbolului.Această strategie ne simplifică procesul de verific are a existenței
cheilor în tabel astfel depistând datele introduse mult mai ușor.
Sunt declarate și funcțiile :
get – returnează obiectul de tip simbol din tabel a cărui cheie se potrivește cu
parametrul nostru
constainsKey – verifică dacă vreo cheie din tabel este la fel cu parametrul nostru
put – se introduce în tabelul de dispersie unitatea lexicală primită ca parametru
Clasa LexicalAnalyzer
Constructorul clasei atribuie unei variabile tabelul de dispersie .
Functia inputFileName fixează fișierul care es te de prelucrat .
30
Funcția nextChar returnează următorul caracter de citit. La fiecare apelare
incrementează indicele de poziție pe linie. Este definit comportamentul pentru cazul în care
s-a terminat linia dar nu și fișierul, s-a terminat fișierul și compo rtamentul normal de
returnare a caracterului de procesat.
Funcția retract decrementează indicele de poziție pe linie; efectiv ne mută înapoi cu
un caracter.
Funcția fail este importantă pentru că face legătura între majoritatea stărilor din automatul
nostru. Această funcție face trecerea de la o stare ce corespunde începutul une i unități lexicale
spre altă stare. De exemplu în program cazurile de la 0 până la 15 sunt ded icate operatorilor
și sunt în esență stări terminale. Dacă unitatea lexicală de analizat nu începe cu simbol de
operator ci cu literele corespunzătoare unui cuvânt cheie sau identificator, atunci seria de stări
din program este de la 20 la 21.
Funcția next Token reprezintă automatul de stări. Se citește primul caracter și se fixează prima
stare să fie starea 0. Într -un ciclu infinit se procesează condițiile stărilor și acolo unde este
cazul instrucțiunile stărilor. Acest ciclu rulează până se returnează o va loare de tip
TokenType.
Stările de la 0 la 15 sunt dedicate operatorilor.
Stările 13 si 18 sunt dedicate comentariilor; starea 13 este o stare intermediară în acest caz.
Stările 20 si 21 sunt dedicate cuvintelor cheie, identificatorilor ș i a literalilor di n tabelul de
dispersie. .
Stărea 30 este dedicata separatorilor .
Stările 40 si 41 sunt dedicate literalilor de tip string.
Stările 50 si 51 sunt dedicate literalilor de tip caracter.
Stările de la 60 î n sus sunt dedicate numerelor.
Starea 99 este dedicată spațiilor albe.
Starea 100 se execută când se ajunge la finalul bandei de intrare.
Functia PrintToken afiseaza rezultatul final al programului ; tipul tokenului ș i valoarea
semantica.
Un ex emplu de rezultat pentru textul intodus următor este:
public static void main(String args[])
{
int a=15;
int b=0B1___01_1_0;
String b="Serbus";
char c='a'+'b';
// aiurea:
System./*sau nu*/out.println("Hello World!");
}
31
Type in the file name you wish to lexically analyze:
D:/test.txt
public –RESERVEDWORD
static –RESERVEDWORD
void–RESERVEDWORD
main –ID
(–SEPARATOR
String –ID
args–ID
[–SEPARATOR
]–SEPARATOR
)–SEPARATOR
{–SEPARATOR
int–RESERVEDWORD
a–ID
=–OPERATOR
15–INTEGERLITERAL
;–SEPARATOR
int–RESERVEDWORD
b–ID
=–OPERATOR
0B1___01_1_0 –INTEGERLITERAL
;–SEPARATOR
String –ID
b–ID
=–OPERATOR
Serbus –STRINGLITERAL
;–SEPARATOR
char–RESERVEDWORD
c–ID
=–OPERATOR
a–CHARACTERLITERAL
+–OPERATOR
b–CHARACTERLITERAL
;–SEPARATOR
System –ID
.–SEPARATOR
out–ID
.–SEPARATOR
println –ID
(–SEPARATOR
Hello World! –STRINGLITERAL
)–SEPARATOR
;–SEPARATOR
}–SEPARATOR
32
Capitolul V Concluzii
Java este un limb aj foarte ultilizat în multiple domenii deoare ce are intenția Write Once Run
Anywhere . Prin aceasta se observă faptul că , o dată scris se poate ultiliza pe orice platformă
sau gadget care suportă limbajul Java.
Motivul alegerii acestei teme este ca, cel mai important pas al unui compilator este Analiza
Lexicală . Fară această etapă nu ar putea funcționa nici un compilator atât pentru limbajul
Java cât și pentru alte limbaje de programare .
Se pot crea compilatoare proprii pentru limbaje de programare și gramatici proprii.
Analiza lexicală este importantă pentru verificarea c orectitudinii limbajului nostr u și pentru
buna funcționare a oricărui compilator.
Un limbaj propriu este foarte folositor pentru că poate fi specializat. Orice programator poate
să formuleze și să programeze propriul lui limbaj de aceea am ales limbajul Java ., acesta fiind
foarte ultil izat în multiple domenii.
Expresiile regulate sunt o metodă ușoară de definire a unităților lexical e acestea fiind cel mai
des ultilizate în limbajul Java.
Un nou concept de cercetat și implementat ar putea fi realizarea unui analizor lexical global,
adică unul care s ă recunoasc ă toate limbajele de programare. Acest lucru ar fi necesar din
comoditatea de a nu instala mai multe compilatoare diferite și pentru a facilita stu denții
facultății de Inginerie Electrică și Tehnologia Info rmației respectiv și a alt or facultați cu
profil de informatică . Acest lucru mărind productivitatea și percepția studenților pentru a
înțelege și a lucra mai bine si mult mai efficient în mediile de programare, dezvoltându -se
astfel mult mai rapid pe plan professional și academic .
33
DECLARAȚIE DE AUTENTICITATE
A
LUCRĂRII DE FINALIZARE A STUDIILOR
Titlul lucrării ANALIZOR LEXICAL ÎN JAVA
Autorul lucrării LUKA CĂLIN -ANDREI
Lucrarea de finalizare a studiilor este elaborată în vederea susținerii examenului de
finalizare a studiilor organiz at de către Facultatea de Inginerie Electrică și Tehnologia
Informației din cadrul Universității din Oradea, sesiunea Septembrie a anului universitar
2018 .
Prin prezenta, subsemna tul (nume, prenume, CNP) LUKA CĂLIN -ANDREI,
1951130055066.
declar pe proprie răspundere că această lucrare a fost scrisă de către mine, fără nici un ajutor
neautorizat și că nici o parte a lucrării nu conține aplicații sau studii de caz publicate de alți
autori.
Declar, de asemenea, că în lucrare nu există idei, tabele, grafice, hărți sau alte surse
folosite fără respectarea legii române și a convențiilor internaționale privind drepturile de
autor.
Oradea,
Data Semnătura
34
Bibliografie
[1]Mircea Drăgan, Limbaje Formale și Tehnici de Compilare 19, April 2006 .
[2]Mircea Drăgan Ștefan Mărușter, Limbaje formale 23, February 2005 .
[3]http://en.wikipedia.org/wiki/Java_language Consultat la 1 4.03.2018 , 1 5.03.2018 și
16.03.2018 .
[4]http://en.wikipedia.org/wiki/Regular_expression Consultat la 10.04.2018, 11.04.2018,
12.04.2018 și 19.04.2018 .
[5]http://docs.oracle.com/javase/specs/jls/se7/html/index.html Consultat la 02.05.2018,
03.05.201 8, 04.05.2018, 05.05.2018.
[6] https://docs.oracle.com/javase/spe cs/jls/se7/html/jls -1.html#jls -1.1 Consultat la
02.05.2018.
[7] https://docs.oracle.com/javase/specs/jls/se7/html/jls -3.html Consultat la 02.05.2018.
[8] https://docs.oracle.com/javase/specs/jls/se7/html/jls -3.html#jls -3.10.1 Consultat la
03.05.2018.
[9] https://docs.oracle.com/javase/specs/jls/se7/html/jls -3.html#jls -3.11 Consultat la
04.05.2018.
[10] https://docs.oracle.com/javase/spe cs/jls/se7/html/jls -3.html#jls -3.12 Consultat la
04.05.2018.
[11] https://docs.oracle.com/javase/specs/jls/se7/html/jls -3.html#jls -3.9 Consultat la
04.05.2018.
[12] https://docs.oracle.com/javase/specs/jls/se7/html/jls -3.html#jls -3.10 Consultat la
04.05.2018.
[13] https://docs.oracle.com/javase/specs/jls/se7/html/jls -3.html#jls -3.10.2 Consultat la
04.05.2018
[14] https://docs.oracle.com/javase/specs/jls/se7/html/jls -3.html#jls -3.10.3 Consultat la
05.05.2018.
[15] https://docs.oracle.com/javase/s pecs/jls/se7/html/jls -3.html#jls -3.10.4 Consultat la
05.05.2018.
[16] https://docs.oracle.com/javase/specs/jls/se7/html/jls -3.html#jls -3.10.5 Consultat la
06.05.2018.
[17] https://docs.oracle.com/javase/specs/jls/se7/html/jls -3.html#jls -3.10.6 Consultat la
06.05.2018.
35
[18] https://docs.oracle.com/javase/specs/jls/se7/html/jls -3.html#jls -3.10.7 Consultat la
06.05.2018.
[19] https://docs.oracle.com/javase/specs/jls/se7/html/jls -3.html#jls -3.5 Consultat la
07.05.2018.
[20] Notițe curs “Ingineria Program ării”, Profesor : Novac Constantin Ovidiu.
[21] https://en.wikipedia.org/wi ki/Gaikai Consultat la 12.05.2018.
[22] https://ro.wikipedia.org/wiki/Software Consultat la 14.05.2018.
[23]D. Zmaranda, Programare orientată pe obiecte cu aplicații în Visual C++, Editura
Universității din Oradea, ISBN 973 -613-681-7, 2004
[24] D. Zmaranda, Elemente de programare orientată pe obiecte utilizând limbajul C++,
Editura Universității din Oradea, ISBN 973 -613-013-4, 2001
[25] D. Zmaranda, Elemente de programare orientată pe obiecte în limbajul C# – Editura
Universității din Oradea, ISBN 978 -973-759-522-5, 2008
[26] https://despretot.info/ software -definitie/ Consultat la 22 .05.2018
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Forma de Învățământ : zi [632252] (ID: 632252)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
