====== Etapa 2 - Transformarea Regex-urilor la AFD-uri ====== **Update legat de upload**: incarcati o arhiva ce contine **atat fisierele sursa, cat si directorul de testare tests/ si script-ul checker.py**. **Checker si teste**: [[lfa:proiect:checker|Checker proiect LFA]]. Transformarea Regex-AFD ne permite operationalizarea expresiilor regulate, iar aceasta este o parte importanta atat dintr-un lexer cat si dintr-un parser. In etapa 2, vom implementa aceasta transformare in doi pasi: * transformarea **Regex** -> **AFN** * transformarea **AFN** -> **AFD**
Implementarea transformarilor se bazeaza integral pe procedurile algoritmice prezentate la curs.Pentru a realiza prima transformare, este necesar sa parsam o expresie regulata si sa construim arborele acesteia. Spre exemplu, expresia: ''ab|c*'' are asociat arborele: ''UNION(CONCAT(a,b),STAR%%(%%c))''. Datorita regulilor de precenta ale operatorilor, construirea acestui arbore este mai complicata, iar aceast task va fi amanat pentru etapa finala. Pentru a simplifica problema parsarii, expresiile vor fi prezentate **in forma prenex**. * In **forma prenex**, **//operatorul preceda intotdeauna//** operanzii. Prezentate astfel, expresiile pot fi parsate mult mai usor. Spre exemplu, expresia de mai sus in forma **prenex** este ''UNION CONCAT a b STAR c''. Observati similaritatea cu reprezentarea de arbore. ===== Descrierea inputului ===== * Un test consta intr-un fisier ''.in'' ce contine o expresie regulata in forma prenex. * Exemple:
UNION a b
UNION CONCAT a b STAR c
CONCAT UNION a b UNION c d
CONCAT STAR UNION a b UNION b c
STAR UNION CONCAT a b CONCAT b STAR d
CONCAT PLUS c UNION a PLUS b
===== Cerinta si descrierea outputului =====
Implementarea voastra va primi un fisier de input ''
...
**Atentie**: Implementarea va fi punctata doar in masura in care respecta cerinta (se construieste un AFD folosind algoritmii prezentati la curs).
===== Identitatea starilor =====
Implementarea algoritmului lui Thomson este complicata de faptul ca starile sunt reprezentate ca intregi. Spre exemplu, AFN-urile pentru expresiile ''0'' si ''1'' vor avea fiecare cate doua stari, cel mai probabil numerotate de la 0 la 1. Pentru a construi AFN-ul pentru ''0|1'', trebuie sa **renumerotam** starile tinand cont de:
* faptul ca trebuie sa cream o noua stare initiala, si una finala
* starile AFN-ului pentru ''0'' trebuie sa fie **disjuncte** fata de cele ale lui ''1''
* logica AFN-urilor (ce cuvinte accepta) nu trebuie sa influentata de numerotare
* renumerotarea trebuie sa fie **suficient de generala** (vom mai avea nevoie de ea la etapa 3), astfel incat sa se comporte ca un **map** (o aplicatie de //functie// peste stari). In aceasta etapa, //functia// va fi ''+x''.
===== Parsarea formei Prenex =====
Pentru a parsa forma Prenex, 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).
* Exemplul 1: daca pe stiva avem: ''0 | Star(?) | ... '', atunci vom inlocui cele doua expresii cu : '' Star(0) | ...''
* Exemplul 2: daca pe stiva avem: ''Star(0) | Concat(?,?) | ... '', rezultatul reducerii va fi: ''Concat(Star(0),?) | ...''
* ** adaugarea expresiilor**: vom citi operatorul sau operandul curent, si vom adauga elementele corespunzatoare pe stiva.
Implementarea voastra trebuie sa combine in mod eficient **adaugarea** cu **reducerea**.
===== Development si testare =====
Pentru a testa AFD-urile generate de voi, veti avea nevoie de o modalitate de afisare a acestora in text. Va sugeram sa adaugati la aceasta si o modalitate grafica. Atasam un script Python3 ({{:lfa:proiect:draw_fa.zip|}}) care poate fi folosit pentru a desena AFD-uri:
* script-ul primeste in linia de comanda un fisier CSV ce contine un AFD, si deseneaza AFD-ul respectiv.
* va fi necesar sa instalati anumite module pentru a rula scriptul (e.g. networkx, numpy).
* trebuie sa scrieti o procedura de afisare a unui AFD sub forma de CSV. Atasam un exemplu (AFD-ul generat pentru expresia ''00*''):
from,char,to
0,0,1
1,ε,2
2,ε,3
2,ε,f5
3,0,4
4,ε,3
4,ε,f5
* prima linie este obligatorie si aceeasi pt orice AFD
* tranzitiile sunt codificate pe linii, sub forma: ''stare initiala, caracter, stare finala''
* pentru legibilitate, starile finale au fost precedate de simbolul ''f''
===== Sugestii de implementare pentru Python =====
* Folositi **clase** pentru reprezentarea interna a unei expresii regulate. Vom refolosi aceasta reprezentare in etapa 3.
* Alegeti o implementare pentru AFN-uri care sa permita **refolosirea** (extinderea) ei pentru situatii in care **starile** au alta structura (multimi de intregi in loc de intregi).
* In procesul de parsare, instantiati cu ''None'' expresii a caror parsare este in curs de desfasurare. Ele vor fi modificate ulterior.
===== Sugestii de implementare pentru Haskell =====
* In desfasurarea procesului de constructie al regex-urilor, nu toate expresiile sunt disponibile. Aveti mai multe alternative pentru a construi expresii mai complicate, atunci cand parti (continute) ale acestora nu sunt cunoscute inca:
* adaugati constructori pentru expresii a caror parsare este in curs de desfasurare. Acestia vor fi folositi doar in timpul parsarii formei prenex. Spre exemplu, pe langa ''Concat :: Expr -> Expr -> Expr'', definiti ''LConcat :: Expr -> Expr''. Acesta va codifica faptul ca am citit doar partea din **stanga** a unei concatenari. Asemanator, ''LRConcat :: Expr'' va codifica faptul ca am citit o concatenare si urmeaza sa citim operanzii acesteia.
* adaugati un constructor pentru expresia regulata ''Unknown'' care codifica o expresie ce urmeaza sa fie calculata
-----------------------------------------------------------
* Interactiunea cu stiva se face cel mai natural folosind **pattern matching** (si este mult mai simplu de implementat in Haskell).
* Pentru codificarea de AFN-uri:
* Adaugati clasei ''FA'', definita la etapa anterioara, metoda ''relabel'' (si implementati-o):
class FA t where
fromList :: State s => Set Char -> s -> [(s,Char,s)] -> [s] -> t s
states :: (State s) => t s -> [s]
relabel :: (State s, State s') => (s -> s') -> t s -> t s'
size :: (State s) => t s -> Integer
* Bonus: de ce nu am folosit ''fmap'', in locul functiei ''relabel''?
* Definiti tipul ''Nfa a'' si inrolati-l in clasa anterioara. O definitie (mai multe sunt posibile) este urmatoarea:
type NDelta a = Map (a,Char) (Set a)
data Nfa a = Nfa {nsigma :: Set Char, ninitial :: a, ndelta :: (NDelta a), nfin :: [a]}
===== Metodologia de testare =====
Odata generate AFD-urile de catre implementarea voastra si afisate in cadrul fisierlor de output, checker-ul etapei 2 compara rezultatele fata de o suita de AFD-uri de referinta, verificand pentru fiecare caz in parte **daca automatele accepta acelasi limbaj**. Mai multe detalii legate de checker gasiti pe pagina [[lfa:proiect:checker|Checker proiect LFA]].