Можно ли создать «загруженный» интерпретатор независимо от исходного интерпретатора?


21

Согласно Википедии, термин «самозагрузка» в контексте написания компиляторов означает это :

В информатике начальная загрузка - это процесс написания компилятора (или ассемблера) на исходном языке программирования, который он намеревается скомпилировать. Применение этой методики приводит к самостоятельному компилятору.

И я могу понять, как это будет работать. Тем не менее, история кажется немного другой для переводчиков. Теперь, конечно, можно написать переводчик-хостинг. Это не то, что я спрашиваю. На самом деле я спрашиваю: возможно ли сделать независимого переводчика независимым от первоначального, первого переводчика ? Чтобы объяснить, что я имею в виду, рассмотрим этот пример:

Вы пишете свой первый вариант интерпретатора в языке X , и интерпретатор для нового языка , который вы создаете, называется Y . Сначала вы используете компилятор языка X для создания исполняемого файла. Теперь вы можете интерпретировать файлы , написанные на новом языке Y , используя интерпретатор , написанный на языке X .

Теперь, насколько я понимаю, чтобы быть в состоянии «начальной загрузки» интерпретатора вы писали на языке X , вам нужно переписать интерпретатор в языке Y . Но вот загвоздка: даже если вы переписать весь интерпретатор в языке Y , вы все еще будете нуждаться в оригинальный интерпретатор вы писали на языке X . Потому что для запуска переводчика на языке Y вам придется интерпретировать исходные файлы. Но что именно собирается интерпретировать исходные файлы? Ну, конечно, это не может быть ничто, поэтому вы все равно вынуждены пользоваться первым переводчиком.

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

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

Интерпретатор - это интерпретатор языка программирования, написанный на языке программирования, который может интерпретировать сам себя; Примером является интерпретатор BASIC, написанный на BASIC. Автопереводчики связаны с самодостаточными компиляторами.

Если для интерпретируемого языка не существует компилятора, создание самоинтерпретатора требует реализации языка на основном языке (который может быть другим языком программирования или ассемблером). При наличии первого переводчика, такого как этот, система загружается и новые версии интерпретатора могут быть разработаны на самом языке.

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

Теперь упомянутая выше статья ссылается на другую статью, в которой Википедия приводит несколько примеров предполагаемых самостоятельных переводчиков . Тем не менее, при ближайшем рассмотрении кажется, что основная часть «интерпретации» многих из этих автономных интерпретаторов (особенно некоторые из наиболее распространенных, таких как PyPy или Rubinius) фактически написаны на других языках, таких как C ++ или C.

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

Ответы:


24

Краткий ответ: вы правы в своем подозрении, вам всегда нужен либо другой интерпретатор, написанный на 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, чтобы выполнить себя.


Иш . Я никогда не знал, что мир переводчиков-самоучек был таким липким! Спасибо за хороший обзор, хотя.
Кристиан Дин

1
Хаха, ну почему мир должен быть менее изнурительным, чем концепция? ;-)
Jörg W Mittag

3
Я предполагаю, что одна из проблем заключается в том, что люди часто играют быстро и свободно с вовлеченными языками. Например, Rubinius обычно называют «Ruby in Ruby», но это только половина дела. Да, строго говоря , компилятор Ruby в Rubinius написан на Ruby, а виртуальная машина, которая выполняет байт-код, - нет. И что еще хуже: PyPy часто называют «Python in Python», за исключением того, что на самом деле там нет ни одной строки Python. Все это написано на RPython, который предназначен для знакомых программистам Python, но не на Python . Точно так же SqueakVM: это не написано на Smalltalk, это…
Йорг W Mittag

… Написан на сленге, который, по словам людей, которые на самом деле его закодировали, еще хуже, чем С, в своих возможностях абстракции. Единственное преимущество Slang состоит в том, что он является подходящим подмножеством Smalltalk, что означает, что вы можете разработать его (и запустить и, что самое важное, отладить ВМ) на мощной среде Smalltalk IDE.
Йорг Миттаг

2
Просто добавим еще одну сложность: некоторые интерпретируемые языки (например, FORTH и, возможно, TeX) способны записывать загружаемый образ памяти работающей системы в виде исполняемого файла. В этом смысле такие системы могут работать без оригинального интерпретатора. Например, однажды я написал интерпретатор FORTH, используя 16-разрядную версию FORTH для «кросс-интерпретации» 32-разрядной версии для другого ЦП и для запуска в другой ОС. (Обратите внимание, что язык FORTH включает в себя собственный ассемблер, поэтому «FORTH VM» (обычно это всего лишь 10 или 20 инструкций машинного кода) может быть написана в самом FORTH.)
alephzero

7

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

Тем не менее, самодостаточный язык не то же самое, что самозанятый переводчик. Обычно легче создать интерпретатор, чем создать компилятор. Поэтому, чтобы реализовать новый язык, мы могли бы сначала реализовать интерпретатор на несвязанном языке. Затем мы можем использовать этот интерпретатор для разработки компилятора для нашего языка. Затем язык размещается самостоятельно, поскольку компилятор интерпретируется. Затем компилятор может скомпилировать себя и затем может считаться полностью загруженным.

Частным случаем этого является среда JIT-компиляции с собственным хостингом. Он может начинаться с интерпретатора на главном языке, который затем использует новый язык для реализации JIT-компиляции, после чего JIT-компилятор может компилировать себя. Это похоже на самодостаточного переводчика, но обходит проблему бесконечных интерпретаторов. Этот подход используется, но пока не очень распространен.

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

Случай, который на самом деле встречается довольно часто, - это способность языка влиять на собственный синтаксический анализ, например, в качестве макрокоманд, оцениваемых по времени анализа. Поскольку макроязык совпадает с языком, обрабатываемым, он имеет тенденцию быть гораздо более функциональным, чем выделенные или ограниченные макроязыки. Тем не менее, правильно отметить, что язык, выполняющий расширение, немного отличается от языка после расширения.

Когда используются «настоящие» переводчики с собственным участием, это обычно делается по соображениям образования или исследования. Например, реализация интерпретатора для Scheme внутри Scheme - это крутой способ обучения языкам программирования (см. SICP).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.