Tema de casă 1 - Analiza sintactică

Î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] …..

Informaţii organizatorice

  • Deadline: Termenul limită până când se pot trimite temele fără depunctări de întârziere este luni, 15 ianuarie 2018, ora 23:59. Pentru mai multe detalii, consultaţi regulamentul aferent temelor de casă.
  • Colaborare: Tema va fi rezolvată individual.
  • Punctare: 125p, din care
    • 100p pentru implementarea completă şi corectă a analizei sintactice
    • 25p pentru implementarea operațiilor pe vectori în limbajul LCPL

Enunţ

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.

Analiza lexicală

În cadrul analizei lexicale, va trebui să urmăriţi următoarele puncte:

  • Să scrieți o specificație Bison în care să definiți tokenii lexicali pe care îi va primi la intrare analizorul sintactic precum și valorile semantice ale acestora.
  • Să scrieți o specificație Flex folosind expresii regulate și tokenii lexicali ai limbajului LCPL şi să generaţi codul care scanează fişierul sursă.
  • Să pasați analizorului sintactic tokenii rezultați in urma analizei lexicale.

Analiza sintactică

În cadrul analizei sintactice, va trebui să urmăriţi următoarele puncte:

  • Să scrieți în specificația Bison regulile care descriu structura sintactică a limbajului LCPL și să generați codul care primește la intrare un șir de tokeni lexicali si furnizează la ieșire nodul rădăcină al unui Abstract Syntax Tree, care va fi procesat mai departe în cadrul analizei semantice.
  • Să evitaţi regulile ambigue (care să poată genera pentru acelaşi input AST-uri diferite).
  • Să detectaţi şi să afişaţi erorile de natură sintactică.
  • Nu trebuie să trataţi acum erorile de natură semantică, de exemplu incompatibilitatea tipurilor operanzilor unei expresii sau parametrii actuali transmişi unei metode diferind ca număr şi tip de cei formali.

Formatul datelor

Î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!"
                  }
                ]
              }
            ]
          }
        }
      ]
    }
  ]
}

Prezentarea arhivei

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 sintactic
  • lcpl-AST/src - implementarea funcționalității de serializare în format JSON
  • test - trei programe simple scrise in LCPL
  • CMakeLists.txt - Folosită pentru generarea Makefile-urilor continand secvența de comenzi necesară pentru a compila biblioteca ce oferă arborele sintactic și parserul
  • README - 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 LCPL
  • lcpl.y- specificația Bison a analizorului sintactic pentru LCPL
  • lcpl-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ă.

Vectori

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.

  • declararea unui vector de siruri de caractere, inițializat cu null.
 String[] myArray;
  • funcție ce are ca parametru un vector și întoarce un vector.
 init sort String[] anArray -> String[];
  • instanțierea unui nou obiect de tip vector:
 myArray = new String[8];
  • modificarea unui element de la un anumit index:
 myArray[3] = "Hello";
  • accesarea unui element de la un anumit index:
 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.

Exemplu de utilizare

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;

Implementare

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.

Cum limbajul LCPL nu suportă implicit operații pe obiecte de tip vector, va trebui să vă definiți o clasă ajutătoare pentru a simula aceste funcționalități; puteţi apoi ca în timpul parsării să adugaţi codul acestei clase la fişierul LCPL primit la intrare. Clasa trebuie implementată folosind doar facilităţile oferite de limbajul LCPL de bază.

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 şi notarea

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:

  • Testele publice (80p)
    • Testele simple (40p)
    • Testele advanced (30p)
    • Testele complex (10p)
  • Calitatea implementării (20p)
    • Organizarea codului sursă
    • Comentariile din cod
    • Explicațiile din README - acestea trebuie să conţină o prezentare extinsă a modului de implementare a temei şi a problemelor întâmpinate pe parcurs.

La punctajele de mai sus se adaugă 25 puncte dacă operațiile pe vectori sunt implementate complet și parserul trece toate testele.

Change Log

FAQ

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.

Download

  • Manualul limbajului LCPL.
  • Arhiva de pornire pentru rezolvarea temei.
  • Set de teste conținând fișiere LCPL și AST-ul corespunzător.
cpl/teme/tema1.txt · Last modified: 2017/11/28 05:51 by bogdan.nitulescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0