Projekt kompilatora - usuwanie błędów
Parser powinien być w stanie wykryć i zgłosić każdy błąd w programie. Oczekuje się, że w przypadku napotkania błędu parser powinien być w stanie go obsłużyć i kontynuować analizowanie reszty danych wejściowych. Przeważnie oczekuje się, że parser będzie sprawdzał błędy, ale błędy mogą wystąpić na różnych etapach procesu kompilacji. Program może mieć następujące rodzaje błędów na różnych etapach:
Lexical : nazwa jakiegoś identyfikatora wpisanego nieprawidłowo
Syntactical : brak średnika lub niezrównoważonego nawiasu
Semantical : niezgodne przypisanie wartości
Logical : kod nieosiągalny, nieskończona pętla
Istnieją cztery typowe strategie usuwania błędów, które można zaimplementować w parserze, aby radzić sobie z błędami w kodzie.
Tryb paniki
Gdy parser napotka błąd w dowolnym miejscu instrukcji, ignoruje pozostałą część instrukcji, nie przetwarzając danych wejściowych od błędnych danych wejściowych do separatora, takiego jak średnik. Jest to najłatwiejszy sposób naprawy błędów, a także zapobiega tworzeniu nieskończonych pętli przez parser.
Tryb instrukcji
Kiedy parser napotka błąd, próbuje podjąć działania naprawcze, tak aby pozostałe dane wejściowe instrukcji umożliwiały parserowi wykonanie analizy z wyprzedzeniem. Na przykład wstawienie brakującego średnika, zastąpienie przecinka średnikiem itp. Projektanci parserów muszą tutaj zachować ostrożność, ponieważ jedna błędna poprawka może prowadzić do nieskończonej pętli.
Produkcje błędów
Projektanci kompilatora znają niektóre typowe błędy, które mogą wystąpić w kodzie. Ponadto projektanci mogą tworzyć gramatykę rozszerzoną do wykorzystania jako produkcje, które generują błędne konstrukcje w przypadku napotkania tych błędów.
Globalna korekta
Parser traktuje program w ręku jako całość i próbuje dowiedzieć się, co program ma zrobić, i próbuje znaleźć najbliższe dopasowanie do niego, co jest bezbłędne. Podanie błędnego wejścia (instrukcji) X tworzy drzewo parsowania dla jakiejś najbliższej wolnej od błędów instrukcji Y. Może to pozwolić parserowi na wprowadzenie minimalnych zmian w kodzie źródłowym, ale ze względu na złożoność (czas i przestrzeń) tej strategii, nie została ona jeszcze wdrożona w praktyce.
Abstrakcyjne drzewa składniowe
Reprezentacje drzewa analizy nie są łatwe do przeanalizowania przez kompilator, ponieważ zawierają więcej szczegółów niż jest to faktycznie potrzebne. Weźmy jako przykład następujące drzewo parsowania:
Jeśli przyjrzymy się uważnie, stwierdzimy, że większość węzłów liści jest pojedynczymi potomkami ich węzłów macierzystych. Informacje te można wyeliminować przed przekazaniem ich do następnej fazy. Ukrywając dodatkowe informacje, możemy uzyskać drzewo, jak pokazano poniżej:
Drzewo abstrakcyjne można przedstawić jako:
AST to ważne struktury danych w kompilatorze zawierające najmniej niepotrzebnych informacji. AST są bardziej zwarte niż drzewo parsowania i mogą być łatwo używane przez kompilator.