Projekt kompilatora - analiza semantyczna
Dowiedzieliśmy się, jak parser konstruuje drzewa parsowania w fazie analizy składni. Zwykłe drzewo parsowania skonstruowane w tej fazie jest generalnie bezużyteczne dla kompilatora, ponieważ nie zawiera żadnych informacji o tym, jak ocenić drzewo. Produkcje gramatyki bezkontekstowej, która czyni reguły języka, nie uwzględniają sposobu ich interpretacji.
Na przykład
E → E + T
Z powyższą produkcją CFG nie jest związana żadna reguła semantyczna i nie może pomóc w nadaniu sensu produkcji.
Semantyka
Semantyka języka nadaje znaczenie jego konstrukcjom, takim jak tokeny i struktura składni. Semantyka pomaga w interpretacji symboli, ich typów i relacji między nimi. Analiza semantyczna ocenia, czy struktura składni skonstruowana w programie źródłowym ma jakiekolwiek znaczenie, czy nie.
CFG + semantic rules = Syntax Directed Definitions
Na przykład:
int a = “value”;
nie powinien powodować błędu w fazie analizy leksykalnej i składniowej, ponieważ jest poprawny leksykalnie i strukturalnie, ale powinien generować błąd semantyczny, ponieważ rodzaj przypisania jest inny. Reguły te są wyznaczane przez gramatykę języka i oceniane w analizie semantycznej. W analizie semantycznej należy wykonać następujące zadania:
- Rozdzielczość zakresu
- Sprawdzanie typu
- Sprawdzanie związane z tablicą
Błędy semantyczne
Wspomnieliśmy o niektórych błędach semantycznych, które analizator semantyczny powinien rozpoznać:
- Niezgodność typów
- Niezadeklarowana zmienna
- Niewłaściwe użycie zastrzeżonego identyfikatora.
- Wielokrotna deklaracja zmiennej w zakresie.
- Dostęp do zmiennej spoza zakresu.
- Rzeczywista i formalna niezgodność parametrów.
Gramatyka atrybutów
Gramatyka atrybutów jest specjalną formą gramatyki bezkontekstowej, w której pewne dodatkowe informacje (atrybuty) są dołączane do jednego lub większej liczby nieterminali w celu dostarczenia informacji kontekstowych. Każdy atrybut ma dobrze zdefiniowaną dziedzinę wartości, taką jak liczba całkowita, zmiennoprzecinkowa, znak, ciąg znaków i wyrażenia.
Gramatyka atrybutów jest medium dostarczającym semantykę do gramatyki bezkontekstowej i może pomóc w określeniu składni i semantyki języka programowania. Gramatyka atrybutów (widziana jako drzewo parsowania) może przekazywać wartości lub informacje między węzłami drzewa.
Example:
E → E + T { E.value = E.value + T.value }
Prawa część CFG zawiera reguły semantyczne, które określają, jak należy interpretować gramatykę. Tutaj wartości nieterminalowe E i T są sumowane, a wynik jest kopiowany do nieterminalowego E.
Atrybuty semantyczne mogą być przypisane do ich wartości z ich domeny w czasie analizy i oceniane w czasie przypisywania lub warunków. Na podstawie sposobu, w jaki atrybuty uzyskują swoje wartości, można je ogólnie podzielić na dwie kategorie: atrybuty zsyntetyzowane i atrybuty dziedziczone.
Zsyntetyzowane atrybuty
Te atrybuty pobierają wartości z wartości atrybutów ich węzłów podrzędnych. Aby to zilustrować, przyjmijmy następującą produkcję:
S → ABC
Jeśli S pobiera wartości z węzłów potomnych (A, B, C), to mówi się, że jest to atrybut zsyntetyzowany, ponieważ wartości ABC są syntetyzowane do S.
Podobnie jak w naszym poprzednim przykładzie (E → E + T), węzeł macierzysty E pobiera wartość ze swojego węzła potomnego. Zsyntetyzowane atrybuty nigdy nie pobierają wartości ze swoich węzłów nadrzędnych ani żadnych węzłów siostrzanych.
Dziedziczone atrybuty
W przeciwieństwie do atrybutów zsyntetyzowanych, odziedziczone atrybuty mogą przyjmować wartości od rodzica i / lub rodzeństwa. Jak w następnej produkcji,
S → ABC
A może pobierać wartości z S, B i C. B może przyjmować wartości z S, A i C. Podobnie C może przyjmować wartości z S, A i B.
Expansion : Gdy nieterminal jest rozszerzany do terminali zgodnie z regułą gramatyczną
Reduction: Kiedy terminal jest zredukowany do odpowiadającego mu nieterminala zgodnie z regułami gramatycznymi. Drzewa składniowe są analizowane z góry na dół i od lewej do prawej. Ilekroć następuje redukcja, stosujemy odpowiadające jej reguły semantyczne (akcje).
Analiza semantyczna wykorzystuje tłumaczenia ukierunkowane na składnię do wykonywania powyższych zadań.
Analizator semantyczny otrzymuje AST (Abstract Syntax Tree) z poprzedniego etapu (analiza składni).
Analizator semantyczny dołącza informacje o atrybutach do AST, które są nazywane Attributed AST.
Atrybuty to dwie wartości krotki, <nazwa atrybutu, wartość atrybutu>
Na przykład:
int value = 5;
<type, “integer”>
<presentvalue, “5”>
Do każdej produkcji dołączamy regułę semantyczną.
SDT przypisywane S.
Jeśli SDT używa tylko zsyntetyzowanych atrybutów, nazywa się to SDT z atrybutem S. Te atrybuty są oceniane za pomocą SDT z atrybutem S, które mają swoje działania semantyczne zapisane po produkcji (po prawej stronie).
Jak pokazano powyżej, atrybuty w SDT z atrybutem S są oceniane w analizie oddolnej, ponieważ wartości węzłów nadrzędnych zależą od wartości węzłów potomnych.
SDT przypisane L.
Ta forma SDT wykorzystuje zarówno atrybuty zsyntetyzowane, jak i odziedziczone, z zastrzeżeniem niepobierania wartości od prawego rodzeństwa.
W SDT z przypisanym atrybutem L, nieterminal może pobierać wartości ze swoich węzłów nadrzędnych, potomnych i rodzeństwa. Jak w następnej produkcji
S → ABC
S może przyjmować wartości z A, B i C (zsyntetyzowane). A może przyjmować wartości tylko z S. B może przyjmować wartości z S i A. C może uzyskać wartości z S, A i B. Żaden nieterminal nie może uzyskać wartości od rodzeństwa po jego prawej stronie.
Atrybuty w L-przypisanych SDT są oceniane metodą analizy najpierw w głąb i od lewej do prawej.
Możemy wywnioskować, że jeśli definicji przypisuje się S, to jest ona również przypisywana L, ponieważ definicja z atrybutem L obejmuje definicje z atrybutem S.