Edit this page Backlinks This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ====== Etapa 2 - Transformarea Regex-urilor la AFD-uri ====== 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** <blockquote> Implementarea transformarilor se bazeaza integral pe procedurile algoritmice prezentate la curs. </blockquote> 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: <code> UNION a b </code> <code> UNION CONCAT a b STAR c </code> <code> CONCAT STAR UNION a b UNION b c </code> <code> STAR UNION CONCAT a b c CONCAT b STAR d </code> ===== Cerinta si descrierea outputului ===== Implementarea voastra va primi un fisier de test ''<testxy.in>'', va construi **AFD-ul asociat** expresiei regulate, apoi va verifica daca acesta accepta cuvintele din fisierul de test. Implementarea va scrie la output un fisier ''<testxy.out>'' ce va contine, pe cate o linie ''ACCEPT'' sau ''REJECT'', pentru fiecare din cuvintele de la input. Exemplu de fisier de output pentru input-ul anterior: <code> REJECT REJECT ACCEPT ACCEPT </code> **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 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*''): <code> from,char,to 0,0,1 1,ε,2 2,ε,3 2,ε,f5 3,0,4 4,ε,3 4,ε,f5 </code> * 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'' * [TODO: link catre script pe github-ul LFA] ===== 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): <code Haskell> 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 </code> * 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: <code Haskell> type NDelta a = Map (a,Char) (Set a) data Nfa a = Nfa {nsigma :: Set Char, ninitial :: a, ndelta :: (NDelta a), nfin :: [a]} </code> ===== Metodologia de testare: ===== * construim un fisier mare cu expresii regulate **de mana**, peste diverse alfabete, care sa acopere cat mai multe din toate situatiile posibile, impreuna cu cuvinte acceptate sau nu. Acest fisier va trebui discutat eventual cu fiecare dintre noi. Exemplu: <code> 0 ACCEPT 0 REJECT 01 REJECT 10 00* ACCEPT 0000000000 REJECT 1 REJECT ε </code> * parsam expresiile cu implementarea noastra, generam forma prenex, si creem **mai multe perechi de fisiere separate, input-output** - in fiecare fisier input se va gasi o expresie si cuvintele de test. Ficare fisier de output va marca rezultatul asteptat. Exemplu: * Test0.in <code> 0 0 01 10 </code> * Test0.out <code> ACCEPT REJECT REJECT </code> * Test1.in <code> CONCAT 0 STAR 0 0000000000 1 ε </code> * Test1.out <code> ACCEPT REJECT REJECT </code> * Vom verifica, fiecare, ca outputul este corect, folosind implementarile noastre. ===== Metodologia de testare: ===== Folosim acelasi fisier de input, insa generam, in locul expresiilor PRENEX, nfa-uri, din lista de referinta. Obiectivul etapei 2 este conversia unei expresii regulate la un Automat Finit Nedeterminist. Aceasta transformare este un prim pas in conversia unei **specificatii** (expresia regulata) intr-o **implementare** (AFN).