This shows you the differences between two versions of the page.
cpl:labs:03 [2015/10/20 01:42] cristina.ciocan [Analizorul sintactic] |
cpl:labs:03 [2016/10/05 23:17] (current) bogdan.nitulescu [Introducere in LCPL] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== 03. Bison Advanced ====== | ====== 03. Bison Advanced ====== | ||
- | In acest laborator vom aprofunda cunoștințele de bison și vom incepe să discutăm despre prima temă. | + | În acest laborator vom aprofunda cunoștințele de bison și vom incepe să discutăm despre prima temă. |
====== Bison ====== | ====== Bison ====== | ||
Line 11: | Line 11: | ||
''yylex()'' citește caracterele dintr-un file pointer (FILE*) numit yyin. Dacă nu setați yyin, acesta va pointa către intrarea standard (stdin). Rezultatul este transmis către yyout, care, implicit, pointează către ieșirea standard (stdout). | ''yylex()'' citește caracterele dintr-un file pointer (FILE*) numit yyin. Dacă nu setați yyin, acesta va pointa către intrarea standard (stdin). Rezultatul este transmis către yyout, care, implicit, pointează către ieșirea standard (stdout). | ||
- | Fiecare apel către ''yylex()'' returnează un întreg, care reprezintă tipul token-ului (id unic). Astfel, bison-ul știe ce token a primit. Token-ul poate avea o valoare, care trebuie sa fie pusă în variabila ''yylval''. | + | Fiecare apel către ''yylex()'' returnează un întreg, care reprezintă tipul token-ului (id unic). Astfel, bison-ul știe ce token a primit. Token-ul poate avea o valoare, care trebuie să fie pusă în variabila ''yylval''. |
Implicit, yylval este de tip ''int'', tip care, însă, se poate schimba redefinind YYSTYPE în fișierul bison. | Implicit, yylval este de tip ''int'', tip care, însă, se poate schimba redefinind YYSTYPE în fișierul bison. | ||
Line 77: | Line 77: | ||
După ce s-a recunoscut '' LET '(' var ')' '', este executată prima acțiune. Se salvează o copie a contextului curent (lista variabilelor disponibile). Apoi, se apelează ''declare_variable'' care adaugă ''var'' la lista curentă. În acest moment s-a terminat execuția acțiunii din mijlocul regulii, și se va parsa ''stmt''. Această acțiune este componenta numărul 5 a regulii, iar ''stmt'' este componenta numărul 6. Dupa ce s-a parsat și ''stmt'' se execută acțiunea de la sfârșitul regulii, care scoate variabila din tabela de simboli. | După ce s-a recunoscut '' LET '(' var ')' '', este executată prima acțiune. Se salvează o copie a contextului curent (lista variabilelor disponibile). Apoi, se apelează ''declare_variable'' care adaugă ''var'' la lista curentă. În acest moment s-a terminat execuția acțiunii din mijlocul regulii, și se va parsa ''stmt''. Această acțiune este componenta numărul 5 a regulii, iar ''stmt'' este componenta numărul 6. Dupa ce s-a parsat și ''stmt'' se execută acțiunea de la sfârșitul regulii, care scoate variabila din tabela de simboli. | ||
- | ====== Introducere in CMake ====== | + | ====== Introducere în CMake ====== |
CMake este un meta-sistem de build independent de platformă. Acesta citeste niște fișiere de configurare (''CMakeLists.txt'') care descriu procesul de build într-un [[https://cmake.org/Wiki/CMake/Language_Syntax|limbaj specific]], și pe baza acestora generează toate fișierele necesare pentru a face build-ul folosind un anumit tool: make, Ninja, Eclipse, Visual Studio etc. Pentru a vedea sistemele de build suportate pe o anumită platformă, puteți rula ''cmake -help'' (secțiunea ''Generators''). | CMake este un meta-sistem de build independent de platformă. Acesta citeste niște fișiere de configurare (''CMakeLists.txt'') care descriu procesul de build într-un [[https://cmake.org/Wiki/CMake/Language_Syntax|limbaj specific]], și pe baza acestora generează toate fișierele necesare pentru a face build-ul folosind un anumit tool: make, Ninja, Eclipse, Visual Studio etc. Pentru a vedea sistemele de build suportate pe o anumită platformă, puteți rula ''cmake -help'' (secțiunea ''Generators''). | ||
Line 95: | Line 95: | ||
Fișierele generate se vor afla în directorul de unde a fost rulat CMake. Este recomandat ca acesta să fie diferit de cel în care se află sursele proiectului. | Fișierele generate se vor afla în directorul de unde a fost rulat CMake. Este recomandat ca acesta să fie diferit de cel în care se află sursele proiectului. | ||
- | ====== Introducere in LCPL ====== | + | ====== Introducere în LCPL ====== |
În această secțiune vom prezenta un subset al limbajului LCPL, atât cât este necesar pentru rezolvarea exercițiului din laborator. Descrierea completă a limbajului o veți primi în prima temă. | În această secțiune vom prezenta un subset al limbajului LCPL, atât cât este necesar pentru rezolvarea exercițiului din laborator. Descrierea completă a limbajului o veți primi în prima temă. | ||
Line 221: | Line 221: | ||
Cunvinte cheie: ''class, inherits, if, then, else, end, new, var''. | Cunvinte cheie: ''class, inherits, if, then, else, end, new, var''. | ||
- | ====== Exerciții de laborator (10p) ====== | + | ====== Exerciții de laborator (13p) ====== |
În rezolvarea laboratorului folosiți arhiva de sarcini {{ :cpl:labs:lab03_mini_lcpl_parser.zip | lab03_mini_lcpl_parser.zip }} | În rezolvarea laboratorului folosiți arhiva de sarcini {{ :cpl:labs:lab03_mini_lcpl_parser.zip | lab03_mini_lcpl_parser.zip }} | ||
- | Pornind de la scheletul parserului pentru LCPL aflat in directorul ''mini-lcpl-parser'', va trebui să completați acest parser astfel incât să poată parsa următorul cod LCPL: | ||
- | <code> | + | ==== Conținutul mini-lcpl-parser ==== |
- | # Factorial class | + | În directorul ''mini-lcpl-parser'' există două sub-directoare: |
- | class Factorial | + | * **lcpl-AST** |
- | fact Int n -> Int : | + | * conține structurile de date ale arborelui sintactic, precum și codul care serializează nodurile din arbore. Pe parcursul exercițiului veți construi noduri din arborele sintactic folosind aceste structuri. |
- | if n < 1 then | + | * pentru a compila biblioteca **lcpl-AST** intrați in directorul lcpl-AST si executati urmatorii pasi: |
- | 1; | + | * mkdir build; cd build; cmake ..; make |
- | else | + | * în subdirectorul **include/** sunt definițiile claselor care compun AST-ul |
- | n * [fact n-1]; | + | * cui corespund aceste clase ? Gândiți-vă cum ați structurat gramatica în laboratorul trecut și de ce informații ați avea nevoie, informații pe care să le preluați în momentul executării unei acțiuni asociate unei reguli |
- | end; | + | * hint: cum ați ales neterminalii în momentul în care ați gandit logica gramaticii ? Urmând acest fir, cum ar trebui să fie "traduși" acești neterminali în AST: ar trebui să apară în AST sau nu ? |
- | end; | + | * **lcpl-parser** |
- | end; | + | * conține un schelet al parserului |
+ | * pentru a compila parserul intrați in directorul lcpl-parser și dați comanda ''make''. În urma compilării rezultă executabilul ''lcpl-parser'' | ||
+ | * tot in acest director veți găsi două foldere, unul cu teste pentru fiecare exercițiu exemple, precum și rezultatul de referință. Pentru a genera fișierul .ast, rulați ''./lcpl-parser path_fisier'' | ||
+ | * pentru ușurință puteți să folositi scriptul compare.sh, ce generează fișierul .ast și îl compară cu referința. (ex: ./compare.sh basic sau ./compare.sh all, pentru rularea tuturor testelor) | ||
+ | * pe măsură ce preluați câte un exercițiu pe care trebuie să îl rezolvați, gândiți-vă la următorii pași: | ||
+ | * tokenii pe care trebuie să îi prelucrez sunt generați de analizorul lexical ? | ||
+ | * dacă da, continuați | ||
+ | * dacă nu, modificați în consecință fișierul lcpl.l și/sau lcpl.y pentru a recunoaște noii tokeni **și** tipul lor | ||
+ | * analizorul sintactic (gramatica) conține reguli conform cu ceea ce vreau să interpretez ? | ||
+ | * dacă da, acțiunile asociate sunt cele pe care le urmăresc ? Sunt complete ? | ||
+ | * dacă nu, găsiți **unde** se potrivesc noile reguli în cadrul gramaticii, conform sintaxei LCPL descrise în documentația laboratorului | ||
+ | * puteți modifica regulile existente cum credeți de cuviință, sunt scrise pentru a vă ghida, dar nu toate sunt complete | ||
+ | * în momentul scrierii acțiunii asociate unei reguli, luați în calcul următoarele: | ||
+ | * ce reprezintă elementele regulii curente ? o expresie, o propoziție, o adunare, o grupare, o instanțiere ? după caz, conform limbajului de intrare | ||
+ | * care va fi rezultatul expresiei curente și cum o evaluez ? Tipul asociat este recunoscut in cadrul analizorului ? Unde trebuie definit acest tip ? | ||
+ | * cum arată un nod în cadrul AST-ului care va reprezenta regula/elementele regulii curente ? În cazul scheletului de laborator, căutați aceste clase in directorul lcpl-AST/include/, ele sunt deja implementate | ||
- | # Main class | ||
- | class Main inherits IO | ||
- | var | ||
- | Factorial f = new Factorial; | ||
- | end; | ||
- | | ||
- | main : | ||
- | [out [f.fact 10]]; | ||
- | end; | ||
- | end; | ||
- | </code> | ||
- | ==== Conținutul mini-lcpl-parser ==== | + | |
- | În directorul ''mini-lcpl-parser'' exisă două sub-directoare: | + | Ierarhia de clase care reprezintă noduri ale AST-ului este formată dupa cum urmează (din lcpl-AST/include/): |
- | * lcpl-AST conține structurile de date ale arborelui sintactic precum și codul care serializează nodurile din arbore. Pe parcursul exercițiului veți construi noduri din arborele sintactic folosind aceste structuri. Pentru a compila biblioteca lcpl-AST intrați in directorul lcpl-AST și dați comenzile ''cmake .'' urmată de ''make''. | + | |
- | * lcpl-parser conține un schelet al parserului. Pentru a compila parserul intrați in directorul lcpl-parser și dați comanda ''make''. În urma compilării rezultă executabilul ''lcpl-parser''. Tot in acest director veți găsi două exemple ''simple.lcpl'' si ''factorial.lcpl'', precum si fișierele ''.ast'' care corespund acestora. Pentru a genera fișierul simple.lcpl.ast, rulați ''./lcpl-parser simple.lcpl''. | + | |
+ | {{ :cpl:labs:ierarhie_clase_lcpl.png?450 }} | ||
+ | |||
+ | Ulterior rulării "./lcpl-parser simple.lcpl", urmăriți cum a fost parsat conținutul fișierului simple.lcpl și AST-ul rezultat (pe acesta îl găsiți în simple.lcpl.ast). | ||
+ | <note tip>Observați legătura dintre clasele reprezentând nodurile AST-ului și elementele gramaticii definite în Bison.</note> | ||
==== Analizorul lexical ==== | ==== Analizorul lexical ==== | ||
Va trebui să completați analizorul lexical astfel încât să recunoască și să paseze către analizorul sintactic următorii atomi lexicali: | Va trebui să completați analizorul lexical astfel încât să recunoască și să paseze către analizorul sintactic următorii atomi lexicali: | ||
Line 262: | Line 269: | ||
* operatorul logic < | * operatorul logic < | ||
* operatorii binari * și - | * operatorii binari * și - | ||
+ | * operatorul de asignare = | ||
+ | * operatorul de atribuire al tipului de retur din funcție -> | ||
+ | <note>Luați în calcul implicațiile faptului că '-' este atât operator, cât și parte a sintaxei de definire a unei metode</note> | ||
+ | **Țineți cont de faptul că o parte dintre ei sunt deja implementați. Trebuie doar să îi căutați pe cei care lipsesc și să adăugați codul corespunzător.** | ||
+ | |||
==== Analizorul sintactic ==== | ==== Analizorul sintactic ==== | ||
- | Analizorul sintactic va trebui extins astfel încât sa poată parsa: | ||
- | * operatorul ''new'' | ||
- | * atribute inițializate | ||
- | * definiții de metode cu parametri formali | ||
- | * operatorii binari '-' (expresie aditivă) si '*' (expresie multiplicativă) | ||
- | * apeluri de metode cu argumente | ||
- | * apeluri de metode cu obiect pe care se apelează metoda (dispatch) | ||
- | * definiții de clase care moștenesc alte clase | ||
- | Va trebui sa construiți urmatoarele noduri din arborele sintactic: | + | Pentru fiecare nod nou adaugat urmăriți modul de inițializare al acestora din folderul lcpl-AST/include. |
- | * Class | + | |
- | * Attribute | + | ==== Exercițiul 1 (3p) ==== |
- | * Method | + | |
- | * FormalParam | + | Pentru operatorii '-', '*', '<' și metode cu valoare de retur va trebui să adaugați reguli de parsare și noi noduri, de tipul 'BinaryOperator', respectiv 'Method', în arbore. De asemenea, adaugați regula de parsare . Urmăriți exemplul pentru operatorul '+', respectiv metoda fara valoare de retur, și indicațiile marcate cu "TODO 1". |
- | * Block | + | |
- | * IntConstant | + | ==== Exercițiul 2 (3p) ==== |
- | * BinaryOperator | + | |
- | * Dispatch | + | Pentru statementul if va trebui să adaugați o nouă regulă de parsare și nodul aferent, de tipul 'IfStatement', în arbore. Pentru simplitate regula va trebui să evalueze doar structuri de forma: if <cond> then <expr> else <expr> end. De asemenea va trebui să definiți tipul intors de această regulă. Urmăriți indicațiile marcate cu "TODO 2". |
- | * NewObject | + | |
+ | ==== Exercițiul 3 (4p) ==== | ||
+ | |||
+ | Adaugați reguli de parsare și nodurile aferente pentru apelul de funcție ('Dispatch') de forma : [id expr1 expr2 ...], de asemenea va trebui să definiți tipul intors de aceste reguli. Urmăriți indicațiile marcate cu "TODO 3". | ||
+ | |||
+ | ==== Exercițiul bonus (4p) ==== | ||
+ | |||
+ | Extindeți exercițiul anterior, astfel încât gramatica sa accepte asignarea variabilelor și moștenirea unei clase. Urmăriți indicațiile marcate cu "TODO BONUS". |