Co to jest projekt kompilatora? Rodzaje, narzędzia budowlane, przykład

⚡ Inteligentne podsumowanie

Fazy ​​kompilatora opisują sześć etapów, które przekształcają kod źródłowy w kod maszynowy: analiza leksykalna, analiza składniowa, analiza semantyczna, generowanie kodu pośredniego, optymalizacja kodu i generowanie kodu. Proces ten jest wspierany przez zarządzanie tablicą symboli i obsługę błędów.

  • 🔤 Analiza leksykalna: Skaner grupuje znaki w tokeny i zapisuje je w tabeli symboli.
  • 🌳 Analiza składni: Parser sprawdza gramatykę i buduje hierarchiczne drzewo parsowania z tokenów.
  • ✔️ Analiza semantyczna: W tej fazie weryfikowana jest zgodność typów i ich znaczenie przy użyciu drzewa składniowego.
  • ⚙️ Code Pokolenie: Generowany jest kod pośredni, który jest optymalizowany, a na końcu tłumaczony na kod maszynowy docelowy.
  • 📋 Procedury wsparcia: Tabela symboli i procedura obsługi błędów współdziałają na każdym etapie kompilacji.

Fazy ​​kompilatora z przykładem: proces kompilacji i jego kroki

Jakie są fazy projektowania kompilatora?

A kompilator działa w różnych fazach, a każda faza przekształca program źródłowy z jednej reprezentacji do innej. Każda faza pobiera dane wejściowe z poprzedniego etapu i przekazuje dane wyjściowe do kolejnej fazy kompilatora. Kompilator składa się z 6 faz. Każda z nich pomaga w konwersji języka wysokiego poziomu na kod maszynowy. Fazy kompilatora to:

  1. Analiza leksykalna
  2. Analiza składni
  3. Analiza semantyczna
  4. Generator kodu pośredniego
  5. Code optymalizator
  6. Code generator

Fazy ​​​​kompilatora

Fazy ​​​​kompilatora

Wszystkie te fazy polegają na konwersji kodu źródłowego poprzez podzielenie go na tokeny, utworzeniu drzew składniowych i optymalizacji kodu źródłowego w różnych fazach.

Faza 1: Analiza leksykalna

Analiza leksykalna to pierwsza faza, w której kompilator skanuje kod źródłowy. Proces ten może przebiegać od lewej do prawej, znak po znaku, i grupować te znaki w tokeny.

W tym przypadku strumień znaków z programu źródłowego jest grupowany w sensowne sekwencje poprzez identyfikację tokenów. Program wprowadza odpowiednie tokeny do tabeli symboli i przekazuje je do następnej fazy.

Podstawowe funkcje tej fazy to:

  • Zidentyfikuj jednostki leksykalne w kodzie źródłowym.
  • Klasyfikuj jednostki leksykalne do klas, takich jak stałe, słowa zarezerwowane, i wprowadź je do różnych tabel. Komentarze w programie źródłowym zostaną zignorowane.
  • Zidentyfikuj token, który nie jest częścią języka.

Przykład: x = y + 10

żeton Typ
X identyfikator
= Operator przypisania
Y identyfikator
+ Operator dodawania
10 Numer

Faza 2: Analiza składni

Analiza składniowa polega na odkrywaniu struktury kodu. Określa ona, czy tekst jest zgodny z oczekiwanym formatem. Głównym celem tej fazy jest upewnienie się, że kod źródłowy napisany przez programistę jest poprawny.

Analiza składniowa opiera się na regułach danego języka programowania poprzez konstruowanie drzewa rozbioru za pomocą tokenów. Określa ona również strukturę języka źródłowego oraz gramatykę lub składnię tego języka.

Oto lista zadań wykonywanych w tej fazie:

  • Uzyskaj tokeny z analizatora leksykalnego.
  • Sprawdź, czy wyrażenie jest poprawne składniowo.
  • Zgłoś wszystkie błędy składniowe.
  • Utwórz strukturę hierarchiczną znaną jako drzewo rozbioru.

Przykład

Każdy identyfikator/liczba jest wyrażeniem. Jeśli x jest identyfikatorem, a y+10 jest wyrażeniem, to x = y+10 jest instrukcją. Rozważmy drzewo składniowe dla następującego przykładu:

(a+b)*c

Przykład analizy składni

W drzewie parsowania:

  • Węzeł wewnętrzny: rekord z polem operatora i dwoma polami dla dzieci.
  • Liść: rekordy zawierające 2 lub więcej pól; jedno dla tokena i innych informacji o tokenie.
  • Upewnij się, że poszczególne elementy programu logicznie ze sobą współgrają.
  • Gromadzi informacje o typach i sprawdza ich zgodność.
  • Sprawdza, czy operandy są dozwolone przez język źródłowy.

Faza 3: Analiza semantyczna

Analiza semantyczna sprawdza spójność semantyczną kodu. Wykorzystuje drzewo składni z poprzedniej fazy wraz z tablicą symboli, aby sprawdzić, czy dany kod źródłowy jest semantycznie spójny. Sprawdza także, czy kod niesie ze sobą odpowiednie znaczenie.

Analizator semantyczny sprawdzi niezgodność typów, niezgodne operandy, funkcję wywołaną z niewłaściwymi argumentami, niezdefiniowaną zmienną itp.

Funkcje fazy analizy semantycznej to:

  • Pomaga przechowywać zebrane informacje o typach i zapisywać je w tabeli symboli lub drzewie składni.
  • Umożliwia przeprowadzenie kontroli typu.
  • W przypadku niezgodności typu i braku dokładnych reguł korekcji typu, które spełniają żądaną operację, pojawia się błąd semantyczny.
  • Zbiera informacje o typie i sprawdza jego zgodność.
  • Sprawdza, czy język źródłowy zezwala na operandy, czy nie.

Przykład

float x = 20.2;
float y = x*30;

W powyższym kodzie analizator semantyczny dokona rzutowania liczby całkowitej 30 na liczbę zmiennoprzecinkową 30.0 przed mnożeniem.

Faza 4: Poziom średniozaawansowany Code Pożądania

Po zakończeniu fazy analizy semantycznej kompilator generuje kod pośredni dla maszyny docelowej. Reprezentuje on program dla pewnego abs.tracmaszyna t.

Kod pośredni znajduje się pomiędzy językiem wysokiego poziomu a językiem maszynowym. Ten kod pośredni musi być generowany w sposób ułatwiający jego przetłumaczenie na docelowy kod maszynowy.

Funkcje pośrednie Code Pokolenie:

  • Powinien zostać wygenerowany z reprezentacji semantycznej programu źródłowego.
  • Przechowuje wartości obliczone podczas procesu tłumaczenia.
  • Pomaga przetłumaczyć kod pośredni na język docelowy.
  • Umożliwia zachowanie kolejności pierwszeństwa języka źródłowego.
  • Przechowuje prawidłową liczbę operandów instrukcji.

Przykład

Na przykład:

total = count + rate * 5

Kod pośredni uzyskany za pomocą metody kodu adresowego wygląda następująco:

t1 := int_to_float(5)
t2 := rate * t1
t3 := count + t2
total := t3

Faza 5: Code Optymalizacja

Kolejnym etapem jest optymalizacja kodu pośredniego. Ta faza usuwa zbędne wiersze kodu i porządkuje sekwencję instrukcji, aby przyspieszyć wykonywanie programu bez marnowania zasobów. Głównym celem tej fazy jest ulepszenie kodu pośredniego, aby wygenerować kod, który działa szybciej i zajmuje mniej miejsca.

Podstawowe funkcje tej fazy to:

  • Pomaga ustalić kompromis między szybkością wykonywania i kompilacji.
  • Poprawia czas działania programu docelowego.
  • Generuje uproszczony kod nadal w postaci pośredniej.
  • Usuwanie niedostępnego kodu i pozbywanie się nieużywanych zmiennych.
  • Usuwanie z pętli niezmienionych poleceń.

Przykład: Rozważ następujący kod:

a = intofloat(10)
b = c * a
d = e + b
f = d

Może stać się:

b = c * 10.0
f = e + b

Faza 6: Code Pożądania

Code Generowanie to ostatnia i finalna faza kompilatora. Otrzymuje dane wejściowe z fazy optymalizacji kodu i generuje w rezultacie kod strony lub kod obiektowy. Celem tej fazy jest przydzielenie pamięci i wygenerowanie relokowalnego kodu maszynowego.

Przydziela również miejsca w pamięci dla zmiennych. Instrukcje w kodzie pośrednim są konwertowane na instrukcje maszynowe. Ta faza konwertuje zoptymalizowany lub pośredni kod na język docelowy.

Językiem docelowym jest kod maszynowy. Dlatego wszystkie komórki pamięci i rejestry są również wybierane i przydzielane w tej fazie. Kod wygenerowany w tej fazie jest wykonywany w celu pobrania danych wejściowych i wygenerowania oczekiwanych wyników.

Przykład

a = b + 60.0 można by przetłumaczyć na rejestry jako:

MOVF a, R1
MULF #60.0, R2
ADDF R1, R2

Zarządzanie tabelą symboli

Tabela symboli zawiera rekord dla każdego identyfikatora z polami dla atrybutów identyfikatora. Ten komponent ułatwia kompilatorowi wyszukiwanie rekordu identyfikatora i szybkie jego pobieranie. Tabela symboli ułatwia również zarządzanie zakresem. Tabela symboli i procedura obsługi błędów współdziałają ze wszystkimi fazami, a tabela symboli jest odpowiednio aktualizowana.

Procedura obsługi błędów

Podczas projektowania kompilatora błędy mogą wystąpić na wszystkich poniższych etapach:

  • Analizator leksykalny: Błędnie napisane tokeny.
  • Analizator składni: Brak nawiasu.
  • Generator kodu pośredniego: Niezgodne operandy dla operatora.
  • Code Optymalizator: Gdy polecenie jest nieosiągalne.
  • Code Generator: Gdy pamięć jest pełna lub nie są przydzielone odpowiednie rejestry.
  • Tabele symboli: Błąd wynikający z zadeklarowania wielu identyfikatorów.

Najczęstszymi błędami są nieprawidłowe sekwencje znaków podczas skanowania, nieprawidłowe sekwencje tokenów w typie, błąd zakresu oraz analiza semantyczna.

Błąd może wystąpić w dowolnej z powyższych faz. Po znalezieniu błędów, faza musi je obsłużyć, aby kontynuować proces kompilacji. Błędy te muszą zostać zgłoszone do modułu obsługi błędów, który je obsługuje, aby wykonać proces kompilacji. Zazwyczaj błędy są zgłaszane w formie komunikatu.

FAQ

Tak. Asystenci AI mogą wyjaśnić każdą fazę, tracPokaż, jak przykładowy kod przekształca się w tokeny i drzewa parsowania, a także wskaż, gdzie pojawia się błąd składniowy lub semantyczny. Są to pomocne pomoce naukowe, ale należy zweryfikować wyjaśnienia z materiałem kursu.

Uczenie maszynowe potrafi przewidywać, które przebiegi optymalizacji generują najszybszy kod dla danego programu i maszyny, dostosowując wybory, które kiedyś były ręcznie opracowywanymi heurystykami. Kompilator musi jednak nadal gwarantować, że zoptymalizowany program zachowuje się dokładnie tak samo, jak oryginał.

Front-end zajmuje się analizą leksykalną, składniową i semantyczną, a także kodem pośrednim i zależy od języka źródłowego. Back-end zajmuje się optymalizacją i generowaniem kodu i zależy od maszyny docelowej. Ten podział upraszcza retargeting.

Nie. Analiza leksykalna (skanowanie) grupuje znaki w tokeny, podczas gdy analiza składniowa (analiza składniowa) porządkuje te tokeny w drzewie parsowania zgodnie z regułami gramatyki. Najpierw uruchamiana jest analiza leksykalna, która przekazuje swoje tokeny do parsera.

Podsumuj ten post następująco: