În cadrul acestei teme de casă veţi implementa partea de analiză lexicală şi sintactică a unui compilator pentru limbajul LCPL descris mai jos.
Pentru orice nelămuriri cu privire la această temă, puteţi posta un mesaj pe lista de discuţii, de preferinţă având subiectul mesajului de forma [Tema1] ….
.
Va trebui să realizaţi în limbajul C++, folosind Flex și Bison, porţiunea responsabilă cu analiza lexicală şi sintactică a codului sursă pentru un compilator al limbajului LCPL. Programul vostru va trebui să primească la intrare codul sursă LCPL şi să furnizeze la ieşire informaţii (un AST) pentru analiza semantică.
Documentaţia principală în cadrul acestei teme va fi manualul limbajului LCPL. Rezultatul programului realizat de voi va fi folosit de restul compilatorului în temele următoare.
În cadrul analizei lexicale, va trebui să urmăriţi următoarele puncte:
În cadrul analizei sintactice, va trebui să urmăriţi următoarele puncte:
În urma compilării temei, va trebui să rezulte un fişier executabil bin/lcpl-parser
, ce va conţine funcţia main()
asociată programului. Programul va citi de la linia de comandă numele unui fişier LCPL, pe care îl va procesa, şi numele unui fişier în care se va scrie AST-ul. Pentru a formata corespunzător AST-ul, vă punem la dispoziţie o serie de clase ce descriu un AST; acestea vor fi serializate în format JSON, folosind biblioteca rapidjson, ce se găseşte în arhivă. Secţiunea următoare prezintă conţinutul arhivei cu codul pe care va trebui să-l extindeţi/folosiţi.
Exemplu:
class Main inherits IO main : [out "Hello world!"]; end; end;
La ieşire nu vă interesează atât de mult formatul, pentru că aveţi în arhivă codul care generează acest text:
{ "NodeType": "Program", "NodeID": 0, "LineNumber": 1, "Classes": [ { "NodeType": "Class", "NodeID": 1, "LineNumber": 1, "Name": "Main", "Parent": "IO", "Features": [ { "NodeType": "Method", "NodeID": 2, "LineNumber": 2, "Name": "main", "ReturnType": "Void", "Body": { "NodeType": "Block", "NodeID": 3, "LineNumber": 3, "Expressions": [ { "NodeType": "Dispatch", "NodeID": 4, "LineNumber": 3, "Name": "out", "Arguments": [ { "NodeType": "StringConstant", "NodeID": 5, "LineNumber": 3, "Value": "Hello world!" } ] } ] } } ] } ] }
Pentru a rezolva această temă va trebui să porniţi de la această arhivă.
Structura arhivei este următoarea:
bin
- directorul care va conține binarul generat (lcpl-parser
)lcpl-AST
- directorul ce conține implementarea arborelui sintactic și funcționalitatea de serializare a acestuia în format JSON.lcpl-AST/include
- definițiile claselor care compun arborele sintacticlcpl-AST/src
- implementarea funcționalității de serializare în format JSONtest
- trei programe simple scrise in LCPLCMakeLists.txt
- Folosită pentru generarea Makefile-urilor continand secvența de comenzi necesară pentru a compila biblioteca ce oferă arborele sintactic și parserulREADME
- Descrieți implementarea voastră în acest fișier.Fişierele sursă prezente în arhivă sunt următoarele
lcpl.l
- specificația Flex a analizorului lexical pentru LCPLlcpl.y
- specificația Bison a analizorului sintactic pentru LCPLlcpl-AST/include/*
- câte o clasă pentru fiecare nod din AST; nodul care se va afla în rădăcina arborelui va fi de tip Program
.Puteți modifica orice fișier din aceasă arhivă, va trebui să descrieți în fișierul README aceste modificări. Nu vă este impusă o structură anume a programului atâta vreme cât programul vostru face corect analiza sintactică.
Extindeți limbajul LCPL prin adăugarea unui tip de date vector. Acest tip de date va fi folosit conform sintaxei din exemplele următoare.
String[] myArray;
init sort String[] anArray -> String[];
myArray = new String[8];
myArray[3] = "Hello";
String myValue = myArray[3];
Nu este necesar să implementați vectori de tip Int, însă va trebui să puteți avea vectori din orice tip derivat din Object, nu doar String.
Pentru definirea unui obiect de tip stivă cu ajutorul vectorilor se poate folosi următorul program LCPL extins:
class StringStack var String[] st; Int index; end; init Int size -> StringStack : st = new String[size]; self; end; # adăugarea în stivă push String o : st[index] = o; index = index + 1; end; # extragerea din stivă pop -> String : index = index - 1; st[index]; end; end;
Clasa Main folosită la testare:
class Main inherits IO var StringStack stack = [(new StringStack).init 8]; end; main : [stack.push "Hello"]; [stack.push "smile"]; [out [stack.pop][1,5]]; # "mile" end; end;
In LCPL, vectorii nu fac parte din limbajul de bază, ci sunt doar o extensie de sintaxă (Syntax Sugar).
Implementarea NU trebuie să introducă noi tipuri de noduri în AST, ci trebuie să se folosească doar de clasele și nodurile existente. Practic va trebui să simulați vectorii folosind o altă structură de date.
Sugerăm ca acest cod LCPL suplimentar scris de voi să fie plasat în arhivă într-un fișier numit src/Vector.lcpl
.
Nu uitați să introduceţi în AST noduri de tip CAST necesare pentru compatibilitatea tipurilor.
Testarea temei de casă va folosi o serie de teste ce vor fi disponibile pe vmchecker. Modul în care este distribuit punctajul pentru această temă este următorul:
simple
(40p)advanced
(30p)complex
(10p) La punctajele de mai sus se adaugă 25 puncte dacă operațiile pe vectori sunt implementate complet și parserul trece toate testele.
Q: Clasele din AST sunt documentate undeva? De unde încep?
A: De la clasa “Program”. Conține o listă de “Class”, care conține o listă de “Feature”, ce pot fi “Attribute” sau “Method” … și așa mai departe.
Pentru a înțelege mai bine ierarhia nodurilor din arbore și care sunt structurile de date corespunzătoare, puteți încărca fișierele *.ast.ref din arhiva de teste într-un viewer de JSON.
Q: Cum tratăm erorile sintactice?
A: Mesajul de eroare generat de Bison este suficient.
Q: Dacă există diferențe între teste și textul temei, care e rezultatul corect?
A: În general, dacă descoperiți diferențe între teste și manual, comportamentul din teste e cel bun. Dacă le raportați, noi vom corecta manualul și actualiza change log-ul.