Differences

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

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
lfa:2024:proiect:etapa2 [2024/11/22 13:55]
atoader
lfa:2024:proiect:etapa2 [2024/11/24 19:02] (current)
atoader
Line 1: Line 1:
-{{:​lfa:​2024:​proiect:​src.zip|}}Deadline etapa 1: 22 nov 2023 23:59+Deadline etapa 1: 21 nov 2024 23:55
  
-Deadline etapa 2: dec 2023 23:59+Deadline etapa 2: dec 2024 23:55
  
 Schelet etapa 1 {{:​lfa:​2023:​lfa2024-skel-etapa1.zip|}} Schelet etapa 1 {{:​lfa:​2023:​lfa2024-skel-etapa1.zip|}}
Line 7: Line 7:
 Schelet etapa 2 {{:​lfa:​2024:​proiect:​src.zip|}} Schelet etapa 2 {{:​lfa:​2024:​proiect:​src.zip|}}
  
-====== Proiect ====== 
- 
-Proiectul consta in implementarea unui lexer in python. 
-===== 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. 
- 
-==== Care este input-ul unui lexer? ==== 
-Lexer-ul primeste initial o specificatie de forma: 
-<​code>​ 
-TOKEN1 : REGEX1; 
- 
-TOKEN2 : REGEX2; 
- 
-TOKEN3 : REGEX3; 
- 
-... 
-</​code>​ 
-unde fiecare ''​TOKENi''​ este un nume dat unui token, iar ''​REGEXi''​ este un regex ce descrie lexemele ce pot fi clasificate ca acel token. Puteti imagina aceasta specificatie ca un //fisier de configurare//,​ care descrie modul in care va functiona lexerul pe diverse fisiere de text. 
- 
-Inputul efectiv al unui lexer este un text care va fi impartit in lexeme folosind expresii regulate. In cursurile viitoare veti afla mai multe detalii despre cum functioneaza si cum sunt implementate lexerele. 
- 
-==== Care este output-ul unui lexer? ==== 
-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 1 ===== 
-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. Aceste etape construiesc un DFA pe baza regex-ului. 
- 
-**Etapa 1** a proiectului consta in conversia NFA in DFA (folosind Algoritmul __Subset Construction__ prezentat la curs) 
- 
-Tema va fi implementata in Python, iar scheletul va ofera un pattern pentru implementarea functionalitatilor necesare. 
- 
-====Structura scheletului==== 
- 
-In scheletul temei veti gasi 2 clase: **NFA** si **DFA**. In fiecare veti avea de implementate metodele necesare pentru verificarea comportamentului lor si pentru a realiza conversia ceruta de tema. 
- 
-=== Clasa DFA === 
- 
-Un DFA va fi descris de urmatoarele campuri: 
-<​code>​ 
-S - alfabetul limbajului, reprezentat ca un set de string-uri 
-K - starile automatului,​ reprezentat ca un set de __STATE__ 
-q0 - starea initiala a automatului 
-d - functia de tranzitie, reprezentata ca un dictionar cu cheie (stare, caracter_alfabet) si alta stare ca si valoare 
-F - starile finale ale automatului 
-</​code>​ 
- 
-Desi cea mai simpla modalitate de a ne referi la o stare este printr-un numar intreg, in anumite componente ale proiectului (si de la aceasta etapa, dar si de la etape viitoare) va fi mult mai convenabil sa lucram cu alte tipuri de etichete pentru stari (de exemplu, seturi de intregi sau tupluri). De aceea, parametrul ''​STATE''​ va permite sa utilizati ce tip doriti ca si stare a automatului,​ puteti nota starile automatului atat cu intregi ''​0,​1,​2,​3,​...''​ cat si cu siruri de caractere ''​s0,​q1,​sink,​...''​ sau alte tipuri de date (''​frozenset''​) 
- 
-  - In aceasta clasa veti avea de implementat **obligatoriu** functia ''​accept'',​ functie care primeste un cuvant si ruland DFA-ul pe acel cuvant va intoarce ''​True''​ daca cuvantul este acceptat, iar ''​False''​ in caz contrar. 
-  - Functia ''​remap_states''​ **nu este** obligatoriu de implementat,​ din moment ce nu este apelata de checker, insa este recomandata din moment ce va va usura implementarea algoritmului subset construction. Ea are ca scop **transformarea** setului de stari, de la un tip (spre exemplu ​ //string//) la un altul (spre exemplu, //​integer//​). Astfel de transformari vor fi necesare, in special in etapele ulterioare ale proiectului. 
- 
-Spre exemplu, daca am avea automatul de mai jos: 
- 
-{{:​lfa:​2023:​remap_before.png?​400|}} 
-/* 
-<​code>​ 
-> (0) -a,b-> (1) ----a---->​ ((2)) 
-               ​\-b->​ (3) <-a,b-/ 
-                   / ​    \ 
-                   ​\-a,​b-/​ 
-</​code>​ 
-*/ 
- 
-Am putea aplica functia ''​x -> '​q'​ + str(x+2)'',​ care ar creea un DFA cu urmatoarele stari: 
- 
-{{:​lfa:​2023:​remap_after.png?​400|}} 
-/* 
-<​code>​ 
-> (q2) -a,b-> (q3) ----a---->​ ((q4)) 
-                \-b-> (q5) <-a,b-/ 
-                    /     \ 
-                    \-a,b-/ 
-</​code>​ 
-*/ 
- 
-===Clasa NFA=== 
- 
-Clasa functioneaza in aceeasi maniera cu cea a DFA-ului, cu o singura diferenta: 
-  * Spre deosebire de reprezentarea de la curs, unde $math[\Delta] reprezenta o relatie peste $math[K \times \Sigma \times K], in Etapa 1, ''​d''​ va fi tot o functie, (codificata printr-un dictionar), care va asocia unei perechi (stare, caracter_alfabet),​ un **set de stari** succesor (in loc de o stare unica, asa cum se intampla intr-un DFA).  
- 
-Alte observatii: 
-  - Functia ''​epsilon_closure''​ primeste o stare a automatului si intoarce un set de stari, care reprezinta starile la care se poate ajunge doar prin epsilon-tranzitii de la starea initiala (fara a consuma nici-un caracter) 
-  - Functia ''​subset_construction''​ va intoarce un DFA, construit din NFA-ul curent prin algoritmul __subset construction__. DFA-ul intors va avea ca tip al starilor ''​frozenset[STATE]''​ (starile ''​0''​ si ''​1''​ dintr-un NFA vor ajunge o multime de stari ''​{0,​1}''​ dintr-un DFA). Folosim ''​frozenset''​ in loc de ''​set'',​ pentru ca acesta din urma **nu este imutabil** (seturile pot fi modificate prin efecte laterale). Avem nevoie de un obiect **imutabil** pentru a putea calcula un hash (mereu acelasi), si implicit pentru a putea folosi astfel de obiecte drept chei intr-un dictionar (lucru imposibil daca obiectul-cheie este mutabil). 
-  - Functia ''​remap_states''​ care are acelasi format si scop ca functia de la DFA-uri 
- 
-Functiile ''​epsilon_closure''​ si ''​subset_construction''​ **sunt obligatoriu** de implementat,​ iar functia ''​remap_states''​ **nu** este. 
  
 ===== Etapa 2 ===== ===== Etapa 2 =====
  
-**Etapa 2** a proiectului consta in conversia Regex - NFA (folosind Algoritmul Thompson prezentat la curs)+**Etapa 2** a proiectului consta ​in parsarea unei expresii regulate precum si in conversia Regex - NFA (folosind Algoritmul Thompson prezentat la curs)
  
 ====Structura scheletului==== ====Structura scheletului====
Line 116: Line 30:
 </​code>​ </​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.+In descrierea de mai sus, elementele dintre parantezele angulare <> sunt **non-terminali** care trebuie generati, caracterele sunt intotdeauna plasate intre ghilimele simple, iar sirurile ​de caractere - 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 doi de forma ''​\c'',​ unde ''​c''​ poate fi orice caracter inclusiv de control.+''<​character>''​ se refera la orice caracter care nu face parte din caracterele ​de //control// (precum ''​*''​ sau ''​|''​),​ sau la orice sir de lungime doi de forma ''​\c'',​ unde ''​c''​ poate fi orice caracter inclusiv de control ​(mai exact - caracter escapat).
  
-"​eps"​ reprezinta caracterul ​Epsilon.+"​eps"​ reprezinta caracterul ​$math[\epsilon].
  
 ==== Preprocesarea Regex-urilor ==== ==== Preprocesarea Regex-urilor ====
Line 142: Line 56:
 In aceasta clasa veti avea de implementat metoda ''​thompson'',​ metoda care primeste un obiect de tip regex si intoarce un NFA (cu starile de tipul ''​int''​ ca si conventie). Regexul primit ca input va avea forma prezentata mai sus. In aceasta clasa veti avea de implementat metoda ''​thompson'',​ metoda care primeste un obiect de tip regex si intoarce un NFA (cu starile de tipul ''​int''​ ca si conventie). Regexul primit ca input va avea forma prezentata mai sus.
  
-Concatenarea nu va fi reprezentata printr-un caracter anume, vom considera ca constructiile de forma ''​ab''​ se traduc automat in "​caracterul a concatenat cu caracterul b". Concatenarea oprindu-se astfel la intalnirea **unei paranteze** sau **a unei uniuni**. Spre exemplu:+Concatenarea nu va fi reprezentata printr-un caracter anume, vom considera ca constructiile de forma ''​ab''​ se traduc automat in "​caracterul a concatenat cu caracterul b". Concatenarea oprindu-se astfel la intalnirea **unei paranteze** sau **a unei reuniuni**. Spre exemplu:
   - ''​ab|c''​ se traduce in ''​(ab)|c''​   - ''​ab|c''​ se traduce in ''​(ab)|c''​
   - ''​abd*''​ se traduce in ''​ab(d)*''​   - ''​abd*''​ se traduce in ''​ab(d)*''​
Line 149: Line 63:
 === Hint de implementare === === Hint de implementare ===
  
-Pentru a va usura lucrul cu expresiile regulate, ​va puteti creea mai multe clase care extind ​din clasa regex (ex ''​Character'',​ ''​Star'',​ ''​Union'',​ ''​Concat'',​ etc...) iar fiecare clasa va avea propria implementare a metodei ''​thompson''​.+Pentru a va usura lucrul cu expresiile regulate, puteti creea mai multe clase care extind clasa regex (ex ''​Character'',​ ''​Star'',​ ''​Union'',​ ''​Concat'',​ etc...) iar fiecare clasa va avea propria implementare a metodei ''​thompson''​.
  
  
Line 186: Line 100:
 ├── ID.txt ├── ID.txt
 </​code>​ </​code>​
 +
 +====== Proiect ======
 +
 +Proiectul consta in implementarea unui lexer in python.
 +===== 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.
 +
 +==== Care este input-ul unui lexer? ====
 +Lexer-ul primeste initial o specificatie de forma:
 +<​code>​
 +TOKEN1 : REGEX1;
 +
 +TOKEN2 : REGEX2;
 +
 +TOKEN3 : REGEX3;
 +
 +...
 +</​code>​
 +unde fiecare ''​TOKENi''​ este un nume dat unui token, iar ''​REGEXi''​ este un regex ce descrie lexemele ce pot fi clasificate ca acel token. Puteti imagina aceasta specificatie ca un //fisier de configurare//,​ care descrie modul in care va functiona lexerul pe diverse fisiere de text.
 +
 +Inputul efectiv al unui lexer este un text care va fi impartit in lexeme folosind expresii regulate. In cursurile viitoare veti afla mai multe detalii despre cum functioneaza si cum sunt implementate lexerele.
 +
 +==== Care este output-ul unui lexer? ====
 +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 1 =====
 +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. Aceste etape construiesc un DFA pe baza regex-ului.
 +
 +**Etapa 1** a proiectului consta in conversia NFA in DFA (folosind Algoritmul __Subset Construction__ prezentat la curs)
 +
 +Tema va fi implementata in Python, iar scheletul va ofera un pattern pentru implementarea functionalitatilor necesare.
 +
 +====Structura scheletului====
 +
 +In scheletul temei veti gasi 2 clase: **NFA** si **DFA**. In fiecare veti avea de implementate metodele necesare pentru verificarea comportamentului lor si pentru a realiza conversia ceruta de tema.
 +
 +=== Clasa DFA ===
 +
 +Un DFA va fi descris de urmatoarele campuri:
 +<​code>​
 +S - alfabetul limbajului, reprezentat ca un set de string-uri
 +K - starile automatului,​ reprezentat ca un set de __STATE__
 +q0 - starea initiala a automatului
 +d - functia de tranzitie, reprezentata ca un dictionar cu cheie (stare, caracter_alfabet) si alta stare ca si valoare
 +F - starile finale ale automatului
 +</​code>​
 +
 +Desi cea mai simpla modalitate de a ne referi la o stare este printr-un numar intreg, in anumite componente ale proiectului (si de la aceasta etapa, dar si de la etape viitoare) va fi mult mai convenabil sa lucram cu alte tipuri de etichete pentru stari (de exemplu, seturi de intregi sau tupluri). De aceea, parametrul ''​STATE''​ va permite sa utilizati ce tip doriti ca si stare a automatului,​ puteti nota starile automatului atat cu intregi ''​0,​1,​2,​3,​...''​ cat si cu siruri de caractere ''​s0,​q1,​sink,​...''​ sau alte tipuri de date (''​frozenset''​)
 +
 +  - In aceasta clasa veti avea de implementat **obligatoriu** functia ''​accept'',​ functie care primeste un cuvant si ruland DFA-ul pe acel cuvant va intoarce ''​True''​ daca cuvantul este acceptat, iar ''​False''​ in caz contrar.
 +  - Functia ''​remap_states''​ **nu este** obligatoriu de implementat,​ din moment ce nu este apelata de checker, insa este recomandata din moment ce va va usura implementarea algoritmului subset construction. Ea are ca scop **transformarea** setului de stari, de la un tip (spre exemplu ​ //string//) la un altul (spre exemplu, //​integer//​). Astfel de transformari vor fi necesare, in special in etapele ulterioare ale proiectului.
 +
 +Spre exemplu, daca am avea automatul de mai jos:
 +
 +{{:​lfa:​2023:​remap_before.png?​400|}}
 +/*
 +<​code>​
 +> (0) -a,b-> (1) ----a---->​ ((2))
 +               ​\-b->​ (3) <-a,b-/
 +                   / ​    \
 +                   ​\-a,​b-/​
 +</​code>​
 +*/
 +
 +Am putea aplica functia ''​x -> '​q'​ + str(x+2)'',​ care ar creea un DFA cu urmatoarele stari:
 +
 +{{:​lfa:​2023:​remap_after.png?​400|}}
 +/*
 +<​code>​
 +> (q2) -a,b-> (q3) ----a---->​ ((q4))
 +                \-b-> (q5) <-a,b-/
 +                    /     \
 +                    \-a,b-/
 +</​code>​
 +*/
 +
 +===Clasa NFA===
 +
 +Clasa functioneaza in aceeasi maniera cu cea a DFA-ului, cu o singura diferenta:
 +  * Spre deosebire de reprezentarea de la curs, unde $math[\Delta] reprezenta o relatie peste $math[K \times \Sigma \times K], in Etapa 1, ''​d''​ va fi tot o functie, (codificata printr-un dictionar), care va asocia unei perechi (stare, caracter_alfabet),​ un **set de stari** succesor (in loc de o stare unica, asa cum se intampla intr-un DFA). 
 +
 +Alte observatii:
 +  - Functia ''​epsilon_closure''​ primeste o stare a automatului si intoarce un set de stari, care reprezinta starile la care se poate ajunge doar prin epsilon-tranzitii de la starea initiala (fara a consuma nici-un caracter)
 +  - Functia ''​subset_construction''​ va intoarce un DFA, construit din NFA-ul curent prin algoritmul __subset construction__. DFA-ul intors va avea ca tip al starilor ''​frozenset[STATE]''​ (starile ''​0''​ si ''​1''​ dintr-un NFA vor ajunge o multime de stari ''​{0,​1}''​ dintr-un DFA). Folosim ''​frozenset''​ in loc de ''​set'',​ pentru ca acesta din urma **nu este imutabil** (seturile pot fi modificate prin efecte laterale). Avem nevoie de un obiect **imutabil** pentru a putea calcula un hash (mereu acelasi), si implicit pentru a putea folosi astfel de obiecte drept chei intr-un dictionar (lucru imposibil daca obiectul-cheie este mutabil).
 +  - Functia ''​remap_states''​ care are acelasi format si scop ca functia de la DFA-uri
 +
 +Functiile ''​epsilon_closure''​ si ''​subset_construction''​ **sunt obligatoriu** de implementat,​ iar functia ''​remap_states''​ **nu** este.
 +