====== 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 '''' si un fisier de output '''', va construi **AFD-ul asociat** expresiei regulate, iar apoi va afisa automatul in fisierul de output conform formatului de mai jos: ... **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]].