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. Schelet si checker pentru fiecare limbaj: * {{lfa:2022:skel-py-e2.zip}} * {{lfa:2022:skel-scala-e2.zip}} Deadline etapa 3: ... ora 23:59 ====== Proiect ====== Etapa 3 a proiectul consta in implementarea unui lexer in python sau scala. <note important>Este recomandat sa va folositi de functionalitatile implementate la etapele precedente pentru rezolvarea etapei</note> ===== Ce este un lexer? ==== Un lexer este un program care imparte un sir de caractere in subsiruri numite //lexeme//, fiecare dintre acestea fiind clasificat ca un //token//, pe baza unei specificatii. ==== Care este input-ul unui lexer? ==== Lexer-ul primeste initial o specificatie de forma: <code> TOKEN1 : REGEX1; TOKEN2 : REGEX2; TOKEN3 : REGEX3; ... </code> unde fiecare ''TOKENi'' este un nume dat unui token, iar ''REGEXi'' este un regex ce descrie lexemele ce pot fi clasificate ca acel token. Puteti imagina aceasta specificatie ca un //fisier de configurare//, care descrie modul in care va functiona lexerul pe diverse fisiere de text. Inputul efectiv al unui lexer este un text care va fi impartit in lexeme folosind expresii regulate. In cursurile viitoare veti afla mai multe detalii despre cum functioneaza si cum sunt implementate lexerele. ==== Care este output-ul unui lexer? ==== Lexer-ul are ca output o lista de forma : ''[(lexema1, TOKEN_LEXEMA_1), (lexema2, TOKEN_LEXEMA_2), ...]'', unde ''TOKEN_LEXEMA_N'' este numele token-ului asociat lexemei n, pe baza specificatiei. ==== Cand isi termina un lexer executia ? ==== Un lexer va incepe dintr-o stare initiala si va continua pana cand ajunge in sink state. Pe parcursul trecerii lui dintr-o stare in alta el va tine minte starile si cuvintele intermediare pe care le a acceptat. Exemplu: pentru un lexer cu configuratia ''TOKEN -> a+'' si un cuvant ''aaab'' vom accepta 3 cuvinte intermediare inainte sa ajungem in sink state pe caracterul b: ''a'', ''aa'', ''aaa'' ==== Acceptarea celui mai lung prefix ==== In exemplul precedent avem 3 cuvinte intermediare acceptate, un lexer il va considera doar pe acela cel mai lung (''aaa'') si dupa va continue lexarea partii ramase din cuvant (''b''). ==== Prioritatea unui token ==== Un lexer poate intalni urmatorul caz: sa accepte prin 2 tokenuri diferite acelasi cuvant de lungime maxima. In cazul asta vom considera tokenul cu prioritate mai mare (care apare inaintea celuilalt in configuratie) Exemplu: pentru configuratia: ''(TOKEN1 -> abbc*, TOKEN2 -> ab+)'' si cuvantul ''abbd'' lexerul se va opri la caracterul ''d'' in sink state si va fi acceptat inainte o data prin ''TOKEN1 -> abb'' si a doua oara prin ''TOKEN2 -> abb''. Astfel, lexerul va considera lexemul ''(TOKEN1 -> abb)'' deoarece primul token are prioritate mai mare decat al doilea (apare inaintea lui in configuratie) ===== Structura specificatiei si incarcarea ei ===== ==== Python ==== Pentru aceasta etapa va fi nevoie sa implementati clasa ''Lexer'' cu 2 metode obligatorii: * constructorul care primeste ca parametru configuratia lexerului * metoda''lex'' care va primi un cuvand ca ''str'' si va intoarce rezultatul lexarii lui sub forma ''List[Tuple[str, str]] | str''. Metoda ''lex'' va intoarce o lista de tupluri ''(token, lexem_cuvant)'' in cazul in care lexarea reuseste si un string cu un mesaj de eroare in caz contrar. (//Mai multe despre cazurile in care un lexer poate esua mai jos//) Specificatia va fi incarcata in teste sub forma unui dictionar ''TOKEN -> REGEX'' ==== Scala ==== La aceasta etapa va trebui sa implementati clasa ''Lexer'' care va primi ca parametru o specificatie sub forma de ''String''. Specificatia va fi data sub forma TOKEN1: REGEX1; TOKEN2: REGEX2; TOKEN3: REGEX3; Metoda ''lex'' va imparti cuvantul dat ca parametru intr-o lista de lexeme sub forma ''(LEXEM, TOKEN)''. Ea va intoarce un ''Either[String,List[(String,String)]]'' deoarece in caz de succes vrem sa intoarcem ''Right(LISTA_LEXEME)'' si in caz de eroare ''Left(ERROR_MESSAGE)'' (//Mai multe despre cazurile in care un lexer poate esua mai jos//) ===== Parser pentru un limbaj de programare ===== Ultimul test din fisierul de teste va testa lexerul scris de voi pe sample-ul de cod real, folosind un limbaj super simplificat. Pentru a rezolva aceasta parte va trebui sa scrieti voi configuratia pentru toate tokenurile prezente in limbaj. Un scehelet pentru aceasta configuratie se afla in fisierul ''configuration.json'' in python si ''configuration'' in scala. Tokenurile prezente in schelet sunt toate tokenurile care pot sa apara in acest limbaj, singurul lucru care va ramane voua fiind scrierea unor regexuri care accepta aceste tokenuri. Tokenurile sunt: "BEGIN": "END": "EQUAL": "ASSIGN": "PLUS": "MINUS": "MULTIPLY": "GREATER": "WHILE": "DO": "OD": "IF": "THEN": "ELSE": "FI": "RETURN": "OPEN_PARANTHESIS": "CLOSE_PARANTHESIS": "NUMBER": "VARIABLE": Fiecare token va trebui sa accepte o componenta din cod, spre exemplu tokenul ''BEGIN'' va trebui sa accepte cuvantul ''begin'' care se foloseste la inceputul unui scope nou. Codul furnizat are prezente si whitespace uri (space-uri, tab-uri si new-line uri). Nu avem un token special pentru aceste whitespaces, si pentru a simplifica complexitatea output-ului va trebui sa considerati pentru fiecare token un numar arbitrar de whitespaces in jurul lui, important fiind doar ca el sa contina tokenul pe care incearca sa il accepte. Testerul va testa doar ca ordinea tokenurilor sa fie corecta, nu si ce lexeme vor accepta aceste tokenuri Exemplu: pentru codul begin a = 1 end va trebui sa afisam tokenurile in ordinea "BEGIN", "VARIABLE", "ASSIGN", "NUMBER", "END" ===== Erori de lexare ===== Erorile de lexare sunt in general cauzate o configuratie gresita / incompleta sau de un cuvant invalid. Informatiile care trebuie transmise in acest caz trebuie sa ajute programatorul sa isi dea seama unde un cod s-a intamplat eroare si care este tipul erorii. Din acest motiv vom afisa linia si coloana unde lexarea a esuat si tipul erorii. * Unordered List ItemEroare cauza de un caracter invalid in cuvant Aceasta eroare va aparea daca lexarea s-a oprit fara a accepta nici-un cuvant in prealabil. Aceasta este echivalenta cu ajungerea in starea ''SINK_STATE'' a lexerului fara a trece in prealabil printr-o stare finala. In acest caz vom afisa un mesaj de eroare in formatul No viable alternative at character ..., line ... In primul loc liber vom pune indexul caracterului unde s-a oprit lexarea (am ajuns in ''SINK_STATE'') indexat de la 0, iar in an doilea spatiu liber vom pune linia unde s-a intamplat asta (indexata de la 0). * Unordered List ItemEroare cauza de un cuvant incomplet Aceasta eroare va aparea daca lexarea a ajuns la finalul cuvantului fara a accepta in prealabil un lexem. In aceasta stare lexerul nu a ajuns in sink state, insa nici intr-o stare finala. In acest caz vom afisa un mesaj de eroare in formatul: No viable alternative at character EOF, line ... Ca un mic rezumat: prima eroare apare atunci cand caracterul la care am ajuns este invalid si nu avem cum sa acceptam, iar a doua apare atunci cand lexerul ar mai accepta, insa cuvantul este incomplet si nu mai are ce. ==== Python ==== In python eroare va fi intoarsa sub forma unui ''string'' de functa ''lex'' ==== Scala ==== In scala eroare va fi intoarsa sub forma unui ''Either[String]'' in formatul ''Left(message)'' ===== Format arhiva ====== <note important> In radacina proiectului trebuie pus un fisier intitulat ''ID.txt'' ce va avea pe prima linie a sa ID-ul vostru anonim (ar trebui sa il fi primit pe mail, dar daca din vreun motiv nu il aveti, luati legatura cu asistentul vostru) si pe a doua linie limbajul in care rezolvati tema (''python'' sau ''scala'') Exemplu de continut pentru ''ID.txt'': <code> 9921225 scala </code> sau <code> 9246163 python </code> </note> ==== Structura arhivei (Python) ==== <code> . ├── ID.txt └── src ├── DFA.py ├── __init__.py ├── NFA.py ├── Regex.py ├── Parser.py ├── Lex.py ... (alte surse pe care le folositi) </code> ==== Structura arhivei (Scala) ==== <code> . ├── build.sbt ├── ID.txt └── src └── main └── scala ├── Dfa.scala ├── Nfa.scala ├── Regex.scala ├── Lexer.scala ... (alte surse pe care le folositi) </code> <note> Pentru niciunul din limbaje nu este necesar sa includeti folder-ul cu teste, dar includerea sa nu va cauza erori. </note>