Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
lfa2021:proiect [2021/10/07 13:26] pdmatei |
lfa2021:proiect [2021/11/24 14:21] (current) stefan.stancu [Structura punctajului] |
||
---|---|---|---|
Line 1: | Line 1: | ||
===== Proiect LFA - Lexer in Python sau Haskell ===== | ===== Proiect LFA - Lexer in Python sau Haskell ===== | ||
- | * **[[lfa:proiect:etapa1 | Etapa 1 - Lexer cu AFD-uri]]** | + | ==== Structura punctajului ==== |
- | * **[[lfa:proiect:etapa2 | Etapa 2 - Transformare Regex - AFD]]** | + | |
- | * **[[lfa:proiect:etapa3 | Etapa 3 - Lexer cu Regexuri si parser]]** | + | |
+ | * Proiectul valoreaza **3p**, care se impart egal intre etape. | ||
+ | * O etapa se considera **trecuta**, daca, concomitent: (i) **a fost trimisa la deadline** si (ii) $math[pr=]70% din teste au trecut. | ||
+ | * Daca o etapa **nu** a fost **trecuta**, ea poate fi trimisa sau retrimisa oricand, pentru $math[pmin=]60% din punctaj (adica 60% din valoarea testelor care trec). | ||
+ | * Orice etapa **trecuta** poate fi retrimisa oricand, fara depunctare, pentru a obtine puncte in plus pentru teste aditionale care trec. | ||
+ | * Exemple: | ||
+ | * Studentul MP nu a **trecut** etapele 1 si 2 (nu a trimis etapa 1 la timp, si doar jumatate din teste au trecut la etapa 2), insa la etapa 3 a trimis tot proiectul 100% functional (toate testele trec). Punctajul lui este: 0.6p (Etapa 1) + 0.6p (Etapa 2) + 1p (Etapa 3) = 2.2p. | ||
+ | * Studentul DM a **trecut** etapa 1 (cu 70% din teste), la fel si etapa 2 (tot cu 70% din teste). In loc sa trimita etapa 3, a lucrat la etapa 1 si a reusit sa faca toate testele sa treaca la aceasta. Punctajul lui este 1p (Etapa 1) + 0.7p (Etapa 2) = 1.7p. | ||
+ | * Studentul XZ nu a **trecut** etapa 1 (a trimis la timp insa doar 50% din teste trec) si nu a trecut nici etapa 2 (din nou, a trimis la timp, insa doar 60% din teste trec). Etapa 3 nu a fost trimisa. Punctajul lui este 0.3p la etapa 1 (1p * 0.5teste * 0.6penalty) si 0.36p la etapa 2 (1p * 0.6teste * 0.6penalty)) | ||
+ | * **Ultima etapa va avea un deadline hard final** (punctajul obtinut pe aceasta este exclusiv cel de pe teste). Nu se mai pot trimite submisii (la nici o etapa), dupa expirarea deadline-ului acesta. | ||
- | ====== 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. | ||
+ | * **[[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]]** | ||
+ | * **[[lfa:proiect:checker | Checker]]** | ||