Краткий ответ: вы правы в своем подозрении, вам всегда нужен либо другой интерпретатор, написанный на X, либо компилятор с Y на другой язык, для которого у вас уже есть переводчик. Интерпретаторы выполняются, компиляторы переводят только с одного языка на другой, в какой-то момент в вашей системе должен быть интерпретатор… даже это просто процессор.
Независимо от того, сколько новых переводчиков вы пишете на языке Y , вам всегда придется использовать первый интерпретатор, написанный на X, для интерпретации последующих интерпретаторов. Это кажется проблемой просто из-за природы переводчиков.
Правильный. Что вы можете сделать , это написать компилятор от Y к X (или другой язык , для которого у вас есть переводчик), и вы даже можете сделать это в Y . Затем вы можете запустить свой компилятор Y, написанный на Y, на интерпретаторе Y, написанном на X (или на интерпретаторе Y, написанном на Y, работающем на интерпретаторе Y, написанном на X , или на интерпретаторе Y, написанном на Y, на интерпретаторе Y, написанном на Y работает на Yинтерпретатор, написанный на X , или… до бесконечности) для компиляции вашего интерпретатора Y, написанного на Y в X , чтобы вы могли затем выполнить его на интерпретаторе X. Таким образом, вы избавились от своего интерпретатора Y, написанного на X , но теперь вам нужен интерпретатор X (хотя мы знаем, что он у нас уже есть, поскольку в противном случае мы не смогли бы запустить интерпретатор X, написанный на Y ), и вы пришлось написать Y -До- Х -compiler первым.
Однако , с другой стороны, статья в Википедии о переводчиках на самом деле говорит о переводчиках с собственным хостингом. Вот небольшая выдержка, которая имеет отношение к делу:
Интерпретатор - это интерпретатор языка программирования, написанный на языке программирования, который может интерпретировать сам себя; Примером является интерпретатор BASIC, написанный на BASIC. Автопереводчики связаны с самодостаточными компиляторами.
Если для интерпретируемого языка не существует компилятора, создание самоинтерпретатора требует реализации языка на основном языке (который может быть другим языком программирования или ассемблером). При наличии первого переводчика, такого как этот, система загружается и новые версии интерпретатора могут быть разработаны на самом языке.
Мне все еще не ясно, как именно это будет сделано. Кажется, что несмотря ни на что, вы всегда будете вынуждены использовать первую версию вашего переводчика, написанную на языке хоста.
Правильный. Обратите внимание, что в статье в Википедии прямо говорится, что вам нужна вторая реализация вашего языка, и не говорится, что вы можете избавиться от первой.
Теперь упомянутая выше статья ссылается на другую статью, в которой Википедия приводит несколько примеров предполагаемых самостоятельных переводчиков. Тем не менее, при ближайшем рассмотрении кажется, что основная часть «интерпретации» многих из этих автономных интерпретаторов (особенно некоторых из наиболее распространенных, таких как PyPy или Rubinius) фактически написана на других языках, таких как C ++ или C.
Опять правильно. Это действительно плохие примеры. Взять, к примеру, Рубиния. Да, это правда, что Ruby-часть Rubinius размещается самостоятельно, но это компилятор, а не интерпретатор: он компилируется в исходный код Ruby в байт-код Rubinius. Часть интерпретатора OTOH не является самостоятельной: она интерпретирует байт-код Rubinius, но она написана на C ++. Таким образом, называть Рубиниуса «переводчиком с собственным интерфейсом » неверно: часть с самостоятельным размещением не является переводчиком , а часть с переводчиком не является самостоятельной .
PyPy похож, но еще более некорректен: в первую очередь он даже не написан на Python, он написан на RPython, который является другим языком. Он синтаксически похож на Python, семантически является «расширенным подмножеством», но на самом деле это статически типизированный язык, примерно на том же уровне абстракции, что и Java, и его реализация представляет собой компилятор с несколькими бэкэндами, который компилирует RPython в исходный код C, ECMAScript исходный код, байт-код CIL, байт-код JVM или исходный код Python.
Так возможно ли то, что я описал выше? Может ли интерпретатор с собственным хостом быть независимым от своего исходного хоста? Если так, как именно это будет сделано?
Нет, не по себе. Вам нужно будет либо сохранить исходный интерпретатор, либо написать компилятор и скомпилировать свой интерпретатор.
Там являются некоторые мета-круговой виртуальные машины, такие как Кляйн (написано в Самости ) и Максин (написанный на Java). Обратите внимание, однако, что здесь определение «мета-циклический» все же отличается: эти виртуальные машины не написаны на языке, который они выполняют: Klein выполняет байт-код Self, но пишется в Self, Maxine выполняет байт-код JVM, но пишется в Java. Однако исходный код Self / Java виртуальной машины фактически компилируется в байт-код Self / JVM, а затем исполняется виртуальной машиной, поэтому к тому моменту, когда виртуальная машина исполняется, она находится на языке, который она выполняет. Уф.
Также обратите внимание, что это отличается от виртуальных машин, таких как SqueakVM и Jikes RVM . Jikes написан на Java, а SqueakVM написан на Slang (статически типизированное синтаксическое и семантическое подмножество Smalltalk примерно на том же уровне абстракции, что и высокоуровневый ассемблер), и оба статически компилируются в нативный код перед запуском. Они не бегают внутри себя. Однако вы можете запустить их поверх себя (или поверх другой виртуальной машины / виртуальной машины Smalltalk). Но это не «мета-циркуляр» в этом смысле.
Максин и Кляйн, OTOH делаютбеги внутрь себя; они выполняют свой собственный байт-код, используя свою собственную реализацию. Это действительно умопомрачительно! Это дает некоторые интересные возможности оптимизации, например, поскольку виртуальная машина выполняет себя вместе с пользовательской программой, она может выполнять встроенные вызовы от пользовательской программы к виртуальной машине и наоборот, например, вызов сборщика мусора или распределителя памяти может быть встроен в пользователя. код и отражающие обратные вызовы в пользовательском коде могут быть встроены в виртуальную машину. Кроме того, все хитрые приемы оптимизации, которые делают современные виртуальные машины, когда они следят за выполнением программы и оптимизируют ее в зависимости от фактической рабочей нагрузки и данных, виртуальная машина может применять те же приемы к себе, пока она выполняет пользовательскую программу, в то время как пользовательская программа выполняет конкретную рабочую нагрузку. Другими словами, виртуальная машина специализировалась на этомконкретная программа, выполняющая эту конкретную нагрузку.
Тем не менее, обратите внимание, что я обходил использование слова «интерпретатор» выше, и всегда использовал «выполнить»? Ну, эти виртуальные машины построены не на интерпретаторах, а на компиляторах (JIT). Позже к Maxine был добавлен интерпретатор, но вам всегда нужен компилятор: вам нужно запустить виртуальную машину один раз поверх другой виртуальной машины (например, Oracle HotSpot в случае Maxine), чтобы виртуальная машина могла (JIT) компилировать себя. В случае Maxine он JIT скомпилирует свою фазу загрузки, затем сериализует этот скомпилированный нативный код в образ виртуальной машины начальной загрузки и вставит в него очень простой загрузчик (единственный компонент виртуальной машины, написанный на C, хотя это только для удобства). , это может быть в Java также). Теперь вы можете использовать Maxine, чтобы выполнить себя.
Вы правы, отметив, что интерпретатору, выполняющему сам хост, по-прежнему требуется интерпретатор для запуска самого себя, и он не может быть загружен в том же смысле, что и компилятор.
Тем не менее, самодостаточный язык не то же самое, что самозанятый переводчик. Обычно легче создать интерпретатор, чем создать компилятор. Поэтому, чтобы реализовать новый язык, мы могли бы сначала реализовать интерпретатор на несвязанном языке. Затем мы можем использовать этот интерпретатор для разработки компилятора для нашего языка. Затем язык размещается самостоятельно, поскольку компилятор интерпретируется. Затем компилятор может скомпилировать себя и затем может считаться полностью загруженным.
Частным случаем этого является среда JIT-компиляции с собственным хостингом. Он может начинаться с интерпретатора на главном языке, который затем использует новый язык для реализации JIT-компиляции, после чего JIT-компилятор может компилировать себя. Это похоже на самодостаточного переводчика, но обходит проблему бесконечных интерпретаторов. Этот подход используется, но пока не очень распространен.
Еще одна связанная техника - расширяемый интерпретатор, где мы можем создавать расширения на языке, который интерпретируется. Например, мы можем реализовать новые коды операций на языке. Это может превратить простой интерпретатор в многофункциональный интерпретатор, если мы избегаем циклических зависимостей.
Случай, который на самом деле встречается довольно часто, - это способность языка влиять на собственный синтаксический анализ, например, в качестве макрокоманд, оцениваемых по времени анализа. Поскольку макроязык совпадает с языком, обрабатываемым, он имеет тенденцию быть гораздо более функциональным, чем выделенные или ограниченные макроязыки. Тем не менее, правильно отметить, что язык, выполняющий расширение, немного отличается от языка после расширения.
Когда используются «настоящие» переводчики с собственным участием, это обычно делается по соображениям образования или исследования. Например, реализация интерпретатора для Scheme внутри Scheme - это крутой способ обучения языкам программирования (см. SICP).
источник