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 ajest 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 1lub '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 - 5int a = 5 - ((4*3) - (1+2))