Як написати найпростіший компілятор, бібліотека програміста
Кращий спосіб зрозуміти роботу компіляторів - написати свій власний. У цьому допоможе цей короткий, але вичерпний гайд.
Стандартний компілятор здійснює наступні кроки:
У більшості сучасних компіляторів (на кшталт gcc і clang) останні два пункти повторюються ще раз. Для початкової генерації коду вони використовують не зовсім низькорівневий, але платформонезавісимость мову. Потім цей проміжний код переводиться в залежить від архітектури (x86, ARM і так далі).
Після цього об'єктний код готовий до лінковке. Велика частина нативних компіляторів автоматично викликає лінковщік, що створює виконуваний код, але це ще не компіляція. У мовах зразок Java або C # лінковка може бути повністю динамічної і виконуватися в віртуальній машині в момент завантаження.
Компілятор повинен бути:
Ця класична послідовність може бути застосована до всієї сфери розробки ПЗ. Сконцентруйтеся на першому пункті. Зробіть просту річ і змусьте її працювати.
Прочитайте книгу «Компілятори: принципи, технології та інструменти». Ця безсмертна класика до сьогоднішнього дня не втратила актуальності. «Дизайн сучасних компіляторів» - також хороша річ.
Якщо на даному етапі це здається вам занадто складним, почитайте для початку якісь введення в парсинг.
Переконайтеся, що вам комфортно працювати з графами, особливо з деревами. Це основа побудови програм на логічному рівні.
Добре визначите свою мову
Ви можете використовувати будь-яку нотацію, але будьте впевнені, що маєте повне і послідовне опис мови. Воно включає в себе як синтаксис, так і семантику.
Використовуйте свій улюблений мова
Це абсолютно нормально - писати компілятор на Pyhton, Ruby або будь-якому іншому мовою, який вам подобається. Користуйтеся простими алгоритми, принцип яких ви добре розумієте. Перший ваш компілятор зовсім не зобов'язаний бути швидким, або ефективним, або володіти купою фич. Все, що від нього вимагається - працювати досить правильно і легко піддаватися переробкам.
Також нормально писати різні стадії розвитку компілятора на різних мовах, якщо це потрібно.
Приготуйтеся до написання безлічі тестів
Весь ваш мова повинна бути стовідсотково покритий тестами, найефективніше, якщо він буде визначений ними. Будьте на ти з обраним тестовим фреймворком. Пишіть тести з першого дня. Раціонально віддавати перевагу «позитивним» тестами, які припускають коректну роботу коду.
Регулярно проганяйте всі тести. Чините некоректні тести. Буде дуже прикро залишитися біля розбитого корита з погано певною мовою, який не здатний прийняти валідний код.
Зробіть хороший парсер
Парсерів існує величезна кількість, вибирайте будь-який. Можна написати свій власний, але це спрацює тільки в тому випадку, якщо синтаксис вашої мови примітивний до маразму.
Парсер повинен виявляти синтаксично помилки і повідомляти про них. Пишіть багато тестів, як позитивних, так і негативних. Переіспользуйте написаний код для визначення мови.
На виході ваш парсер повинен генерувати абстрактне синтаксичне дерево. Якщо ваша мова використовує модулі, то результатом роботи парсера може бути найпростіше уявлення генерується «об'єктного коду».
Напишіть семантичний валідатор
Також зона його відповідальності охоплює дозвіл залежностей з іншими модулями, написаними на вашій мові, завантаженням цих модулів і використанні їх в процесі валідації. Наприклад, саме на цьому етапі перевіряється відповідність кількості параметрів, що надходять на вхід функції з модуля.
Ще раз, пишіть і запускайте багато тестів. Тривіальні випадки також обов'язкові до розгляду, як і складні.
генеруйте код
Скористайтеся найпростішими техніками, які ви знаєте. Найчастіше допустимо безпосередньо переводити мовну конструкцію (наприклад, умовні й оператор) в слабо параметризованих шаблон коду.
Забудьте про ефективність і зосередьтеся тільки на правильності.
Налаштуйте від платформи незалежну низкоуровневую віртуальну машину
Найімовірніше, вас не дуже цікавлять низькорівневі аспекти, якщо тільки ви не пристрасний шанувальник всього, що пов'язано з архітектурою.
Варіанти для вас:
- LLVM. дозволяє ефективно генерувати машинний код, найчастіше для х86 і ARM.
- CLR. орієнтована на .NET.
- JVM. націлена на світ Java, мультиплатформенна.
Забудьте про оптимізацію
Оптимізація - це складно. І майже завжди вона буває передчасною. Генеруйте неефективний, але робочий код. Реалізуйте весь мову перш ніж приступите до оптимізації.
Звичайно, деяка проста оптимізація цілком доречна на початковому етапі. Але намагайтеся уникати зайвих хитрощів, поки ваш компілятор не буде достатньо стабільний.