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