• Nie Znaleziono Wyników

Rozdział 3. Język zapytań

3.14 Funkcje

Wywołanie funkcji jest opisane składnią:

[<pełna kwalifikowana nazwa klasy>.]<nazwa metody>[::<modyfikator metody>] ([<lista argumentów>])

Gdzie:

<pełna kwalifikowana nazwa klasy> – jeżeli jest wywoływana metoda klasowa,

wtedy konieczne jest podanie pełnej ścieżki do klasy zgodnie z konwencją zdefiniowaną w języku Java.

<nazwa metody> –jest identyfikatorem metody. Dopuszczalna składnia nazwy jest

podana w dodatku A.

<modyfikator metody> – jest listą literałów oddzielonych przecinkiem. Modyfikatory

wywołania metody informują o jej specjalnym użyciu. Obecnie zdefiniowano modyfikator task sygnalizujący, że w wyniku wywołania metody powstaje obiekt typu Task.

<lista argumentów> – jest listą operandów oddzielonych znakiem przecinka.

Wyróżniamy metody obiektowe oraz metody klasowe. Metody klasowe związane są z klasą w której są zdefiniowane. Wywołania tego typu metod zawierają pełną kwalifikowaną nazwę klasy. Przykładami wywołań dla tej grupy metod są:

gui.show::task(result[true]), Agg.min($I.valF) i Set.distinct(I{}, "val"). Metody

obiektowe są związane symbolami. Tutaj wyszukiwanie symboli jest realizowane przez algorytm obsłgujący przestrzenie nazw. Przykładami użycia tych metod są wywołania: select out{…, addPK(id)}, where II{slideWindow(800)} i setGlobal(out{}).

Zauważmy, że wyszukiwanie metody obiektowej lub klasowej jest podobne do składni języka Java. Cecha ta sprawia, że w tym zakresie można w pełni skorzystać z implementacji refleksji języka Java. Niestety definicja metod w języku Java jest zbyt uboga aby analizator semantyczny języka StreamAPAS mógł tylko na niej bazować. Do brakujących elementów definicji zalicza się:

1) Przy użyciu mechanizmu refleksji mamy dostęp do wszystkich metod zdefiniowanych w klasie. Jednak nie wszystkie metody zdefiniowane dla symboli, powinny być udostępniane z poziomu języka zapytań. W definicji metody w języku Java brak informacji o tym czy jest ona przeznaczona do udostępniania z poziomu języka zapytań; istnieją jedynie modyfikatory:

private, public, protected, default definiujące widoczność metod na poziomie

języka programowania.

2) Tabela symboli zawiera meta-typy oraz typy. Przykładowo meta-typem jest interfejs VarSymbolI służący do deklaracji zmiennej. Z kolei typ zmiennej jest

definiowany przez metodę getSymbolClass. Jeżeli metoda realizuje operację arytmetyczną, wtedy typem operandu jest typ zmiennej. Jeżeli metoda implementuje fragment analizy semantycznej, wtedy wymagany jest dostęp do deklaracji zmiennej, czyli symbolu definiującego zmienną. Metody w języku Java nie rozróżniają operacji na poziomie meta-typów i typów, w związku z tym konieczne jest rozszerzenie obsługi mechanizmu refleksji.

3) Przyjrzyjmy się teraz typowi zwracanemu przez metodę w języku StreamAPAS. W kompilatorze typ zwracany przez metodę jest opisany przez strukturę symboli podczas gdy typem wynikowym metody w języku Java jest klasa. Jeżeli zdefiniujemy w języku Java metodę arytmetyczną wyliczającą sinus dla argumentu typu double, jej deklaracja będzie miała postać:

double sin(double aVal) {….}

Pytanie pojawia się jak zdefiniować w języku Java metodę, która zwraca jako wynik drzewo atrybutów.

4) Należy zauważyć, że w języku StreamAPAS metody realizują operacje na dwóch poziomach abstrakcji. Metody przekształcają analizę semantyczną, jak również definiują proces przetwarzania danych. W wyniku analizy składniowej powstaje drzewo rozbioru składniowego, składające się z węzłów reprezentujących operacje. Na rys. 3.12 przedstawiono przykładowe drzewo rozbioru składniowego składające się z listy zdań reprezentowanych przez węzeł stmt seq. Jednym ze zdań jest operacja zdefiniowana przez węzeł S1. Jeżeli węzeł S1 jest metodą przekształcającą analizę semantyczną, wtedy w wyniku jej uruchomienia powstaje nowy węzeł S2 w miejscu S1. Zauważmy, że drzewo rozbioru składniowego dołączone do węzła S1 jest traktowane jako zmienna, którą można przekształcać. Jeżeli węzeł S1 reprezentuje metodę przetwarzania, wtedy w wyniku jej uruchomienia powstaje wartość, która jest przekazywana operatorowi stmt seq. Metody w języku Java nie operują na poziomie analizy semantycznej, oznacza to że należy rozszerzyć definicję metody o informację na jakim poziomie abstrakcji działa metoda.

stmt seq S1 + x x int 1 stmt seq S2 + x int 1

Rys. 3.12. Przekształcanie drzewa rozbioru składniowego

Aby usunąć powyższe ograniczenia zastosowano mechanizm adnotacji. Język Java pozwala rozszerzyć definicje klasy o dodatkowe komentarze dołączane do deklaracji klas, metod, zmiennych klasowych i obiektowych. Komentarz ten nazywano adnotacją, a odczyt jego wartości jest zbliżony do obsługi interfejsu. Istotny z punktu widzenia rozwijanego kompilatora jest fakt, że adnotacja pozwala zdefiniować własne modyfikatory oraz udekorować nimi metody oraz definicje klas. Adnotacja jest elementem definicji języka Java, oznacza to że poprawność wykorzystania tej techniki jest sprawdzana przez kompilator języka Java, co jest dodatkowym atutem. Ponadto korzystanie z tego rozwiązania jest poręczne, ponieważ nie ma konieczności tworzenia dodatkowych plików konfiguracyjnych.

Usunięcie ograniczenia pierwszego zrealizowano poprzez system adnotacji. Na poziomie klasy zdefiniowano adnotację blokującą dostęp do wszystkich metod składowych; a na poziomie metod zdefiniowano adnotacje oznaczającą ukrycie lub uwidocznienie metody. Użytkownik może zdefiniować dostępność metod na dwa sposoby. Pierwsze podejście polega na ukryciu wszystkich metod dodając adnotację

@CModificator(mode = CModificator.HIDEMEMBERS) na poziomie klasy. Następnie

metody, które mają zostać uwidocznione są oznaczone adnotacją

@MModificator(mode=MModificator.ALLOW). Drugie podejście polega na udostępnieniu

na poziomie klasy wszystkich metod oraz ukrywaniu wybranych przy użyciu adnotacji @MModificator(mode=MModificator.HIDEN). Gdy opisany system adnotacji jest pomijany, wtedy każda metoda jest dostępna z poziomu języka zapytań. Własność ta jest przydatna, ponieważ pakiety z operacjami arytmetycznymi, które nie były tworzone z myślą o zastosowaniu w języku StreamAPAS mogą być swobodnie obsługiwane. Przykładem jest pakiet Math.

Ograniczenie drugie rozwiązano wprowadzając adnotację

MModificator.CUSTOM_OP_BASE. Informuje ona, że pierwszym argumentem metody jest

reprezentujących definicję zmiennych istnieje tylko w trakcie analizy semantycznej. Adnotacja ta zatem przekazuje dodatkowo informację, że metoda ta rozszerza analizę semantyczną. Użycie adnotacji MModificator.CUSTOM_OP_BASE wiąże się również z inną interpretacją wyniku zwracanego przez metodę. Metoda adnotowana może nie zwracać wyniku lub zwracać obiekt reprezentujący węzeł drzewa rozbioru składniowego. Kompilator w trakcie analizy semantycznej uruchamia metodę adnotowaną MModificator.CUSTOM_OP_BASE i jeżeli ma miejsce przypadek drugi, wtedy węzeł reprezentujący wywołanie metody jest zastępowany węzłem zwróconym przez wywołaną metodę. Dzięki takiemu rozwiązaniu usunięte jest również ograniczenie czwarte. Zauważmy, że przy użyciu metody adnotowanej

MModificator.CUSTOM_OP_BASE można skonstruować funkcję zwracającą złożoną

strukturę symboli jako wynik. Oznacza to, że tego typu metody usuwają także ograniczenie trzecie.

Na zakończenie prześledźmy zastosowanie powyższych adnotacji na przykładzie metody setGlobal zdefiniowanej dla obiektu Unit. Metoda ta przenosi definicję fabryki danych z przestrzeni publicznej do przestrzeni globalnej. Jej deklaracja ma postać:

@MModificator(mode=MModificator.CUSTOM_OP_BASE | MModificator.ALLOW) public void setGlobal(OperatorBase[] aOperatorBase, DatasetI aDataset) {

Tylko metoda setGlobal dla obiektu Unit jest dostępna z poziomu języka zapytań, dlatego klasa Unit jest adnotowana przez @CModificator(mode =

CModificator.HIDEMEMBERS), z kolei metoda setGlobal jest adnotowana przez

MModificator.ALLOW. Dodatkowo zastosowano adnotację

MModificator.CUSTOM_OP_BASE, ponieważ metoda ta wymaga dostępu do definicji

zmiennej. Zauważmy, że z poziomu języka zapytań lista argumentów metody

setGlobal składa się wyłącznie z aDataset. Pierwszy argument jest pomijany,

ponieważ jego wartość jest dołączana automatycznie przez kompilator w trakcie analizy semantycznej. Jak widzimy połączenie mechanizmów adnotacji i reflekcji sprawia, że składnia języka zapytań automatycznie się aktualizuje wraz z dodaniem nowych funkcjonalności do obiektów rozbioru składniowego.

Podsumowując, przy użyciu adnotacji tworzących system udostępniania metod oraz adnotację powiadamiającą o uruchomieniu metody jako element analizy semantycznej, usunięto wszystkie ograniczenia mechanizmu refleksji.

3.15 Reprezentacja indeksów oraz operacji na hurtowniach