Tabela symboli - Symbol table
W informatyce , wykorzystując stół symbol jest struktura danych używana przez język tłumacza , takich jak kompilator lub interpreter , gdzie każdy identyfikator (lub symbol) w programie w kodzie źródłowym jest związany z informacji odnoszących się do jego zgłoszenia lub wyglądu w źródle. Innymi słowy, wpisy tablicy symboli przechowują informacje związane z odpowiednim symbolem wpisu.
Tło
Tablica symboli może istnieć tylko w pamięci podczas procesu translacji lub może być osadzona w wyniku translacji, na przykład w pliku obiektowym ABI do późniejszego wykorzystania. Na przykład może być używany podczas interaktywnej sesji debugowania lub jako zasób do formatowania raportu diagnostycznego podczas lub po wykonaniu programu.
Opis
Minimalna informacja zawarta w tablicy symboli używanych przez tłumacza obejmuje nazwę symbolu oraz jego lokalizację lub adres. W przypadku kompilatora ukierunkowanego na platformę z koncepcją relokowalności, będzie on również zawierał atrybuty relokacji (bezwzględne, relokowalne itp.) oraz potrzebne informacje o relokacji dla relokowalnych symboli. Tabele symboli dla języków programowania wysokiego poziomu mogą przechowywać typ symbolu: łańcuch, liczbę całkowitą, zmiennoprzecinkową itp., jego rozmiar, wymiary i granice. Nie wszystkie te informacje są zawarte w pliku wyjściowym, ale można je podać do wykorzystania podczas debugowania . W wielu przypadkach, symbol jest odsyłacz informacje są przechowywane z lub związane z tablicy symboli. Większość kompilatorów drukuje niektóre lub wszystkie te informacje w tablicy symboli i wykazach odsyłaczy na końcu tłumaczenia.
Realizacja
Dostępnych jest wiele struktur danych do implementacji tabel. Drzewa, listy liniowe i listy samoorganizujące się mogą być używane do implementacji tablicy symboli. Dostęp do tablicy symboli uzyskuje się w większości faz kompilatora, począwszy od analizy leksykalnej , a skończywszy na optymalizacji.
Kompilator może używać jednej dużej tablicy symboli dla wszystkich symboli lub używać oddzielnych, hierarchicznych tablic symboli dla różnych zakresów . Na przykład w języku o określonym zakresie, takim jak Algol lub PL/I, symbol „p” można zadeklarować oddzielnie w kilku procedurach, być może z różnymi atrybutami. Zakresem każdej deklaracji jest sekcja programu, w której odwołania do "p" prowadzą do tej deklaracji. Każda deklaracja reprezentuje unikalny identyfikator „p”. Tablica symboli musi mieć pewne sposoby rozróżniania odniesień do różnych „p”.
Powszechną strukturą danych używaną do implementacji tablic symboli jest tablica mieszająca . Czas wyszukiwania w tablicach mieszających jest niezależny od ilości elementów przechowywanych w tablicy, więc jest to efektywne dla dużej ilości elementów. Upraszcza również klasyfikację literałów w formacie tabelarycznym, włączając klasyfikację do obliczania klucza skrótu.
Ponieważ analizator leksykalny spędza dużą część swojego czasu na przeglądaniu tablicy symboli, czynność ta ma decydujący wpływ na ogólną szybkość kompilatora. Tablica symboli musi być zorganizowana w taki sposób, aby wpisy można było znaleźć tak szybko, jak to możliwe. Tabele haszujące są zwykle używane do organizowania tabeli symboli, w której słowo kluczowe lub identyfikator jest haszowane w celu utworzenia indeksu tablicy. Kolizje są nieuniknione w tabeli mieszającej, a częstym sposobem ich obsługi jest przechowywanie synonimu w następnym wolnym miejscu w tabeli.
Aplikacje
Plik obiektowy będzie zawierał tablicę symboli zawartych w nim identyfikatorów, które są widoczne z zewnątrz. Podczas łączenia różnych plików obiektowych, linker zidentyfikuje i rozwiąże te odniesienia do symboli. Zazwyczaj wszystkie niezdefiniowane symbole zewnętrzne będą wyszukiwane w jednej lub kilku bibliotekach obiektów . Jeśli zostanie znaleziony moduł, który definiuje ten symbol, z którym jest powiązany wraz z pierwszym plikiem obiektowym, a wszelkie niezdefiniowane identyfikatory zewnętrzne są dodawane do listy identyfikatorów do wyszukania. Ten proces trwa do momentu rozwiązania wszystkich odwołań zewnętrznych. Jest to błąd, jeśli jeden lub więcej pozostaje nierozwiązanych na końcu procesu.
Podczas inżynierii wstecznej pliku wykonywalnego, wiele narzędzi odwołuje się do tablicy symboli, aby sprawdzić, jakie adresy zostały przypisane zmiennym globalnym i znanym funkcjom. Jeśli tablica symboli została usunięta lub wyczyszczona przed przekształceniem w plik wykonywalny, narzędziom będzie trudniej określić adresy lub zrozumieć cokolwiek na temat programu.
Przykład
Rozważmy następujący program napisany w C :
// Declare an external function
extern double bar(double x);
// Define a public function
double foo(int count)
{
double sum = 0.0;
// Sum all the values bar(1) to bar(count)
for (int i = 1; i <= count; i++)
sum += bar((double) i);
return sum;
}
Kompilator AC, który analizuje ten kod, będzie zawierał co najmniej następujące wpisy w tablicy symboli:
Nazwa symbolu | Rodzaj | Zakres |
---|---|---|
bar |
funkcja, podwójna | zewnętrzny |
x |
podwójnie | parametr funkcji |
foo |
funkcja, podwójna | światowy |
count |
int | parametr funkcji |
sum |
podwójnie | blokować lokalnie |
i |
int | instrukcja for-loop |
Ponadto tablica symboli będzie również zawierać wpisy wygenerowane przez kompilator dla wartości wyrażeń pośrednich (np. wyrażenie rzutujące i
zmienną pętli na a double
i wartość zwracaną wywołania function bar()
), etykiety instrukcji i tak dalej.
Przykład: SysV ABI
Adres | Rodzaj | Nazwa |
---|---|---|
00000020 | a | T_BIT |
00000040 | a | F_BIT |
00000080 | a | I_BIT |
20000004 | T | irqvec |
20000008 | T | fiqvec |
2000000c | T | Resetowanie początkowe |
20000018 | T | _Główny |
20000024 | T | Kończyć się |
20000030 | T | AT91F_US3_CfgPIO_useB |
2000005c | T | AT91F_PIO_CfgPeriph |
200000b0 | T | Główny |
20000120 | T | AT91F_DBGU_Printk |
20000190 | T | AT91F_US_TxReady |
200001c0 | T | AT91F_US_PutChar |
200001f8 | T | AT91F_SpuriousHandler |
20000214 | T | AT91F_DataAbort |
20000230 | T | AT91F_FetchAbort |
200024c | T | AT91F_Undef |
20000268 | T | AT91F_UndefHandler |
20000284 | T | AT91F_LowLevelInit |
200002e0 | T | AT91F_DBGU_CfgPIO |
200030c | T | AT91F_PIO_CfgPeriph |
20000360 | T | AT91F_US_Konfiguruj |
200003dc | T | AT91F_US_Set Szybkość transmisji |
2000041c | T | AT91F_US_Szybkość transmisji |
200004we | T | AT91F_US_SetTimeguard |
2000051c | T | AT91F_PDC_Open |
200059c | T | AT91F_PDC_DisableRx |
200005c8 | T | AT91F_PDC_DisableTx |
200005f4 | T | AT91F_PDC_SetNextTx |
20000638 | T | AT91F_PDC_SetNextRx |
200067c | T | AT91F_PDC_SetTx |
200006c0 | T | AT91F_PDC_SetRx |
20000704 | T | AT91F_PDC_EnableRx |
20000730 | T | AT91F_PDC_EnableTx |
200075c | T | AT91F_US_EnableTx |
20000788 | T | __aeabi_uidiv |
20000788 | T | __udivsi3 |
20000884 | T | __aeabi_uidivmod |
200089c | T | __aeabi_idiv0 |
200089c | T | __aeabi_ldiv0 |
200089c | T | __div0 |
200009a0 | D | _dane |
200009a0 | A | _etekst |
200009a0 | D | holamigosz |
200009a4 | A | __bss_end__ |
200009a4 | A | __bss_start |
200009a4 | A | __bss_start__ |
200009a4 | A | _edycja |
200009a4 | A | _kończyć się |
Przykład tabeli symboli można znaleźć w specyfikacji SysV Application Binary Interface (ABI), która określa sposób rozmieszczenia symboli w pliku binarnym, tak aby różne kompilatory, linkery i programy ładujące mogły konsekwentnie znajdować i pracować z symbole w skompilowanym obiekcie.
SysV ABI jest zaimplementowany w narzędziu nm GNU binutils . Ten format wykorzystuje posortowane pole adresu pamięci, pole „typu symbolu” i identyfikator symbolu (nazywany „Nazwą”).
Typy symboli w SysV ABI (i danych wyjściowych nm) wskazują naturę każdego wpisu w tablicy symboli. Każdy typ symbolu jest reprezentowany przez pojedynczy znak. Na przykład, wpisy tablicy symboli reprezentujące zainicjowane dane są oznaczone znakiem "d", a wpisy tablicy symboli dla funkcji mają typ symbolu "t" (ponieważ kod wykonywalny znajduje się w sekcji tekstowej pliku obiektowego). Dodatkowo, wielkość typu symbolu wskazuje rodzaj powiązania: małe litery wskazują, że symbol jest lokalny, a wielkie litery wskazują na powiązanie zewnętrzne (globalne).
Przykład: tablica symboli Pythona
Język programowania Python zawiera obszerną obsługę tworzenia i manipulowania tablicami symboli. Właściwości, które można przeszukiwać to, czy dany symbol jest wolna zmienna lub zmienna związana , czy jest to zakres blok lub zakres globalny , czy jest on importowany i jakie obszaru nazw należy.
Przykład: dynamiczne tablice symboli
Niektóre języki programowania pozwalają na manipulowanie tablicą symboli w czasie wykonywania, dzięki czemu symbole mogą być dodawane w dowolnym momencie. Przykładem takiego języka jest rakieta .
Zarówno LISP, jak i języki programowania Scheme pozwalają na skojarzenie dowolnych, ogólnych właściwości z każdym symbolem.
Język programowania Prolog jest zasadniczo językiem manipulacji tablicami symboli; symbole nazywane są atomami , a relacje między symbolami można przeanalizować. Podobnie OpenCog zapewnia dynamiczną tablicę symboli, zwaną przestrzenią atomową , która jest używana do reprezentacji wiedzy .