Uwaga: Kiedy użyłem „złożonego” w tytule, mam na myśli, że wyrażenie ma wiele operatorów i operandów. Nie to, że samo wyrażenie jest złożone.
Niedawno pracowałem nad prostym kompilatorem do zestawu x86-64. Skończyłem główny front kompilatora - leksykon i parser - i jestem teraz w stanie wygenerować abstrakcyjne drzewo syntaktyczne mojego programu. A ponieważ mój język będzie wpisywany statycznie, teraz robię następną fazę: sprawdzanie kodu źródłowego. Jednak natknąłem się na problem i nie byłem w stanie samodzielnie go rozwiązać.
Rozważ następujący przykład:
Parser mojego kompilatora odczytał następujący wiersz kodu:
int a = 1 + 2 - 3 * 4 - 5
I przekonwertował go na następujący AST:
=
/ \
a(int) \
-
/ \
- 5
/ \
+ *
/ \ / \
1 2 3 4
Teraz musi wpisać typ AST. zaczyna się od pierwszego sprawdzenia =
operatora. Najpierw sprawdza lewą stronę operatora. Widzi, że zmienna a
jest zadeklarowana jako liczba całkowita. Musi więc teraz sprawdzić, czy wyrażenie po prawej stronie jest liczbą całkowitą.
Rozumiem, jak można to zrobić, jeśli wyrażenie byłoby tylko jedną wartością, taką jak 1
lub 'a'
. Ale jak można to zrobić dla wyrażeń z wieloma wartościami i operandami - złożonym wyrażeniem - takim jak powyższy? Aby poprawnie określić wartość wyrażenia, wygląda na to, że sprawdzanie typu faktycznie musiałoby wykonać samo wyrażenie i zapisać wynik. Ale najwyraźniej wydaje się to sprzeczne z celem oddzielenia faz kompilacji i wykonania.
Jedynym innym sposobem, jaki mogę sobie wyobrazić, jest to, aby rekursywnie sprawdzać liść każdego podwyrażenia w AST i sprawdzać, czy wszystkie typy liści pasują do oczekiwanego typu operatora. Tak więc, zaczynając od =
operatora, moduł sprawdzania typu skanuje następnie wszystkie parametry AST po lewej stronie i sprawdza, czy wszystkie liście są liczbami całkowitymi. Powtórzyłoby to następnie dla każdego operatora w podwyrażeniu.
Próbowałem zbadać ten temat w mojej kopii „Smoczej księgi” , ale wydaje się, że nie zawiera on zbyt wielu szczegółów i po prostu przypomina to, co już wiem.
Jaka jest zwykle stosowana metoda, gdy kompilator sprawdza wyrażenia sprawdzające typ z wieloma operatorami i operandami? Czy stosuje się którąkolwiek z wyżej wymienionych metod? Jeśli nie, jakie są metody i jak dokładnie będą działać?
double a = 7/2
spróbuje zinterpretować prawą stronę jako podwójną, a zatem spróbuje zinterpretować licznik i mianownik jako podwójny i przekształcić je w razie potrzeby; w rezultacie a = 3.5
. Od dołu do góry dokonuje podziału liczb całkowitych i przekształca dopiero w ostatnim kroku (przypisaniu), więc a = 3.0
.
int a = 1 + 2 - 3 * 4 - 5
int a = 5 - ((4*3) - (1+2))