Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
lfa:2022:proiect_etapa2 [2022/11/16 20:15]
alex.ilie created
lfa:2022:proiect_etapa2 [2022/11/27 13:43] (current)
alex.ilie
Line 1: Line 1:
 Schelet si checker pentru fiecare limbaj: Schelet si checker pentru fiecare limbaj:
-  * TBD +  * {{lfa:​2022:​skel-py-e2.zip}} 
-  * TBD+  * {{lfa:​2022:​skel-scala-e2.zip}}
  
-Deadline etapa 2: TBD+Deadline etapa 2: 12 decembrie ora 23:59
  
 ====== Proiect ====== ====== Proiect ======
  
-Proiectul consta in implementarea unui lexer in python sau scala.+/* Proiectul consta in implementarea unui lexer in python sau scala. ​*/
  
 +/*
 ===== Ce este un lexer? ==== ===== Ce este un lexer? ====
 Un lexer este un program care imparte un sir de caractere in subsiruri numite //lexeme//, fiecare dintre acestea fiind clasificat ca un //token//, pe baza unei specificatii. Un lexer este un program care imparte un sir de caractere in subsiruri numite //lexeme//, fiecare dintre acestea fiind clasificat ca un //token//, pe baza unei specificatii.
Line 30: Line 31:
 Lexer-ul are ca output o lista de forma : ''​[(lexema1,​ TOKEN_LEXEMA_1),​ (lexema2, TOKEN_LEXEMA_2),​ ...]'',​ unde ''​TOKEN_LEXEMA_N''​ este numele token-ului asociat lexemei n, pe baza specificatiei. Lexer-ul are ca output o lista de forma : ''​[(lexema1,​ TOKEN_LEXEMA_1),​ (lexema2, TOKEN_LEXEMA_2),​ ...]'',​ unde ''​TOKEN_LEXEMA_N''​ este numele token-ului asociat lexemei n, pe baza specificatiei.
  
-===== Etapa 2 ===== +*/
-Datorita dificultatii lucrului direct cu regex-uri pentru verificarea apartenentei unui cuvant in limbaj, lexerele reale trec prin cateva etape intermediare inainte de inceperea analizei textului.+
  
-**Etapa 2** consta in conversia ​unui Regex in forma prenex+===== Etapa 2 ===== 
 +/* 
 +Datorita dificultatii lucrului direct cu regex-uri pentru verificarea apartenentei ​unui cuvant ​in limbaj, lexerele reale trec prin cateva etape intermediare inainte de inceperea analizei textului*/
  
-Forma prenex ​a fost prezentata ​in etapa precedentaIn aceasta etapa ne vom ocupa de parsarea Regexurilor scrise ​in forma prezentata la curs.+**Etapa 2** consta in **parsarea** unei expresii regulate (regex) scrisa in maniera conventionala,​ si conversia acesteia in forma prenex. ((Forma prenex, intermediara ​in proiectul nostru intre cea conventionala si arborele de parsare (AST) construit de programul vostru, nu este standard pentru o astfel de implementareDe altfel, ea nici nu este folosita in mod curent. Insa cum limbajul expresiilor regulate valide este independent de context (din cauza parantezelor),​ este dificil de implementat parsarea regexurilor fara cunostinte despre gramatici si in special APD-uri. Tocmai de aceea aceasta etapa de parsare, care in mod natural ar fi prima, a fost amanata. Acest lucru a fost posibil introducand ​forma prenex care nu contine paranteze.))
  
 ==== Forma standard a expresiilor regulate ==== ==== Forma standard a expresiilor regulate ====
 +
 +Forma standard a regex-urilor poate fi descrisa in forma [[https://​en.wikipedia.org/​wiki/​Backus%E2%80%93Naur_form | BNF]] astfel:
 +<​code>​
 +<​regex>​ ::= <​regex><​regex>​ | 
 +            <​regex>​ '​|'​ <​regex>​ | 
 +            <​regex>'​*'​ | <​regex>'​+'​ | <​regex>'?'​ | 
 +            '​('​ <​regex>​ '​)'​ | 
 +            "​[A-Z]"​ |
 +            "​[a-z]"​ |
 +            "​[0-9]"​ |
 +            "​eps"​ | <​character> ​
 +</​code>​
 +
 +In descrierea de mai sus, elementele dintre parantezele angulare <> sunt **non-terminali** care trebuie generati, caracterele sunt intotdeauna plasate intre ghilimele simple, iar sirurile intre ghilimele duble.
 +
 +''<​character>''​ se refera la orice caracter obisnuit care nu face parte din caractele de //control// (precum ''​*''​ sau ''​|''​),​ sau la orice sir de lungime trei de forma ''​ '​c'​ '',​ unde ''​c''​ poate fi orice caracter inclusiv de control.
 +
 +"​eps"​ reprezinta caracterul Epsilon.
 +
 +===== Preprocesarea Regex-urilor =====
 +
 +In descrierea de mai sus, pe langa caracterele alfa-numerice si operatiile de baza star, concat si union, veti gasi si:
 +  - doua operatii noi: 
 +    - plus ''​+''​ - expresia asupra careia este aplicat apare de 1 data sau mai multe ori.
 +    - semnul intrebarii ''?''​ - expresia asupra careia este aplicat apare o data sau niciodata.
 +  - 3 syntactic sugars:
 +    - ''​[a-z]''​ - orice caracter litera mica din alfabetul englez
 +    - ''​[A-Z]''​ - orice caracter litera mare din alfabetul englez
 +    - ''​[0-9]''​ - orice cifra
 +
 +Aceste operatii noi nu contribuie la expresivitatea regex-urilor,​ insa ajuta foarte mult utilizatorii sa scrie regex-uri compacte si usor de citit. In implementarea voastra, este recomandat sa //​preprocesati//​ regexurile, adica sa eliminati operatorii nou-introdusi si sa ii inlocuiti cu cei standard. Operatorii standard sunt cei prezentati la curs (concatenare,​ reuniune si star).
 +
 +Spre exemplu: $math[e+ = ee*] sau $ [0-9] = 0 \cup 1 \cup 2 \cup \ldots \cup 9 $.
 +
 +In felul acesta, AST-ul va avea un numar minimal de **tipuri** de noduri, iar algoritmul Thompson cat mai putine cazuri diferite de tratat.
 +
 +===== Caractere de control sau obisnuite? =====
 +
 +O problema care a aparut deja inclusiv la etapa 1 are legatura cu rolul caracterelor intr-un regex. Caracterele pot fi //de control// (precum ''​()*|''​ dar si //​whitespace//​) sau obisnuite. Insa dorim sa folosim caractere de control si cu rolul de caractere obisnuite. In acest caz, acestea trebuie intotdeauna //​escapate//​ folosind ghilimele. Spre exemplu, nu putem folosi spatii albe intr-un regex decat //escapat// - '''​ '''​. ​
 +
 +In acelasi timp, in cadrul parsarii, este important sa stim rolul pe care il are un caracter citit (de control sau obisnuit). Pentru a reprezenta aceasta diferenta in Python, aveti in schelet doua clase: ''​Character'',​ ''​Operator''​. In Scala, puteti folosi tipul de date ''​Either[A,​B]''​ avand constructorii ''​Left(v:​A)''​ si ''​Right(v:​B)''​.
 +
 +===== Precedenta =====
  
 Avantajul formei Prenex este ca folosirea parantezelor nu mai este necesara pentru a specifica prioritatea operatiilor. In forma standard trebuie insa sa avem grija la prioritatea operatiilor pentru a evalua corect o expresie regulata. Avantajul formei Prenex este ca folosirea parantezelor nu mai este necesara pentru a specifica prioritatea operatiilor. In forma standard trebuie insa sa avem grija la prioritatea operatiilor pentru a evalua corect o expresie regulata.
  
-Facem o scurta analogie cu ordinea operatiilor aritmetice: + si -, * si /, respectiv paranteze pentru a intelege mai usor ordinea operatiilor din Regex-uri. 
  
-Prioritatea operatiilor aritmetice este: paranteze (), inmultiri sau impartiri (* sau /), adunari sau scaderi (+ sau -). Astfel, expresia a + b*c - (d + e*f)/g se va transforma in urmatorul AST:+Facem o scurta analogie cu ordinea operatiilor aritmetice: ''​+''​ si ''​-'',​ ''​*''​ si ''/'',​ respectiv paranteze pentru a intelege mai usor ordinea operatiilor din Regex-uri. 
 + 
 +Prioritatea operatiilor aritmetice este: paranteze ​''​()''​, inmultiri sau impartiri (''​*'' ​sau ''​/''​), adunari sau scaderi (''​+'' ​sau ''​-''​). Astfel, expresia ​''​a + b*c - (d + e*f)/g'' ​se va transforma in urmatorul AST:
  
 {{:​lfa:​2022:​ast.png?​500}} {{:​lfa:​2022:​ast.png?​500}}
Line 49: Line 95:
 Putem observa ca paranteza se evalueaza inaintea inmultirilor,​ iar inmultirile inaintea adunarilor. Putem observa ca paranteza se evalueaza inaintea inmultirilor,​ iar inmultirile inaintea adunarilor.
  
-Similar, prioritatea pentru Regex-uri este paranteze (), star *, concat (nu are un simbol asociat) si union |.+Similar, prioritatea pentru Regex-uri este paranteze ​''​()''​, star ''​*''​, concat (nu are un simbol asociat) si union ''​|''​.
  
-De exemplu (a|b)*c|d, care va genera arborele:+De exemplu ​''​(a|b)*c|d''​, care va genera arborele:
  
 {{:​lfa:​2022:​ast2.png?​350}} {{:​lfa:​2022:​ast2.png?​350}}
  
-Din arbore putem genera usor forma prenex: UNION CONCAT STAR UNION a b c d+Din arbore putem genera usor forma prenex: ​''​UNION CONCAT STAR UNION a b c d''​.
-Nu este necesar sa implementati per se AST-ul, insa prin ierarhia de clase acesta este intrisec.+
  
 ===== Implementare ===== ===== Implementare =====
  
-Implementarea consta in parsarea unui Regex si transformarea sa in forma Prenex. Pentru acest lucru se recomanda folosirea unui AST - Abstract Syntax Tree.+Implementarea consta in parsarea unui Regex si transformarea sa in forma Prenex. Pentru acest lucru se recomanda folosirea unui AST - Abstract Syntax Tree. Puteti folosi exact AST-ul implementat la Etapa 1, adaugand o metoda de afisare (eventual chiar ''​toString''​) pentru a obtine forma prenex).
  
 ===== Parsarea expresiilor prenex ===== ===== Parsarea expresiilor prenex =====
  
 Pentru a parsa un Regex, avem nevoie de o stiva care sa retina parti ale expresiei / operatii parsate deja. Vom interactiona in doua feluri cu stiva: Pentru a parsa un Regex, avem nevoie de o stiva care sa retina parti ale expresiei / operatii parsate deja. Vom interactiona in doua feluri cu stiva:
- 
   * reducerea expresiilor (sau cooling):   * reducerea expresiilor (sau cooling):
     * Exemplul 1: daca pe stiva avem: ''​0 | Star(?) | …''​ , atunci vom inlocui cele doua expresii cu : ''​Star(0) | …''​     * Exemplul 1: daca pe stiva avem: ''​0 | Star(?) | …''​ , atunci vom inlocui cele doua expresii cu : ''​Star(0) | …''​
Line 74: Line 118:
  
 Implementarea voastra trebuie sa combine in mod eficient adaugarea cu reducerea. Implementarea voastra trebuie sa combine in mod eficient adaugarea cu reducerea.
- 
-===== Preprocesarea Regex-urilor ===== 
- 
-Pe langa caracterele alfa-numerice si operatiile de baza star, concat si union, in aceasta etapa vom testa si: 
-  - doua operatii noi:  
-    - plus (+) - expresia asupra careia este aplicat apare de 1 data sau mai multe ori. 
-    - semnul intrebarii (?) - expresia asupra careia este aplicat apare o data sau niciodata. 
-  - 3 syntactic sugars: 
-    - [a-z] - orice caracter litera mica din alfabetul englez 
-    - [A-Z] - orice caracter litera mare din alfabetul englez 
-    - [0-9] - orice cifra 
-  - escaparea anumitor caracter de tipul '​\n',​ '​\t'​. In general '​c',​ unde c este un caracter va fi interpretat drept c. 
- 
-Pentru a facilita diferenta intre caractere si operatori aveti in schelet doua clase (Character, Operator in Python, respectiv Left, Right in Scala). 
- 
-Operatiile + si ? pot fi transformate direct in echivalentul lor. De exemplu a+ va fi transformat in aa*, iar a? in a|eps. 
  
 ===== Testare ===== ===== Testare =====
  
 Testarea este similara cu cea de la etapa 1. Mai mult, este necesara implementarea intregii etape 1, deoarece vom testa comportamentul corect al DFA-ului construit si nu rezultatul transformarii. Testarea este similara cu cea de la etapa 1. Mai mult, este necesara implementarea intregii etape 1, deoarece vom testa comportamentul corect al DFA-ului construit si nu rezultatul transformarii.
-Astfel, la etapa 2 veti obtine o forma prenex dintr-un Regex: Regex -> Prenex, pe care o sa il dam mai departe in transformarea facuta la etapa 1 Prenex -> NFA -> DFA (-> MinDFA eventual). La final testam daca DFA-ul construit accepta/​respinge un set de   ​cuvinte.+Astfel, la etapa 2 veti obtine o forma prenex dintr-un Regex: Regex -> Prenex, pe care o sa il dam mai departe in transformarea facuta la etapa 1 Prenex -> NFA -> DFA (-> MinDFA eventual). La final testam daca DFA-ul construit accepta/​respinge un set de cuvinte.
  
 Verificarea corectitudinii implementarii voastre se va face automat, printr-o serie de teste unitare, o parte punctate si o parte nepunctate. Verificarea corectitudinii implementarii voastre se va face automat, printr-o serie de teste unitare, o parte punctate si o parte nepunctate.