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

96

Я слышал об идее начальной загрузки языка, то есть написания компилятора / интерпретатора для языка как такового. Мне было интересно, как это можно сделать, и я немного огляделся и увидел, что кто-то сказал, что это можно сделать только

  • написание исходного компилятора на другом языке.
  • ручное кодирование исходного компилятора на ассемблере, что кажется частным случаем первого

Мне кажется, что ни один из них не является начальной загрузкой языка в том смысле, что оба требуют внешней поддержки. Есть ли способ написать компилятор на его собственном языке?

pbh101
источник
Я не очень разбираюсь в таких вещах, но предполагаю, что исходный компилятор должен быть написан на другом языке. Я абсолютно уверен , что «самонастройки», со ссылкой на составителей, просто относится к написанию на компилятор для языка на языке это означало для компиляции, а не писать первый компилятор для языка в языке это означало для компиляции.
jdd
1
Спасибо за информацию всем. Когда это объясняется идеей сначала написать ограниченный компилятор, а затем наращивать его, тогда идея начальной загрузки приобретает больше смысла. В этом семестре я беру класс компиляторов, на это решение во многом повлиял пост Стива Йегге о том, насколько важен класс в компиляторах , и я только что купил копию книги Dragon по ссылке Amazon, которая так сильно изменилась на SO ранее.
pbh101
1
См. Также аналогичный вопрос: Реализация компилятора сама по себе
Urban Vagabond

Ответы:

107

Есть ли способ написать компилятор на его собственном языке?

У вас должен быть какой-то существующий язык для написания вашего нового компилятора. Если бы вы писали новый, скажем, компилятор C ++, вы бы просто написали его на C ++ и сначала скомпилировали с помощью существующего компилятора. С другой стороны, если вы создавали компилятор для нового языка, назовем его Yazzleof, вам сначала нужно будет написать новый компилятор на другом языке. Как правило, это другой язык программирования, но это не обязательно. Это может быть сборка, а при необходимости и машинный код.

Если бы ты был собираетесь самонастройки компилятора для Yazzleof, вы вообще не написать компилятор для полного языка на начальном этапе. Вместо этого вы должны написать компилятор для Yazzle-lite, наименьшего возможного подмножества Yazzleof (ну, по крайней мере , довольно небольшого подмножества). Затем в Yazzle-lite вы должны написать компилятор для полного языка. (Очевидно, это может происходить итеративно, а не за один прыжок.) Поскольку Yazzle-lite является правильным подмножеством Yazzleof, теперь у вас есть компилятор, который может компилировать себя.

Есть действительно хорошая статья о начальной загрузке компилятора с минимально возможного уровня (который на современной машине в основном представляет собой шестнадцатеричный редактор) под названием « Загрузка простого компилятора из ничего» . Его можно найти по адресу https://web.archive.org/web/20061108010907/http://www.rano.org/bcompiler.html .

Дерек Парк
источник
19

Вы прочитали правильное объяснение. Это обсуждается в « Компиляторах: принципы, методы и инструменты» (Книга Дракона):

  • Напишите компилятор C1 для языка X на языке Y
  • Используйте компилятор C1, чтобы написать компилятор C2 для языка X на языке X
  • Теперь C2 - это полностью самостоятельная хостинговая среда.
Марк Харрисон
источник
7

Супер интересное обсуждение этого в Unix сотворец Кен Томпсон «s премии Тьюринга лекции.

Он начинает с:

Я собираюсь описать одну из многих проблем типа «курица и яйцо», которые возникают, когда компиляторы пишутся на их собственном языке. Для этого я буду использовать конкретный пример из компилятора C.

и переходит к демонстрации того, как он написал версию компилятора Unix C, которая всегда позволяла ему входить в систему без пароля, потому что компилятор C распознавал программу входа в систему и добавлял специальный код.

Второй шаблон предназначен для компилятора C. Код замены - это самовоспроизводящаяся программа этапа I, которая вставляет оба троянских коня в компилятор. Для этого требуется этап обучения, как в примере этапа II. Сначала мы компилируем измененный исходный код с помощью обычного компилятора C, чтобы получить двоичный файл с ошибками. Мы устанавливаем этот двоичный файл как официальный C. Теперь мы можем удалить ошибки из исходного кода компилятора, и новый двоичный файл будет повторно вставлять ошибки при каждой компиляции. Конечно, команда входа в систему останется ошибочной без каких-либо следов в исходном коде.

Марк Харрисон
источник
9
Это не по теме .. Интересно, но запутанно, а не ответ на вопрос.
blueshift
5

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

Это определение начальной загрузки:

процесс активации простой системы более сложной, служащей той же цели.

EDIT: статья в Википедии о начальной загрузке компилятора охватывает эту концепцию лучше, чем я.

Эрик Хаскинс
источник
4

Посмотрите подкаст Software Engineering Radio, выпуск 61 (2007-07-06), в котором обсуждается внутреннее устройство компилятора GCC, а также процесс начальной загрузки GCC.

никто
источник
4

Дональд Э. Кнут фактически построил WEB , написав в нем компилятор, а затем вручную скомпилировал его в ассемблерный или машинный код.

MauganRa
источник
3

Насколько я понимаю, первый интерпретатор Lisp был загружен вручную путем компиляции функций конструктора и считывателя токенов. Остальная часть интерпретатора была затем прочитана из источника.

Вы можете убедиться в этом сами, прочитав оригинальную статью Маккарти « Рекурсивные функции символьных выражений и их машинное вычисление, часть I» .

Люзер Дрог
источник
Что случилось с частями 2 и 3? ... Как я не заметил, что @Wing опубликовал то же самое за 3 года до меня? Я тупица. По крайней мере, я связал бумагу (с помощью).
luser droog
2

Другой альтернативой является создание машины для байт-кода для вашего языка (или использование существующей машины, если ее функции не очень необычны) и запись компилятора в байт-код, либо в байт-коде, либо на желаемом языке с использованием другого промежуточного звена, такого как набор инструментов синтаксического анализатора, который выводит AST как XML, а затем компилирует XML в байт-код с помощью XSLT (или другого языка сопоставления с образцом и представления на основе дерева). Это не устраняет зависимости от другого языка, но может означать, что большая часть работы по начальной загрузке завершится в окончательной системе.

Пит Киркхэм
источник
2

Это компьютерная версия парадокса курицы и яйца. Я не могу придумать, как не писать исходный компилятор на ассемблере или другом языке. Если бы это было возможно, я бы сделал это в Лиспе.

На самом деле, я думаю, что Lisp почти подходит. Посмотрите его запись в Википедии . Согласно статье, функция eval в Лиспе может быть реализована на IBM 704 в машинном коде, а полный компилятор (написанный на самом Лиспе) появится в 1962 году в Массачусетском технологическом институте .

Крыло
источник
2

Каждый пример начальной загрузки языка, о котором я могу думать ( C , PyPy ), был сделан после того, как появился рабочий компилятор. Вы должны с чего-то начать, а для повторной реализации языка нужно сначала написать компилятор на другом языке.

Как еще это могло бы работать? Я не думаю, что иначе вообще возможно.

Адам Лассек
источник
4
По крайней мере, первый компилятор Lisp загружался с использованием существующего интерпретатора Lisp . Так что семантически не другой язык, а реализация на другом языке.
Кен
0

Некоторые загрузочные компиляторы или системы сохраняют как исходную, так и объектную форму в своем репозитории:

  • ocaml - это язык, который имеет как интерпретатор байт-кода (т.е. компилятор для байт-кода Ocaml), так и собственный компилятор (для x86-64 или ARM и т. д. ассемблер). Его репозиторий svn содержит как исходный код (файлы */*.{ml,mli}), так и boot/ocamlcформу байт-кода (файл ) компилятора. Поэтому при сборке он сначала использует свой байт-код (предыдущей версии компилятора) для компиляции самого себя. Позже только что скомпилированный байт-код может скомпилировать собственный компилятор. Итак, репозиторий Ocaml svn содержит как *.ml[i]исходные файлы, так и boot/ocamlcфайл байт-кода.

  • В ржавчину загрузки компилятора ( с использованием wget, так что вам нужно подключение к Интернету рабочий) предыдущую версию двоичного файла компилироваться.

  • MELT - это Lisp-подобный язык для настройки и расширения GCC . Он переводится в код C ++ с помощью загрузочного переводчика. Сгенерированный C ++ код переводчика распространяется, поэтому репозиторий svn содержит как *.meltисходные файлы, так и melt/generated/*.cc«объектные» файлы переводчика.

  • Система искусственного интеллекта CAIA, разработанная Дж. Питратом, полностью самогенерируется. Он доступен в виде набора из тысяч [A-Z]*.cсгенерированных файлов (также со сгенерированным dx.hфайлом заголовка) с набором из тысяч _[0-9]*файлов данных.

  • Также загружаются несколько компиляторов схем. Схема48, Схема с курицей, ...

Василий Старынкевич
источник