Checker si teste: Checker proiect LFA.
Etapa 3 consta la randul ei in trei parti, dintre care ultima este bonus.
Le descriem in continuare pe fiecare dintre ele:
Plecand de la implementarile voastre pentru etapele 1 si 2, veti realiza un lexer complet care, spre deosebire de lexer-ul de la Etapa 1, primeste o specificatie de tokens ce contine expresii regulate in loc de AFD-uri.
Daca nu ati citit pana acum cerinta de la Etapa 1, este bine sa o faceti pentru a intelege mai bine Etapa 3.
Fisierul va contine un token si expresia lui regulata, cate una pe fiecare linie, sub urmatoarea forma:
<token> <regex>;
unde:
<token>
este o secventa de caractere alfabetice (de obicei uppercase)<regex>
este o expresie regulata
Token-ul este separat printr-un spatiu alb de expresia regulata. Fiecare linie se va termina prin caracterul special ;
.
Expresia regulata are urmatoarea sintaxa:
<regex> ::= <regex><regex> | # concatenare <regex> | <regex> | # reuniune (intre regex-uri folosim caracterul '|' pt a desemna reuniunea) <regex>* | # Kleene star <regex>+ | # ee* (<regex>) | # paranteze [a-z]+ | # alias pt expresia a | b | c ... | z, avand numai caractere alfabetice lowercase [0-9]+ | # alias pt expresia 0 | 1 | 2 | ... | 9 <c> | # orice caracter alfanumeric '<any_c>' # orice caracter ASCII, nu doar unul alfabetic
Spatiile albe pot aparea liberal in corpul unei expresii regulate, fara a influenta sensul acesteia. Expresiile regulata ce contin caracterul whitespace se regasesc incadrate intre ' '
.
Exemplu de specificatie:
KEYWORD def | while | if; # concatenare de caractere REGISTER R[0-9]+; # orice sir ce incepe cu caracterul R urmat de unul sau mai multe cifre EXPR [a-z]+ (('+' | '*') [a-z]+)*; # o posibila codificare a expresiilor aritmetice SPACES ' ' | '\t'; # spatii albe
Aceasta poate fi organizata astfel:
Output-ul va fi scris intr-un fisier, fiind unui sir de tokens si lexeme identificat (la fel ca la Etapa 1). El va avea forma:
<token1> <lexeme1> ... <token_n> <lexeme_n>
unde fiecare pereche <token,lexem>
se afla pe cate o linie separata, iar intre fiecare token
si lexem
se afla cate un singur spatiu alb (neincluzand spatiile albe ce se pot afla in compozitia lexemului).
Testarea va fi efectuata intr-o maniera identica ca cea din cadrul Etapei 1, prin compararea output-ului generat de voi cu cel aflat in fisierele de referinta. Mai multe detalii legate de script-ul de testare si cum sa il rulati gasiti pe pagina dedicata Checker-ului.
Folosind lexer-ul scris de voi, implementati un parser simplu pentru limbajul Imperative descris mai jos. Folositi aceeasi abordare ca in implementarea parserului pentru expresii regulate. }
Input-ul va fi un program a carui descriere sintactica se regaseste mai jos (Limbajul Imperative):
<prog> ::= <variable> '=' <expr> | begin <instruction_list> end | while (<expr>) do <prog> od | if (<expr>) then <prog> else <prog> fi <instruction_list> ::= <prog> | <prog> '\n' <instruction_list> <expr> ::= <expr> '+' <expr> | <expr> '-' <expr> | <expr> '*' <expr> | <expr> '>' <expr> | <expr> '==' <expr> | <variable> | <integer>
\n
in sintaxa limbajului (atunci cand delimiteaza intre instructiuni si atunci cand doar formateaza).Pentru implementare, trebuie sa:
Output-ul va fi redat sub forma unui fisier ce contine AST-ul rezultat in urma parsarii programului. Un exemplu de instantiere si afisare se gaseste in comentariile din scheletul de clase ast.py
(atentie: subblocurile sunt identate cu cate doua spatii fata de blocurile parinte).
Folosind parserul scris anterior, realizati un interpretor pentru programe Imperative. Interpretorul va folosi AST-ul scris de voi. Aceasta parte nu va avea tester dedicat si se va baza pe fisierele de testare ale partii 3.2. In timpul prezentarii veti ilustra asistentului vostru modalitatea de functionare.
In termeni generici, un interpretor mentine si gestioneaza un store, adica o mapare intre nume de variabile intalnite in program, si valorile la care sunt legate acestea:
Mai jos aveti un exemplu de actualizare a unui store in timpul interpretarii unui program scurt (store-ul este reprezentat in comentarii drept un dictionar ce retine maparile dintre o variabila si valorea sa curenta):
begin // {} - store vid a = 1; // {a : 1} - s-a adaugat o mapare in store r = 0; // {a : 1, r : 0} if (a == 2) then // {a : 1, r : 0} - expresiile booleene nu modifica store-ul r = a + 3; // nu se ajunge aici else r = a; // {a : 1, r : 1} - valoarea lui r a fost actualizata fi end
Program → Store
, unde Store
trebuie definit de catre voi.IO (Input / Output) in limbajul Haskell se bazeaza pe design pattern-ul Monad, ne-discutat inca. Puteti totusi folosi cu usurinta urmatorul exemplu de cod pentru citirea dintr-un fisier, si afisarea unui mesaj:
test = do in <- readFile "filename.extension" putStrLn $ show $ f in
In codul de mai sus:
in
este o variabila de tip String
ce va retine continutul fisierului indicatputStrLn
este o functie care are ca efect afisareaf :: (Show a) ⇒ String → a
este o functie pe care o puteti defini voi sub ce forma doriti, si care materializeaza intreaba prelucrare pe care o faceti asupra continutului fisierului. Ea poate intoarce orice obiect, cu conditia sa fie afisabil (astfel incat apelul show
sa fie unul valid).