8. Cvičenie - Implementácia a generovanie jazykových procesorov (Úloha 5)

Ciele
  1. Rozšíriť jazyk entít o podporu vzťahov medzi entitami.
  2. Navrhnúť konkrétnu syntax v textovej forme pre rozšírený jazyk entít.
  3. Oboznámiť sa s anotáciami.
  4. Oboznámiť sa s generátorom jazykových procesorov YAJCo.
  5. Upraviť model jazyka entít a obmedzení pre potreby generovania jazykového procesora externého jazyka.
  6. Pridať kontrolu jedinečnosti mien entít a vlastností.
  7. Implementovať jazyk entít a jazyk obmedzení prostredníctvom generátora jazykových procesorov.
  8. Otestovať funkčnosť jazykového procesora vytvoreného generátorom jaz. procesorov YAJCo.
Úvod

    Našim nasledujúcim cieľom bude umožniť vo vytváranom jazyku definovať vzťahy medzi entitami. Príkladom vzťahu je zamestnanec pracujúci v konkrétnom oddelení, bankový účet patriaci osobe alebo položka príslušnej faktúry. Pre tento účel pridáme do navrhnutého jazyka entít referencie v podobnom štýle ako je to v relačných databázových systémoch.

    Referencia je orientovaný vzťah (má definovaný zdroj from a cieľ to) a prepája práve 2 entity. Entita obsahuje zoznam referencií na iné entity outgoingReferences. Diagram tried vzťahu entít a referencií je zobrazený na nasledujúcom obrázku.

    Obr.: Diagram tried vzťahu entít a referencií
    V tomto projekte teda budeme využívať len referenciu typu 1:n.

    Zároveň spojíme jazyky entít a obmedzení do jedného jazyka. Jednoriadkový jazykový procesor vytvorený v predchádzajúcich cvičeniach nahradíme procesorom, ktorý bude generovaný generátorom jazykových procesorov YAJCo. Vygenerovaný jazykový procesor Parser teda v tejto fáze úplne nahradí funkciu tried LineParser a ConstraintBuilder vytvorených v predchádzajúcich úlohách.

Postup
  1. Rozšírenie metamodelu o uvedenú triedu pre určenie referencií je v metamodel3.zip. V nasledujúcich úlohách rozšírime model jazyka entít o podporu vzťahov prostredníctvom referencií.
    Úloha: Pridajte do triedy Model premennú objektu private Reference[] references, ktorá bude slúžiť na evidenciu všetkých referencií medzi entitami.


    Pošli
    Úloha: Pridajte do triedy Model ďalší konštruktor public Model(Entity[] entities, Reference[] references) pre uloženie referencií do objektu.


    Pošli
    Úloha: Pridajte do triedy Model metódu Reference[] getReferences(), ktorej návratovou hodnotou je zoznam všetkých referencií modelu.


    Pošli
    V nasledujúcich úlohách do triedy Entity pridáme podporu pre získanie zoznamu entít, s ktorými je entita vo vzťahu.
    Úloha: Pridajte do triedy Entity premennú objektu private List<Reference> outgoingReferences, ktorá bude slúžiť na evidenciu vzťahov s touto entitou (napr. entita Zamestnanec má vzťah s entitou Oddelenie), ktoré reprezentujeme prostredníctvom referencií vychádzajúcich z entity.


    Pošli
    Úloha: Pridajte do triedy Entity metódu Reference[] getOutgoingReferences(), ktorej návratovou hodnotou je pole všetkých referencií vychádzajúcich z entity.


    Pošli
    Úloha: Pridajte do triedy Entity metódu void addOutgoingReference(Reference reference) na pridanie referencie, ktorá vychádza z danej entity.


    Pošli
    Úloha: Upravte v triede Reference metódu void setFrom(Entity from) tak, aby zároveň bola daná referencia pridaná do zoznamu referencií entity určenej parametrom from.


    Pošli
  2. V tomto kroku spojíme jazyk entít a jazyk obmedzení do jedného jazyka, pre ktorý vytvoríme procesor prostredníctvom generátora jazykových procesorov.
    Konkrétna syntax výsledného jazyka entít (spolu s jazykom obmedzení a podporou referencií) má nasledujúcu podobu:

    • Model systému - pozostáva z postupnosti definície entít a postupnosti definície referencií.
    • Entita - je definovaná vymenovaním vlastností v tvare 'entity' <NAME> '{' Property1 ... Propertyn '}' a má jedinečné meno.
    • Vlastnosť - je zapísaná v tvare <NAME> ':' Type [Constraint1 ',' ... ',' Constraintn] a má jedinečné meno v rámci entity.
    • Referencia - je zapísaná v tvare: 'reference' <NAME> <NAME> kde prvý identifikátor určuje meno zdrojovej a druhý cieľovej entity.

    Konkrétna syntax jazyka vyjadrená prostredníctvom EBNF je nasledovná.
    
          Model ::=  Entity+ Reference*
         Entity ::=  'entity' <NAME> '{' Property+ '}'
       Property ::=  <NAME> ':' Type (Constraint (',' Constraint)*)?
           Type ::=  'integer' | 'real' | 'string'
     Constraint ::=  Required | Length | Range | Regex 
       Required ::=  'required'
         Length ::=  'length' <INT_VALUE> <INT_VALUE>
          Range ::=  'range' <INT_VALUE> <INT_VALUE>
          Regex ::=  'regex' <STRING_VALUE>
      Reference ::=  'reference' <NAME> <NAME>
    
    V apostrofoch sú uvedené neparametrizované lexikálne symboly, ktoré slúžia ako oddeľovače jednotlivých jazykových konštrukcií (napr. 'entity', 'real', '{'). V ostrých zátvorkách sú uvedené lexikálne symboly podstatné pre abstraktnú syntax a sémantiku (napr. <NAME>, <INT_VALUE>, <STRING_VALUE>).
    Príklad vety z uvedeného jazyka je v model.el.
  3. Úloha: Preštudujte si materiály o anotáciách a generátore jazykových procesorov YAJCo a príklady uvedené v nich.
    Využite ich pri plnení nasledujúcich krokov.


    Pošli
  4. V tomto kroku bude pridaný generátor jazykových procesorov YAJCo do projektu.
    Úloha: Vložte do projektu obsah súboru parser1.zip. Súbor obsahuje konfiguračný súbor package-info.java pre jazykový procesor YAJCo a obsahuje anotáciu @Parser pre model jazyka entít. Oboznámte sa s obsahom súboru package-info.java a definovanými lexikálnymi jednotkami.


    Pošli
    Poznámka: Skontrolujte, či máte nastavenú závislosť na knižniciach nástroja Yajco v súbore pom.xml v module magsa-generator, kde by sa v elemente <dependencies> mali nachádzať nasledovné nastavenia:
    
    <dependency>
        <groupId>sk.tuke.yajco</groupId>
        <artifactId>yajco-annotation-processor</artifactId>
        <version>0.5.9</version>
    </dependency>
    <dependency>
        <groupId>sk.tuke.yajco</groupId>
        <artifactId>yajco-javacc-parser-generator-module</artifactId>
        <version>0.5.9</version>
    </dependency>
    
    Ladenie: Od tohto momentu po každej zmene gramatiky (anotácií) bude nutné vykonávať nad projektom dva kroky:
    • clean + compile - pre vygenerovanie jazykového procesora pomocou nástroja yajco,
    • compile - pre preklad projektu magsa-generated s použitím vygenerovaného jazykového procesora.
    Ak sa gramatika (anotácie) nemení, stačí projekt len kompilovať (druhý krok). Pre viac informácií o uvedených krokoch si zopakujte informácie o nástroji Maven.
    Generátor YAJCo je implementovaný ako štandardný anotačný procesor jazyka Java. Jeho použitie je teda totožné s ostatnými anotačnými procesormi.
  5. Pre potreby špecifikovania konkrétnej syntaxe je nutné upraviť triedy modelu jazyka entít a obmedzení - pridanie vhodných konštruktorov, pridanie anotácií generátora.
    Úloha: Pridajte do triedy Property konštruktor public Property(String name, Type type, Constraint[] constraints) { ... }, ktorý umožňuje definovať obmedzenia pre vlastnosť.


    Pošli
    Poznámka: Konštruktory, ktoré nemajú byť spracované procesorom, označte pomocou anotácie @Exclude. Takéto konštruktory sú napríklad Model(Entity[] entities) a Property(String name, Type type).
    Úloha: Označte jednotlivé konštruktory v modeli prostredníctvom uvedených anotácií generátora YAJCo tak aby definovaná konkrétna syntax zodpovedala zápisu uvedenému v bode 2.


    Pošli
    Úloha: Vyznačte v triede Entity jednoznačný identifikátor entity prostredníctvom anotácie @Identifier.


    Pošli
    Úloha: Vyznačte v triede Property jednoznačný identifikátor vlastnosti entity prostredníctvom anotácie @Identifier.


    Pošli
    Poznámka: Zatiaľ čo meno entity musí byť jedinečné v celej vete z jazyka entít, meno vlastnosti entity musí byť jedinečné len pre entitu. Dve rôzne entity teda môžu obsahovať vlastnosti s rovnakým menom. Prispôsobte teda podľa tejto podmienky aj parameter unique v anotácie @Identifier.
    Úloha: V konštruktore triedy Reference použite anotáciu @References pre určenie automatického vytvárania referencií.


    Pošli
    Ladenie: Pri akejkoľvek chybe pri preklade je možné skontrolovať gramatiku vygenerovaného jazykového procesora vo vygenerovanom súbore sk.tuke.magsa.tools.parserext.javacc.grammar.ebnf v adresári target/generated-sources/annotations projektu generátora.
  6. Generátor jazykových procesorov YAJCo vygeneruje na základe anotovaného modelu jazyka jazykový procesor a triedu výnimky (všetko v balíku sk.tuke.magsa.tools.parserext v adresári target/generated-sources/annotations projektu generátora). Tento procesor sa bude nachádzať v balíku, ktorý je definovaný anotáciou @Parser prostredníctvom názvu triedy jazykového procesora. Vygenerovaný jazykový procesor obsahuje dve základné metódy na spracovanie vstupu (<T> je typ hlavnej triedy jazyka):
    
    <T> parse(String text) throws ParseException;
    
    <T> parse(java.io.Reader reader) throws ParseException;
  7. Po vygenerovaní jazykového procesora so správnou gramatikou otestujte jeho funkčnosť na spracovanie viet jazyka zo súboru model.el:
    
    //... other imports
    
    import sk.tuke.magsa.tools.parserext.Parser;
    
    public class Make {
        public static void main(String[] args) throws Exception {
            Model model;
    
            //... pôvodný spôsob spracovania modelu a obmedzení môžete zakomentovať
        
            Parser parserext = new Parser();
            model = parserext.parse(new FileReader("model/model.el"));
    
            System.out.println(model);
        
            //... povôdný obsah generovania
        }
    
        //...
    }        
    
    Poznámka: Vygenerovaný jazykový procesor Parser teda v tejto fáze úplne nahradí funkciu tried LineParser a ConstraintBuilder.
  8. Vymažte súbory z disku a zrušte všetky vami vytvorené nastavenia vo vývojovom prostredí!
Zdroje
  1. Viac o generátore jazykových procesorov YAJCo:
  2. Zdrojové kódy:
    • metamodel3.zip - rozšírenie metamodelu o triedy referencií
    • parser1.zip - konfiguračný súbor pre balík jazykového procesora
  3. Knižnice:
    • yajco.jar - knižnica generátora jazykových procesororv YAJCo
    • javacc.jar - knižnica generátora jazykových procesorov javacc
  4. Príklad vety v novom jazyku entít:
    • model.el - vložte do adresára resources/model modulu magsa-generator
comments powered by Disqus