Table of Contents

Etapa 3 - Lexer complet si Limbajul Imperative

Checker si teste: Checker proiect LFA.

Etapa 3 consta la randul ei in trei parti, dintre care ultima este bonus.

Punctaj

Le descriem in continuare pe fiecare dintre ele:

3.1. Lexer complet

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.

Structura fisierului cu tokens

Fisierul va contine un token si expresia lui regulata, cate una pe fiecare linie, sub urmatoarea forma:

<token> <regex>;

unde:

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

Implementarea lexerului complet

Aceasta poate fi organizata astfel:

Output si testare

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.

Sugestii de implementare pt Lexer complet

3.2. Parser pentru un limbaj de programare (Imperative)

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

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> 
                   

Pentru implementare, trebuie sa:

Output

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).

3.3. Interpretor pentru limbajul Imperative

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

Sugestii de implementare pentru limbajul Python

Sugestii de implementare pentru limbajul Haskell

Citirea unui fisier in limbajul Haskell

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:

  1. in este o variabila de tip String ce va retine continutul fisierului indicat
  2. putStrLn este o functie care are ca efect afisarea
  3. f :: (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).