Analiza semantyczna Do przeprowadzenia poprawnego tłumaczenia, oprócz informacji na temat składni języka podlegającego tłumaczeniu, translator musi posiadać możliwość korzystania z wielu innych informacji dotyczących tłumaczonych konstrukcji. Informacje te mają charakter semantyczny. Dotyczą np. typu zmiennych, typu wyrażeń, wielkości pamięci dla zmiennych, adresów odpowiednich zapisów w tablicach, wartości wyrażeń w procesie interpretacji, itd. Informacje semantyczne mają charakter atrybutów stowarzyszonych z konstrukcjami języka podlegającego tłumaczeniu. Jednym ze sposobów formalnego określenia semantyki języka i zdefiniowania akcji podejmowanych w procesie translacji jest aparat gramatyk atrybutywnych Analiza semantyczna i generowanie kodu oparte są na drzewie rozbioru syntaktycznego. Każdy wierzchołek drzewa (symbol gramatyki) jest udekorowany atrybutami opisującymi własności wierzchołka. Dla podkreślenia tego faktu takie drzewo nazywa się często atrybutowym drzewem rozbioru syntaktycznego. Informację zgromadzoną w atrybutach wierzchołka wprowadza się z jego otoczenia. Obliczenie atrybutów i sprawdzenie ich zgodności jest zadaniem analizy semantycznej. Optymalizację i generację kodu również można opisać w podobny sposób, używając atrybutów do sterowania przekształceniem drzewa i w końcu wyborem instrukcji maszynowych. Gramatyka atrybutywna Gramatyka atrybutywna jest oparta na gramatyce bezkontekstowej G = < N, T, P, Z > Łączy ona z każdym symbolem gramatyki X ( N T ) zbiór atrybutów A(X). Każdy atrybut X.a A(X) reprezentuje specyficzną własność symbolu X i może przyjąć którąkolwiek wartość z pewnego zbioru wartości. X.a oznacza atrybut a będący elementem zbioru A(X). Każdy wierzchołek w drzewie rozbioru syntaktycznego słowa z języka L(G) jest związany ze zbiorem wartości atrybutów dla tego symbolu X ze słownika gramatyki, który stanowi etykietę tego wierzchołka. Wartość atrybutów ustala się za pomocą reguł syntaktycznych. Zbiór R(p) R(p) = {Xi.a! f(xj.b,..., Xk.c)} jest zbiorem reguł semantycznych dla produkcji p P takiej, że : p = (X0 " X1...Xn) 0 i n 0 j n 0 k n Reguła semantyczna Xi.a! f(xj.b,..., Xk.c) definiuje atrybut Xi.a za pomocą atrybutów Xj.b,..., Xk.c symboli z tej samej produkcji Gramatyka atrybutywna AG jest trójką : AG = < G, A, R > gdzie : G = < N, T, P, Z > GBK - gramatyka bezkontekstowa, A =! X ( N T) A(X) A skończony zbiór atrybutów Jeśli A(X) A(Y) to X=Y
R =! p P R(p) R skończony zbiór reguł semantycznych Dla każdego wystąpienia X w drzewie rozbioru syntaktycznego słowa z języka L(G), do obliczenia każdego atrybutu X.a A(X) można zastosować co najwyżej jedną regułę semantyczną. Dla każdej produkcji p = (X0 " X1...Xn) P zbiorem definiujących wystąpień atrybutów jest: AF(p) = { Xi.a Xi.a! f(... ) R(p) } Atrybut X.a nazywamy syntetyzowanym # ( p = (X " χ ) P) (X.a AF(p)) Atrybut X.a nazywamy dziedziczonym # ( p = (Y " µxν ) P)( X.a AF(p)) -5- Tw. X ( N T ) zachodzi : AS(X) AI(X) = gdzie : AS(X) = { X.a ( p = (X " χ ) P) (X.a AF(p)) } AS(X) zbiór atrybutów syntetyzowanych dla X ( N T ) AI(X) = { X.a ( q = (Y " µxν ) P) (X.a AF(q)) } AI(X) zbiór atrybutów dziedziczonych dla X ( N T ) Ponadto istnieje co najwyżej jedna reguła X.a! f(... ) w zbiorze R(p) dla każdego p P i X.a A(X) Def. Gramatyka atrybutywna jest zupełna, jeśli dla wszystkich symboli X ( N T ) są spełnione warunki : ( p = (X " χ ) P) (AS(X) AF(p)) ( q = (Y " µxν ) P) (AI(X) AF(q)) AS(X) AI(X) = A(X) Ponadto : AI(Z) = Def. Gramatyka atrybutywna jest dobrze zdefiniowana, jeśli dla każdego drzewa rozbioru syntaktycznego słowa z L(G) wszystkie atrybuty są efektywnie obliczane.
Przykład : Tłumaczenie z notacji nawiasowej do odwrotnej notacji polskiej. Gramatyka atrybutywna : AG = < G, A, R > G = < N, T, P, Z > T = { +, *, (, ), id } N = {E, T, F } P = { E " E + T T } T " T * F F F " ( E ) id Z = E A = { E.t, T.t, F.t, id.lexptr } R = { E.t! E 1.t T.t + E.t! T.t T.t! T1.t F.t * T.t! F.t F.t! E.t T.t! text(id.lexptr) } - operator konkatenacji tekstów (stringów) text(adres) funkcja zwracająca postać identyfikatora, którego adres w tablicy symboli stanowi argument funkcji Syntax directed definition E " E 1 + T E.t! E 1.t T.t + E " T E.t! T.t T "T1 * F T.t! T1.t F.t * T " F T.t! F.t F " ( E ) F.t! E.t F " id T.t! text(id.lexptr) Przypomnienie notacja nawiasowa = infiksowa prosta notacja polska = prefiksowa odwrotna notacja polska = postfiksowa Niech będą dane dwa zbiory : OPERATOR - zbiór operatorów binarnych OPERAND - zbiór pojedynczych operandów 1. Jeśli wyrażenie E w notacji infiksowej jest pojedynczym operandem ( E OPERAND ), to postać prefiksowa i postfiksowa tego wyrażenia to E. 2. Jeśli E 1 Θ E 2 jest wyrażeniem w notacji infiksowej, E 1, E 2 są wyrażeniami w notacji infiksowej, ( Θ OPERATOR ), to : (a) Θ E1 E2 jest zapisem prefiksowym wyrażenia E 1 Θ E 2 (b) E1 E2 Θ jest zapisem postfiksowym wyrażenia E 1 Θ E 2 przy czym : E1, E2, ( E1, E2 ) są odpowiednikami wyrażeń E 1 i E 2 w notacji prefiksowej (postfiksowej ) 3. Jeśli ( E ) jest wyrażeniem infiksowym, to (a) E jest zapisem wyrażenia ( E ) w notacji prefiksowej (b) E jest zapisem wyrażenia ( E ) w notacji postfiksowej przy czym : E i E są odpowiednikami wyrażenia E w notacji odpowiednio : prefiksowej i postfiksowej.
Rozważane słowo : id1 + ( id2 + id3 ) * id4 a + (b + c) * d id.lexptr Text(id.lexptr) 1 a 2 b 3 c 4 d Atrubutowane drzewo rozbioru syntaktycznego postać ONP E.t = abc+d*t E.t = a + T.t = bc + d * T.t = a T.t = bc + * F.t = d F.t = a F.t = bc + id4.lexptr =4 id1.lexptr =1 ( ) E.t =bc+ E.t = b + T.t = c T.t = b F.t = c F.t = b id3.lexptr =3 id2.lexptr =2 Gramatyką atrybutywną typu S (gramatyką S-atrybutywną) nazywamy gramatykę atrybutywną zawierającą tylko i wyłącznie atrybuty syntetyzowane. Mogą one być obliczone z wykorzystaniem drzewa rozbioru syntaktycznego metodą bottom-up.
Przykład gramatyki atrybutywnej z atrybutami dziedziczonymi D"TL L.in! T.type T"int T.type! integer T"float T.type! real L"L1, id L1.in! L.in L.b! addtype(id.lexptr, L.in) L" id L.b! addtype(id.lexptr, L.in) Analizowane słowo: float id1, id2, id3 funkcja addtype() wstawia do tablicy symboli informację o typie zmiennej Drzewo rozbioru syntaktycznego : D T float L, id L Drzewo rozbioru syntaktycznego L, id id
Def. Dla każdej produkcji p = (X0 " X1...Xn) P zbiorem (relacją) bezpośrednich zależności atrybutów jest zbiór : DDP(p) = { (Xi.a " Xj.b) Xj.b! f(..., Xi.a,... ) R(p) } Gramatyka atrybutywna jest lokalnie acykliczna, jeśli graf każdej produkcji p P DDP(p) jest acykliczny dla Przykład : Produkcja Reguła semantyczna E " E1 + T E.t! E1.t + T.t + E. t E $ graf bezpośrednich zależności atrybutów dla E E. t + T T. t produkcji E " E + T Def Niech S będzie atrybutowanym drzewem rozbioru syntaktycznego słowa z L(G) i niech K0,...,Kn będą wierzchołkami odpowiadającymi zastosowaniu produkcji p = (X0 " X1...Xn). Piszemy Ki.a " Kj.b jeśli (Xi.a " Xj.b) DDP(p). Zbiór DT(S) = { (Ki.a" Kj.b)} uwzględniający wszystkie zastosowania produkcji w drzewie S nazwiemy relacją zależności nad drzewem S, a odpowiadający mu graf zorientowany grafem zależności.
Przykład: S " E E " E1E2 E " id E.i! g(e.s) S.r! E.t E.s! fs(e1.s, E2.s) E1.i! fi1(e.i) E2.i! fi2(e.i) E.t! ft(e1.t, E2.t) E.s! id.s E.t! h(e.i) Analizowane słowo : id id S E E id E id Obliczanie atrybutów : 1 s 2 s 3 s 4 s 5 s 6 i 7 i 8 i 9 t 10 t 11 t 12 r
S T12 6i E s5 t11 7 i E s t 9 8 i E s4 t 10 id s 1 id s 2 Tw. Gramatyka atrybutywna jest dobrze zdefiniowana wtedy i tylko wtedy, gdy jest zupełna i graf DT(S) jest acykliczny dla każdego drzewa S rozbioru syntaktycznego odpowiadającego słowu z języka L(G). Algorytm konstruowania grafu zależności We: gramatyka atrybutywna AG = < G, A, R > oraz drzewo rozbioru syntaktycznego słowa z L(G) Wy: graf zależności atrybutów w drzewie rozbioru syntaktycznego for każdy węzeł n w drzewie rozbioru syntaktycznego do for każdy atrybut a symbolu gramatyki znajdującego się w węźle n do zbuduj dla a węzeł w grafie zależności; for każdy węzeł n w drzewie rozbioru syntaktycznego do for każda reguła semantyczna b!f(c1,...,ck) związana z produkcją stosowaną w węźle n do for i:=1 to k do zbuduj połączenie z węzła odpowiadającego ci do węzła odpowiadającego b ; Uporządkowanie topologiczne zorientowanego grafu acyklicznego jest to każda permutacja m1,m2,...,mk wierzchołków tego grafu taka, że każda krawędź grafu biegnie od wierzchołka wcześniejszego w tym uporządkowaniu do węzła późniejszego. Np. jeśli mi " mj jest krawędzią biegnącą od wierzchołka mi do wierzchołka mj, to mi pojawia się w uporządkowaniu wcześniej niż mj. Porządek topologiczny w grafie zależności daje właściwą kolejność obliczania atrybutów w drzewie rozbioru syntaktycznego.
Metody obliczania reguł semantycznych i atrybutów. 1. Metody oparte o drzewa rozbioru syntaktycznego. Kolejność obliczania atrybutów jest ustalana w czasie kompilacji na podstawie drzewa rozbioru syntaktycznego poprzez skonstruowanie grafu zależności dla każdego wejściowego słowa. słowo drzewo rozbioru graf obliczone wejściowe syntaktycznego zależności atrybuty Metoda ta jest nieskuteczna tylko w przypadku, gdy graf zależności dla rozważanego słowa wejściowego posiada cykle. 2. Metody bazujące na regułach semantycznych Kolejność obliczania atrybutów jest rozstrzygana na etapie konstrukcji kompilatora. Analizowane są reguły semantyczne związane z poszczególnymi produkcjami (automatycznie lub ręcznie). Dla każdej produkcji kolejność obliczania atrybutów związanych z tą produkcją jest zdeterminowana już podczas konstrukcji kompilatora. 3. Metody nie uwzględniające bezpośrednio reguł semantycznych. Kolejność obliczania atrybutów jest ustalana bez bezpośredniego rozważania reguł semantycznych. Często tłumaczenie (analiza semantyczna) ma miejsce równolegle z analizą syntaktyczną: kolejność obliczania atrybutów jest wymuszana przez parser. Taka zasada postępowania ogranicza klasę gramatyk atrybutywnych, które mogą opisywać dokonywane tłumaczenie.