Fazy kompilatora z przykładem: proces i kroki kompilacji
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:
- Analiza leksykalna
- Analiza składni
- Analiza semantyczna
- Generator kodu pośredniego
- Optymalizator kodu
- Generator kodów

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
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: Pośrednie generowanie kodu
Po zakończeniu fazy analizy semantycznej kompilator generuje kod pośredni dla maszyny docelowej. Reprezentuje program dla jakiejś abstrakcyjnej maszyny.
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 generowania kodu pośredniego:
- 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: Optymalizacja kodu
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: Generowanie kodu
Generowanie kodu to ostatnia i ostatnia faza kompilatora. Otrzymuje dane wejściowe z faz optymalizacji kodu i w rezultacie tworzy kod strony lub kod obiektowy. Celem tej fazy jest przydzielenie pamięci i wygenerowanie kodu maszynowego, który można przenieść.
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
- Optymalizator kodu: Gdy instrukcja jest nieosiągalna
- 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.
Podsumowanie
- Kompilator działa w różnych fazach, każda faza przekształca program źródłowy z jednej reprezentacji do innej
- Sześć faz projekt kompilatora są 1) Analiza leksykalna 2) Analiza składniowa 3) Analiza semantyczna 4) Generator kodu pośredniego 5) Optymalizator kodu 6) Kod Generator
- Analiza leksykalna jest pierwszą fazą, w której kompilator skanuje kod źródłowy
- Analiza składni polega na odkrywaniu struktury tekstu
- Analiza semantyczna sprawdza spójność semantyczną kodu
- Po zakończeniu fazy analizy semantycznej w kompilatorze wygeneruj kod pośredni dla maszyny docelowej
- Faza optymalizacji kodu usuwa niepotrzebne linie kodu i porządkuje sekwencję instrukcji
- Faza generowania kodu pobiera dane wejściowe z fazy optymalizacji kodu i w rezultacie tworzy kod strony lub kod obiektowy
- Tabela symboli zawiera rekord dla każdego identyfikatora z polami na atrybuty identyfikatora
- Procedura obsługi błędów obsługuje błędy i raporty w wielu fazach