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. ===== Proiect LFA - Lexer in Python sau Haskell ===== * **[[lfa:proiect:etapa1 | Etapa 1 - Lexer cu AFD-uri]]** * **[[lfa:proiect:etapa2 | Etapa 2 - Transformare Regex - AFD]]** * **[[lfa:proiect:etapa3 | Etapa 3 - Lexer cu Regexuri si parser]]** ====== Etapa 4 - Parsare Regex si limbaj ====== Serializare lexer (ca sa ne legam direct cu etapa 1). Apoi parsare. Doua programe. ==== Limbajul 1: Imperative ==== <code> <prog> ::= <variable> '=' <expr>; | begin <instruction_list> end | while (<expr>) do <prog> od | if (<expr>) then <prog> else <prog> fi <instruction_list> ::= [<prog>] <expr> ::= <expr> + <expr> | <expr> > <expr> | <expr> == <expr> | <variable> | <integer> </code> ==== Limbajul 2: Lisp ==== ==== Limbajul 3: Html ??? ==== ===== Metodologia de testare: ===== Combinam expresii din fisierul de referinta in specificatii. Mai sunt necesare detalii de clarificat. ====== Etapa 3 - Conversie AFN AFD ====== ===== Metodologia de testare: ===== Asemanator cu etapa 2. Folosim acelasi fisier de input, insa generam, in locul expresiilor PRENEX, nfa-uri, din lista de referinta. ====== Etapa 2 - Conversie Regex AS la AFN ====== 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). ===== Descrierea inputului ===== * un prim pas in aceasta etapa este parsarea expresiilor regulate, si construirea unui arbore pentru acestea. 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 4. 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. * Un test consta intr-un fisier ''.in'' ce contine: * **pe prima linie**, o expresie regulata in forma prenex * **in continuare**, cate un cuvant pe o linie * Exemplu: <code> UNION CONCAT a b STAR c abc abcccc ccc ab </code> ===== Cerinta si descrierea outputului ===== Implementarea voastra va primi un fisier de test ''<testxy.in>'', va construi **AFN-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 AFN folosind algoritmul prezentat 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 creem 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**. ===== Developlement si testare ===== Pentru a testa AFN-urile generate de voi, aveti nevoie de o modalitate de afisare a acestora in text. Va sugeram sa ii adaugati si modalitate grafica. Atasam un script Python3 care poate fi folosit pentru a desena AFN-uri: * script-ul primeste in linia de comanda un fisier CSV ce contine un AFN, si deseneaza AFN-ul respectiv. * va fi necesar sa instalati anumite dependinte pentru a rula scriptul (e.g. networkx, numpy). * trebuie sa scrieti o procedura de afisare a unui AFN sub forma de CSV. Atasam un exemplu (AFN-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 AFN * 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 4. * 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 tipul vostru de date care va retine expresii, 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. * Interactiunea cu stiva se face cel mai natural folosind **pattern matching** (si este mult mai simplu de implementat in Haskell). * Pentru codificarea de NFA-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.