Як саме компілятор відновлює помилку типу?


10

Я прочитав кілька статей, статей та розділу 4.1.4, розділ 4 « Компілятори: принципи, методи та засоби» (2-е видання) (він же «Книга драконів»), де всі обговорюються теми відновлення помилок синтаксичного компілятора. Однак, експериментуючи з декількома сучасними компіляторами, я бачив, що вони також відновлюються після семантичних помилок, а також синтаксичних помилок.

Я досить добре розумію алгоритми та методи компіляторів, що відновлюються після синтаксично пов’язаних помилок, однак я не точно розумію, як компілятор може відновити після семантичної помилки.

Наразі я використовую незначну зміну шаблону відвідувачів для створення коду з мого дерева абстрактних синтаксисів. Подумайте, що мій компілятор склав такі вирази:

1 / (2 * (3 + "4"))

Компілятор генерує таке абстрактне синтаксичне дерево:

      op(/)
        |
     -------
    /       \ 
 int(1)    op(*)
             |
          -------
         /       \
       int(2)   op(+)
                  |
               -------
              /       \
           int(3)   str(4)

Фаза генерації коду потім використовує шаблон відвідувача для рекурсивного переходу абстрактного дерева синтаксису та проведення перевірки типу. Абстрактне дерево синтаксису буде проходити до тих пір, поки компілятор не дійшов до найпотаємнішої частини виразу; (3 + "4"). Потім компілятор перевіряє кожну сторону виразів і бачить, що вони не є семантично еквівалентними. Компілятор викликає помилку типу. Ось де криється проблема. Що тепер повинен робити компілятор ?

Щоб компілятор відновився після цієї помилки і продовжив перевірку зовнішніх частин виразів, йому доведеться повернути деякий тип ( intабо str) з оцінки найпотужнішої частини виразу до наступної найпотужнішої частини виразу. Але він просто не має типу повернення . Оскільки сталася помилка типу, жодного типу не було виведено.

Одне можливе рішення, яке я постулював, - це те, що якщо помилка типу трапляється, помилка повинна бути підвищена, а спеціальне значення, яке означає, що сталася помилка типу, має повернутися до попередніх абстрактних викликів обходу дерева синтаксису. Якщо попередні обхідні дзвінки зустрічаються з цим значенням, вони знають, що помилка типу сталася глибше в абстрактному синтаксичному дереві, і повинні уникати спроб вивести тип. Хоча цей метод, здається, працює, він здається дуже неефективним. Якщо найглибша частина виразу знаходиться глибоко в абстрактному синтаксичному дереві, тоді компілятору доведеться робити багато рекурсивних викликів лише для того, щоб зрозуміти, що ніякої реальної роботи неможливо зробити, і просто повернутися з кожного.

Чи застосовується описаний вище метод (я сумніваюся). Якщо так, то чи не це ефективно? Якщо ні, то які саме методи використовуються, коли компілятори відновлюються після семантичних помилок?


3
Досить впевнений, що саме це використовується, і чому ви не вважаєте, що це досить ефективно? Для перевірки типів, компілятор повинен йти по всьому дереву в будь-якому випадку . Семантичний збій є більш ефективним, оскільки дозволяє компілятору усунути гілку після виявлення помилки.
Теластин

Відповіді:


8

Ваша запропонована ідея по суті правильна.

Ключовим є те, що тип вузла AST обчислюється лише один раз та зберігається. Кожен раз, коли тип знову потрібен, він просто витягує збережений тип. Якщо роздільна здатність закінчується помилкою, замість цього зберігається тип помилки.


3

Один цікавий підхід - мати спеціальний тип помилок. Коли вперше виникає така помилка, реєструється діагностика, а тип помилки повертається як тип виразу. Цей тип помилки має деякі цікаві властивості:

  • Будь-яка операція, яка виконується на ній, є успішною (щоб запобігти каскаду повідомлень про помилки, всі викликані однією і тією самою початковою помилкою)
  • Результат будь-якої операції, виконаної на об'єкті з типом помилки, також має тип помилки
  • Якщо тип помилки доходить до генерування коду, генератор коду помічає використання та генерує код, який не вдається (наприклад, викидає виняток, перериває або будь-що, що підходить для вашої мови)

За допомогою цієї комбінації ви можете успішно компілювати код, що містить помилки типу, і поки цей код фактично не використовується, помилка виконання не відбуватиметься. Це може бути корисно, наприклад, щоб ви могли запустити одиничні тести для частин коду, які не впливають.


Дякую за відповідь, Жуль. Досить смішно, саме такий метод я і в кінцевому підсумку використав. Великі розуми думають однаково, так? ;-)
Крістіан Дін

2

Якщо є смислова помилка, користувачеві видається повідомлення про помилку компіляції, яке вказує на таке.

Коли це буде зроблено, нормально припинити компіляцію, оскільки програма введення помиляється - це не юридична програма мовою, тому її можна просто відхилити.

Це досить суворо, проте є більш м'які варіанти. Відмовтеся від генерації будь-якого коду та генерації вихідних файлів, але продовжуйте щось шукати більше помилок.

Наприклад, він може просто перервати будь-який подальший аналіз типу для поточного дерева виразів і продовжити обробку виразів з наступних операторів.


2

Припустимо, що ваша мова дозволяє додавати цілі числа, а також дозволяє об'єднувати рядки з +оператором.

Оскільки int + stringце не дозволено, оцінка оцінки +призведе до повідомлення про помилку. Компілятор може просто повернутися errorяк тип. Або це може бути більш розумним, оскільки int + int -> intі string + string -> stringдозволено, воно може повернути "помилка, може бути int або string".

Потім приходить *оператор, і ми будемо вважати int + int, що дозволено лише . Потім компілятор може вирішити, що +насправді повинен був повернутися int, і тип, який повертається для *, буде int, без будь-якого повідомлення про помилку.


Я думаю, я слідкую за тобою, @gnasher, але що саме ти маєш на увазі під "" оператором ? Це був друкарський помилок?
Крістіан Дін

@ChristianDean в цитатах є зірочка, яка інтерпретується як розмітка Markdown, а не надається.
JakeRobb

Я надіслав редагування у відповідь, яке вирішить проблему, як тільки моя редакція буде проаналізована.
JakeRobb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.