Fazy ​​kompilatora z przykładem, procesem i krokami

⚡ Inteligentne podsumowanie

Fazy ​​kompilatora opisują sposób, w jaki kompilator przekształca kod źródłowy w kod maszynowy, wykonując sześć etapów: analizę leksykalną, analizę składniową, analizę semantyczną, generowanie kodu pośredniego, optymalizację kodu i generowanie kodu. Wszystko to jest wspierane przez zarządzanie tablicą symboli i obsługę błędów.

  • 🔤 Analiza leksykalna: W pierwszej fazie skanowany jest kod źródłowy i grupowane są znaki w tokeny, które wypełniają tablicę symboli.
  • 🌳 Analiza składni: Tokeny są sprawdzane pod kątem reguł gramatycznych i układane w drzewo składniowe.
  • Analiza semantyczna: Kompilator weryfikuje zgodność typów i ich znaczenie, korzystając z drzewa składniowego i tablicy symboli.
  • 🔁 Średniozaawansowany Code: Aby ułatwić tłumaczenie, generowana jest niezależna od maszyny reprezentacja, np. kod trójadresowy.
  • Optymalizacja: Zbędny i niedostępny kod jest usuwany, dzięki czemu program staje się szybszy i mniejszy.
  • 🖥️. Code Pokolenie: W fazie końcowej zoptymalizowany kod jest konwertowany na kod maszynowy docelowy, przydzielane są rejestry i pamięć.

Jakie są fazy projektowania kompilatora?

kompilator działa w różnych fazach, każda faza przekształca program źródłowy z jednej reprezentacji do drugiej. Każda faza pobiera dane wejściowe z poprzedniego etapu i przekazuje dane wyjściowe do następnej fazy kompilatora.
Kompilator składa się z 6 faz. Każda z tych faz 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 konwertują kod źródłowy, dzieląc go na tokeny, tworząc drzewa analizy i optymalizując kod źródłowy w różnych fazach.

Faza 1: Analiza leksykalna

Analiza leksykalna jest pierwszą fazą, w której kompilator skanuje kod źródłowy. Ten proces można kontynuować od lewej do prawej, znak po znaku i grupować te znaki w tokeny.
Tutaj strumień znaków z programu źródłowego jest pogrupowany w znaczące sekwencje poprzez identyfikację tokenów. Dokonuje wpisu odpowiednich biletów do tablicy symboli i przekazuje ten żeton do następnej fazy.
Podstawowe funkcje tej fazy to:

  • Identyfikuj jednostki leksykalne w kodzie źródłowym
  • Klasyfikuj jednostki leksykalne na klasy, takie jak stałe, słowa zastrzeżone, i wprowadzaj je do różnych tabel. Spowoduje to zignorowanie komentarzy w programie źródłowym
  • Zidentyfikuj token, który nie jest częścią języka

Przykład:
x = y + 10

Żetony

X identyfikator
= Operator przypisania
Y identyfikator
+ Operator dodawania
10 Numer

Faza 2: Analiza składni

Analiza składni polega na odkrywaniu struktury w kodzie. Określa, czy tekst ma oczekiwany format. Głównym celem tej fazy jest upewnienie się, czy kod źródłowy napisany przez programistę jest poprawny, czy nie.
Analiza składni opiera się na regułach opartych na konkretnym języku programowania poprzez konstruowanie drzewa analizy za pomocą tokenów. Określa także strukturę języka źródłowego oraz gramatykę, czyli składnię języka.
Oto lista zadań wykonywanych w tej fazie:

  • Uzyskaj tokeny z analizatora leksykalnego
  • Sprawdza, czy wyrażenie jest poprawne składniowo
  • Zgłaszaj wszystkie błędy składniowe
  • Zbuduj strukturę hierarchiczną znaną jako drzewo składniowe

Przykład

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

(a+b)*c

Przykład analizy składni

W drzewie Parse

  • Węzeł wewnętrzny: rekord z polem operatora i dwoma plikami dla dzieci
  • Liść: rekordy z 2/więcej pól; jeden dla tokena i inne informacje o tokenie
  • Upewnij się, że elementy programu pasują do siebie w znaczący sposób
  • Zbiera informacje o typie i sprawdza zgodność typów
  • 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, czy występują niezgodności typów, niezgodne operandy, funkcja została wywołana z niewłaściwymi argumentami, niezdefiniowana zmienna itp.
Funkcje fazy analiz semantycznych to:

  • Pomaga przechowywać zebrane informacje o typie i zapisywać je w tabeli symboli lub drzewie składni
  • Umożliwia sprawdzenie typu
  • W przypadku niezgodności typów, gdy nie ma dokładnych reguł korekcji typów spełniających żądaną operację, wyświetlany jest błąd semantyczny
  • Zbiera informacje o typie i sprawdza zgodność typu
  • 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 przed pomnożeniem przerzuci liczbę całkowitą 30 na liczbę zmiennoprzecinkową 30.0

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 należy wygenerować w taki sposób, aby można go było łatwo przetłumaczyć na docelowy kod maszynowy.
Funkcje na poziomie średnio zaawansowanym Code Pokolenie:

  • Powinien zostać wygenerowany na podstawie semantycznej reprezentacji programu źródłowego
  • Przechowuje wartości obliczone podczas procesu tłumaczenia
  • Pomaga w tłumaczeniu kodu pośredniego 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 przy pomocy metody kodu adresowego to:

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

Faza 5: Code Optymalizacja

Następną fazą jest optymalizacja kodu lub kod pośredni. Ta faza usuwa niepotrzebną linię kodu i porządkuje sekwencję instrukcji, aby przyspieszyć wykonanie programu bez marnowania zasobów. Głównym celem tej fazy jest ulepszenie kodu pośredniego w celu wygenerowania kodu, który działa szybciej i zajmuje mniej miejsca.
Podstawowe funkcje tej fazy to:

  • Pomaga w ustaleniu kompromisu pomiędzy szybkością wykonywania i kompilacji
  • Poprawia czas działania programu docelowego
  • Generuje usprawniony kod nadal w reprezentacji pośredniej
  • Usuwanie nieosiągalnego kodu i pozbywanie się nieużywanych zmiennych
  • Usuwanie instrukcji, które nie zostały zmienione z pętli

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

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

Może zostać

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 faz optymalizacji kodu i generuje w rezultacie kod strony lub kod obiektowy. Celem tej fazy jest przydzielenie pamięci i wygenerowanie relokowalnego kodu maszynowego.
Przydziela także miejsca w pamięci dla zmiennej. Instrukcje zawarte w kodzie pośrednim są konwertowane na instrukcje maszynowe. W tej fazie kod optymalizacyjny lub pośredni jest przekształcany w język docelowy.
Językiem docelowym jest kod maszynowy. Dlatego też w tej fazie wybierane i przydzielane są również wszystkie lokalizacje pamięci i rejestry. Kod wygenerowany w tej fazie jest wykonywany w celu pobrania danych wejściowych i wygenerowania oczekiwanych wyników.

Przykład

a = b + 60.0
Prawdopodobnie zostałoby przetłumaczone na rejestry.

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

Zarządzanie tabelą symboli

Tabela symboli zawiera rekord dla każdego identyfikatora z polami na atrybuty identyfikatora. Ten komponent ułatwia kompilatorowi przeszukiwanie rekordu identyfikatora i szybkie jego pobieranie. Tabela symboli pomaga również w zarządzaniu zakresem. Tablica symboli i obsługa błędów współdziałają ze wszystkimi fazami i odpowiednio aktualizują tabelę symboli.

Procedura obsługi błędów

W procesie projektowania kompilatora błąd może wystąpić we wszystkich poniższych fazach:

  • Analizator leksykalny: błędnie zapisane tokeny
  • Analizator składni: brak nawiasu
  • Generator kodu pośredniego: Niedopasowane 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 wielu zadeklarowanych identyfikatorów

Najczęstsze błędy to nieprawidłowa sekwencja znaków podczas skanowania, nieprawidłowe sekwencje tokenów typu, błąd zakresu i analiza semantyczna.
Błąd może wystąpić w dowolnej z powyższych faz. Po znalezieniu błędów, faza musi sobie z nimi poradzić, aby kontynuować proces kompilacji. Błędy te należy zgłosić do procedury obsługi błędów, która obsługuje błąd, aby przeprowadzić proces kompilacji. Zazwyczaj błędy są zgłaszane w formie komunikatu.

FAQ

Faza analizy (front-end) rozbija kod źródłowy na części poprzez analizę leksykalną, składniową i semantyczną. Faza syntezy (back-end) buduje program docelowy poprzez generowanie kodu pośredniego, optymalizację i generowanie kodu.

Kompilator tłumaczy cały program źródłowy na kod maszynowy przed jego uruchomieniem. Interpreter tłumaczy i wykonuje kod linia po linii, co jest łatwiejsze do debugowania, ale zazwyczaj wolniejsze w czasie wykonywania.

Kod trójadresowy to reprezentacja pośrednia, w której każda instrukcja ma maksymalnie trzy operandy, np. t1 = a + b. Jest on łatwy do optymalizacji i łatwy do przetłumaczenia na kod maszynowy docelowy.

Sztuczna inteligencja i uczenie maszynowe pomagają nowoczesnym kompilatorom podejmować trafniejsze decyzje optymalizacyjne, takie jak wybór transformacji pętli, inline i alokacja rejestrów. Przewidują, które optymalizacje poprawiają wydajność danego programu i celu.

Tak. Sztuczna inteligencja może wyjaśnić każdą fazę za pomocą przykładów, tracPokaż, jak przykładowe polecenie przechodzi z tokenów do kodu maszynowego i odpowiedz na pytania uzupełniające. Dzięki temu studenci łatwiej nauczą się projektowania kompilatorów.

Podsumuj ten post następująco: