Я прочитал несколько статей, статей и раздел 4.1.4, глава 4 « Компиляторы: принципы, методы и инструменты» (2-е издание) (он же «Книга Дракона»), в которых все обсуждается тема исправления синтаксических ошибок компилятора. Однако, после экспериментов с несколькими современными компиляторами, я увидел, что они также восстанавливаются после семантических ошибок, а также синтаксических ошибок.
Я достаточно хорошо понимаю алгоритмы и методы, лежащие в основе компиляторов, восстанавливающихся после синтаксически связанных ошибок, однако я не совсем понимаю, как компилятор может восстанавливаться после семантической ошибки.
В настоящее время я использую небольшой вариант шаблона посетителя для генерации кода из моего абстрактного синтаксического дерева. Рассмотрим мой компилятор, компилирующий следующие выражения:
1 / (2 * (3 + "4"))
Компилятор сгенерирует следующее абстрактное синтаксическое дерево:
op(/)
|
-------
/ \
int(1) op(*)
|
-------
/ \
int(2) op(+)
|
-------
/ \
int(3) str(4)
Фаза генерации кода будет затем использовать шаблон посетителя для рекурсивного обхода абстрактного синтаксического дерева и выполнения проверки типов. Абстрактное синтаксическое дерево будет проходить до тех пор, пока компилятор не перейдет к самой внутренней части выражения; (3 + "4")
, Затем компилятор проверяет каждую сторону выражений и видит, что они не являются семантически эквивалентными. Компилятор вызывает ошибку типа. Вот в чем проблема. Что теперь должен делать компилятор ?
Чтобы компилятор восстановился после этой ошибки и продолжил проверку типов внешних частей выражений, он должен был бы вернуть некоторый тип ( int
или str
) из оценки самой внутренней части выражения в следующую самую внутреннюю часть выражения. Но у него просто нет типа для возврата . Поскольку произошла ошибка типа, тип не был выведен.
Одно возможное решение, которое я постулировал, заключается в том, что если ошибка типа действительно возникает, она должна возникать, и к предыдущим вызовам обхода дерева абстрактного синтаксиса должно возвращаться специальное значение, которое означает, что произошла ошибка типа. Если предыдущие вызовы обхода встречают это значение, они знают, что ошибка типа произошла глубже в абстрактном синтаксическом дереве, и должны избегать попыток определить тип. Хотя этот метод, похоже, работает, он кажется очень неэффективным. Если самая внутренняя часть выражения находится глубоко в абстрактном синтаксическом дереве, то компилятору придется совершать много рекурсивных вызовов только для того, чтобы понять, что никакая реальная работа не может быть выполнена, и просто возвращаться из каждого из них.
Используется ли метод, который я описал выше (я сомневаюсь в этом). Если так, разве это не эффективно? Если нет, какие именно методы используются, когда компиляторы восстанавливаются после семантических ошибок?