Вступ
Типовий компілятор виконує такі кроки:
- Розбір: вихідний текст перетворюється на абстрактне дерево синтаксису (AST).
- Дозвіл посилань на інші модулі (C відкладає цей крок до з'єднання).
- Семантична перевірка: вилучення синтаксично правильних висловлювань, які не мають сенсу, наприклад, недоступний код або дублювання декларацій.
- Еквівалентні перетворення та оптимізація на високому рівні: AST перетворюється на представлення більш ефективного обчислення з однаковою семантикою. Сюди входить, наприклад, ранній підрахунок загальних підвиражень та постійних виразів, усунення надмірних локальних призначень (див. Також SSA ) тощо.
- Генерація коду: AST перетворюється на лінійний код низького рівня зі стрибками, розподілом регістрів тощо. Деякі виклики функцій можна накреслити на цьому етапі, деякі петлі розкручуються тощо.
- Оптимізація видовища: код низького рівня сканується на прості локальні неефективність, які усуваються.
Більшість сучасних компіляторів (наприклад, gcc та clang) повторюють останні два кроки ще раз. Вони використовують проміжну низькорівневу, але незалежну від платформи мову для початкового генерування коду. Тоді ця мова перетворюється на специфічний для платформи код (x86, ARM тощо), роблячи приблизно те саме, що оптимізовано для платформи. Це включає, наприклад, використання векторних інструкцій, коли це можливо, впорядкування інструкцій для підвищення ефективності прогнозування галузей тощо.
Після цього об'єктний код готовий до посилання. Більшість компіляторів нативного коду знають, як викликати посилання для створення виконуваного файлу, але це не є етапом компіляції. У таких мовах, як Java та C #, посилання можуть бути абсолютно динамічними, що робиться VM під час завантаження.
Пригадайте основи
- Зроби так, щоб він працював
- Зробіть це красивим
- Зробіть це ефективним
Ця класична послідовність стосується всієї розробки програмного забезпечення, але несе повторення.
Концентруйтесь на першому кроці послідовності. Створіть найпростішу річ, яка могла б спрацювати.
Читай книги!
Прочитайте Книгу Драконів Ахо та Улмана. Це класика і досі досить застосовна.
Сучасний дизайн компілятора також високо оцінений.
Якщо цей матеріал зараз для вас занадто важкий, спочатку прочитайте кілька вступів про розбір; зазвичай розбір бібліотек включає вступи та приклади.
Переконайтеся, що вам зручно працювати з графіками, особливо з деревами. Ці речі - це те, що програми складаються на логічному рівні.
Добре визначте свою мову
Використовуйте будь-які позначення, які хочете, але переконайтесь, що ви маєте повний і послідовний опис вашої мови. Сюди входить і синтаксис, і семантика.
Давно пора писати фрагменти коду новою мовою як тестові приклади для майбутнього компілятора.
Використовуйте свою улюблену мову
Цілком нормально писати компілятор на Python чи Ruby або будь-якою іншою мовою. Використовуйте прості алгоритми, які ви добре розумієте. Перша версія не повинна бути швидкою, ефективною чи повною. Це потрібно лише бути правильним і легко змінювати.
Також добре писати різні етапи компілятора різними мовами, якщо це потрібно.
Підготуйтеся до написання безлічі тестів
Ваша мова повинна охоплюватися тестовими справами; ефективно це буде визначено ними. Ознайомтеся з бажаними рамками тестування. Пишіть тести з першого дня. Концентруйтеся на "позитивних" тестах, які приймають правильний код, на відміну від виявлення неправильного коду.
Запускайте всі тести регулярно. Виправте зламані тести, перш ніж продовжувати. Соромно було б опинитися з неправильно визначеною мовою, яка не може прийняти дійсний код.
Створіть хороший аналізатор
Генераторів парсера багато . Вибирайте все, що завгодно. Ви також можете написати власний парсер з нуля, але воно того варте лише, якщо синтаксис вашої мови мертвий простий.
Аналізатор повинен виявляти та повідомляти про синтаксичні помилки. Напишіть багато тестових випадків, як позитивних, так і негативних; повторно використовувати код, який ви написали, визначаючи мову.
Вихід вашого парсера - абстрактне синтаксичне дерево.
Якщо у вашій мові є модулі, висновок аналізатора може бути найпростішим представленням 'об’єктного коду', який ви створюєте. Існує маса простих способів скинути дерево у файл і швидко завантажити його назад.
Створіть смисловий валідатор
Швидше за все, ваша мова передбачає синтаксично правильні конструкції, які можуть не мати сенсу в певних контекстах. Приклад - це дублікат оголошення однієї змінної або передача параметра неправильного типу. Валідатор виявить такі помилки, дивлячись на дерево.
Валідатор також вирішить посилання на інші модулі, написані вашою мовою, завантажить ці інші модулі та використає в процесі перевірки. Наприклад, цей крок забезпечить правильність кількості параметрів, переданих функції з іншого модуля.
Знову ж таки, напишіть і запустіть багато тестових справ. Тривіальні випадки так само незамінні при вирішенні проблем, як розумні та складні.
Створити код
Використовуйте найпростіші методи, які ви знаєте. Часто нормально безпосередньо перекладати мовну конструкцію (як-от if
заяву) на злегка параметризований шаблон коду, не на відміну від шаблону HTML.
Знову ж таки, ігноруйте ефективність та концентруйтесь на коректності.
Націліться на платформу, незалежну від низькорівневого управління
Я вважаю, що ви ігноруєте матеріали низького рівня, якщо ви не цікавитесь деталями, що стосуються обладнання. Ці деталі - горі і складні.
Ваші варіанти:
- LLVM: дозволяє ефективно генерувати машинний код, як правило, для x86 та ARM.
- CLR: цілі .NET, в основному на базі x86 / Windows; має хороший JIT.
- JVM: цільовий Java-світ, досить багатоплатформенний, має хороший JIT.
Ігноруйте оптимізацію
Оптимізація важка. Майже завжди оптимізація передчасна. Створюйте неефективний, але правильний код. Реалізуйте всю мову, перш ніж спробувати оптимізувати отриманий код.
Звичайно, тривіальні оптимізації добре вводити. Але уникайте будь-яких хитрих, волохатих речей, перш ніж ваш компілятор буде стабільним.
І що?
Якщо все це не для вас занадто залякує, продовжуйте! Для простої мови кожен з кроків може бути простішим, ніж ви могли подумати.
Побачити "Hello world" від програми, яку створив ваш компілятор, варто докласти зусиль.