stringtranslate.com

Переводчик (вычислительная техника)

W3sDesign Interpreter Шаблон проектирования UML

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

  1. Анализировать исходный код и выполнять его поведение напрямую;
  2. Преобразовать исходный код в некоторое эффективное промежуточное представление или объектный код и немедленно выполнить его;
  3. Явно выполнить сохраненный предварительно скомпилированный байт-код [1], созданный компилятором и сопоставленный с виртуальной машиной интерпретатора .

Ранние версии языка программирования Lisp и диалекты BASIC для мини- и микрокомпьютеров являются примерами первого типа. Perl , Raku , Python , MATLAB и Ruby являются примерами второго типа, в то время как UCSD Pascal является примером третьего типа. Исходные программы компилируются заранее и сохраняются как машинно-независимый код, который затем связывается во время выполнения и выполняется интерпретатором и/или компилятором (для систем JIT ). Некоторые системы, такие как Smalltalk и современные версии BASIC и Java , также могут объединять два и три типа. [2] Интерпретаторы различных типов также были построены для многих языков, традиционно связанных с компиляцией, таких как Algol , Fortran , Cobol , C и C++ .

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

История

Интерпретаторы использовались еще в 1952 году для облегчения программирования в условиях ограничений компьютеров того времени (например, нехватка места для хранения программ или отсутствие встроенной поддержки чисел с плавающей точкой). Интерпретаторы также использовались для перевода между низкоуровневыми машинными языками, что позволяло писать код для машин, которые все еще находились в стадии разработки и тестировались на уже существующих компьютерах. [3] Первым интерпретируемым языком высокого уровня был Lisp . Lisp был впервые реализован Стивом Расселом на компьютере IBM 704. Рассел прочитал статью Джона Маккарти «Рекурсивные функции символических выражений и их вычисление машиной, часть I» и понял (к удивлению Маккарти), что функция eval языка Lisp может быть реализована в машинном коде. [4] Результатом стал работающий интерпретатор Lisp, который можно было использовать для запуска программ на Lisp или, точнее, «оценки выражений Lisp».

Общая операция

Интерпретатор обычно состоит из набора известных команд, которые он может выполнить , и списка этих команд в том порядке, в котором программист хочет их выполнить. Каждая команда (также известная как Инструкция ) содержит данные, которые программист хочет изменить, и информацию о том, как изменить данные. Например, интерпретатор может прочитать ADD Books, 5и интерпретировать ее как запрос на добавление пяти к Books переменной .

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

Компиляторы против интерпретаторов

Иллюстрация процесса связывания. Объектные файлы и статические библиотеки собираются в новую библиотеку или исполняемый файл.

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

В то время как компиляторы (и ассемблеры) обычно производят машинный код, непосредственно исполняемый компьютерным оборудованием, они часто (опционально) могут производить промежуточную форму, называемую объектным кодом . Это в основном тот же машинно-специфичный код, но дополненный таблицей символов с именами и тегами, чтобы сделать исполняемые блоки (или модули) идентифицируемыми и перемещаемыми. Скомпилированные программы обычно используют строительные блоки (функции), хранящиеся в библиотеке таких модулей объектного кода. Компоновщик используется для объединения (заранее созданных) библиотечных файлов с объектными файлами приложения для формирования одного исполняемого файла. Таким образом, объектные файлы, которые используются для генерации исполняемого файла, часто создаются в разное время, а иногда даже разными языками (способными генерировать тот же формат объекта).

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

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

Таким образом, компилятор может выполнить почти все преобразования из семантики исходного кода на машинный уровень раз и навсегда (т. е. до тех пор, пока программа не будет изменена), в то время как интерпретатор должен выполнять часть этой работы по преобразованию каждый раз, когда выполняется оператор или функция. Однако в эффективном интерпретаторе большая часть работы по переводу (включая анализ типов и тому подобное) выносится за скобки и выполняется только при первом запуске программы, модуля, функции или даже оператора, таким образом, весьма похоже на то, как работает компилятор. Однако скомпилированная программа все равно работает намного быстрее, в большинстве случаев, отчасти потому, что компиляторы предназначены для оптимизации кода и могут иметь достаточно времени для этого. Это особенно верно для более простых языков высокого уровня без (многих) динамических структур данных, проверок или проверки типов .

При традиционной компиляции исполняемый вывод компоновщиков ( файлы .exe или .dll или библиотека, см. рисунок) обычно перемещаем при запуске под управлением общей операционной системы, подобно модулям объектного кода, но с той разницей, что это перемещение выполняется динамически во время выполнения, т. е. когда программа загружается для выполнения. С другой стороны, скомпилированные и скомпонованные программы для небольших встраиваемых систем обычно статически распределены, часто жестко закодированы во флэш-памяти NOR , поскольку часто нет вторичного хранилища и операционной системы в этом смысле.

Исторически большинство систем интерпретаторов имели встроенный автономный редактор. Это становится все более распространенным и для компиляторов (тогда часто называемых IDE ), хотя некоторые программисты предпочитают использовать редактор по своему выбору и запускать компилятор, компоновщик и другие инструменты вручную. Исторически компиляторы появились раньше интерпретаторов, поскольку оборудование в то время не могло поддерживать как интерпретатор, так и интерпретируемый код, а типичная пакетная среда того времени ограничивала преимущества интерпретации. [5]

Цикл разработки

В течение цикла разработки программного обеспечения программисты часто вносят изменения в исходный код. При использовании компилятора, каждый раз, когда в исходный код вносятся изменения, они должны ждать, пока компилятор переведет измененные исходные файлы и свяжет все файлы двоичного кода вместе, прежде чем программа сможет быть выполнена. Чем больше программа, тем дольше ожидание. Напротив, программист, использующий интерпретатор, ждет гораздо меньше, так как интерпретатору обычно нужно просто перевести код, над которым ведется работа, в промежуточное представление (или не переводить его вообще), таким образом, требуя гораздо меньше времени, прежде чем изменения можно будет протестировать. Эффекты очевидны при сохранении исходного кода и перезагрузке программы. Скомпилированный код, как правило, сложнее отлаживать, поскольку редактирование, компиляция и компоновка являются последовательными процессами, которые должны выполняться в правильной последовательности с помощью правильного набора команд. По этой причине многие компиляторы также имеют исполнительную помощь, известную как Makefile и программа. Makefile перечисляет командные строки компилятора и компоновщика, а также файлы исходного кода программы, но может принимать простой ввод меню командной строки (например, «Make 3»), который выбирает третью группу (набор) инструкций, а затем выдает команды компилятору и компоновщику, которые передают указанные файлы исходного кода.

Распределение

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

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

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

Эффективность

Главным недостатком интерпретаторов является то, что интерпретируемая программа обычно работает медленнее, чем если бы она была скомпилирована . Разница в скорости может быть незначительной или большой; часто на порядок, а иногда и больше. Обычно запуск программы под интерпретатором занимает больше времени, чем запуск скомпилированного кода, но на ее интерпретацию может уйти меньше времени, чем на ее компиляцию и запуск. Это особенно важно при прототипировании и тестировании кода, когда цикл редактирование-интерпретация-отладка часто может быть намного короче цикла редактирование-компиляция-запуск-отладка. [6] [7]

Интерпретация кода медленнее, чем запуск скомпилированного кода, потому что интерпретатор должен анализировать каждый оператор в программе каждый раз, когда он выполняется, а затем выполнять желаемое действие, тогда как скомпилированный код просто выполняет действие в фиксированном контексте, определенном компиляцией. Этот анализ во время выполнения известен как «интерпретационные накладные расходы». Доступ к переменным также медленнее в интерпретаторе, потому что сопоставление идентификаторов с местами хранения должно выполняться повторно во время выполнения, а не во время компиляции . [6]

Существуют различные компромиссы между скоростью разработки при использовании интерпретатора и скоростью выполнения при использовании компилятора. Некоторые системы (например, некоторые Lisp ) позволяют интерпретируемому и скомпилированному коду вызывать друг друга и совместно использовать переменные. Это означает, что после того, как процедура была протестирована и отлажена в интерпретаторе, ее можно скомпилировать и, таким образом, получить выгоду от более быстрого выполнения, пока разрабатываются другие процедуры. [ необходима цитата ] Многие интерпретаторы не выполняют исходный код в его нынешнем виде, а преобразуют его в некоторую более компактную внутреннюю форму. Многие интерпретаторы BASIC заменяют ключевые слова однобайтовыми токенами , которые можно использовать для поиска инструкции в таблице переходов . [6] Некоторые интерпретаторы, такие как интерпретатор PBASIC , достигают еще более высоких уровней сжатия программ, используя бит-ориентированную, а не байт-ориентированную структуру памяти программ, где токены команд занимают, возможно, 5 бит, номинально «16-битные» константы хранятся в коде переменной длины, требующем 3, 6, 10 или 18 бит, а операнды адреса включают «битовое смещение». Многие интерпретаторы BASIC могут сохранять и считывать свое собственное токенизированное внутреннее представление.

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

Регрессия

Интерпретация не может использоваться как единственный метод выполнения: даже если интерпретатор сам по себе может быть интерпретирован и т. д., непосредственно выполняемая программа необходима где-то в нижней части стека, поскольку интерпретируемый код по определению не является тем же самым, что и машинный код, который может выполнить ЦП. [8] [9]

Вариации

Интерпретаторы байт-кода

Существует спектр возможностей между интерпретацией и компиляцией, в зависимости от объема анализа, выполненного перед выполнением программы. Например, Emacs Lisp компилируется в байт-код , который является сильно сжатым и оптимизированным представлением исходного кода Lisp, но не является машинным кодом (и, следовательно, не привязан к какому-либо конкретному оборудованию). Этот «скомпилированный» код затем интерпретируется интерпретатором байт-кода (который сам написан на C ). Скомпилированный код в этом случае является машинным кодом для виртуальной машины , который реализован не на оборудовании, а в интерпретаторе байт-кода. Такие компилирующие интерпретаторы иногда также называются компретерами . [10] [11] В интерпретаторе байт-кода каждая инструкция начинается с байта, и, следовательно, интерпретаторы байт-кода имеют до 256 инструкций, хотя не все они могут быть использованы. Некоторые байт-коды могут занимать несколько байтов и могут быть произвольно сложными.

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

Поточные интерпретаторы кода

Поточные интерпретаторы кода похожи на интерпретаторы байт-кода, но вместо байтов они используют указатели. Каждая «инструкция» — это слово, которое указывает на функцию или последовательность инструкций, за которой может следовать параметр. Поточный интерпретатор кода либо циклически извлекает инструкции и вызывает функции, на которые они указывают, либо извлекает первую инструкцию и переходит к ней, а каждая последовательность инструкций заканчивается извлечением и переходом к следующей инструкции. В отличие от байт-кода, нет эффективного ограничения на количество различных инструкций, кроме доступной памяти и адресного пространства. Классическим примером потокового кода является код Forth , используемый в системах Open Firmware : исходный язык компилируется в «код F» (байт-код), который затем интерпретируется виртуальной машиной . [ требуется цитата ]

Интерпретаторы абстрактного синтаксического дерева

В спектре между интерпретацией и компиляцией другой подход заключается в преобразовании исходного кода в оптимизированное абстрактное синтаксическое дерево (AST), затем выполнении программы в соответствии с этой древовидной структурой или использовании ее для генерации собственного кода just-in-time . [12] При таком подходе каждое предложение должно быть проанализировано только один раз. В качестве преимущества перед байт-кодом AST сохраняет глобальную структуру программы и отношения между операторами (которые теряются в представлении байт-кода), а при сжатии обеспечивает более компактное представление. [13] Таким образом, использование AST было предложено в качестве лучшего промежуточного формата для компиляторов just-in-time, чем байт-код. Кроме того, это позволяет системе выполнять лучший анализ во время выполнения.

Однако для интерпретаторов AST вызывает больше накладных расходов, чем интерпретатор байт-кода, из-за узлов, связанных с синтаксисом, не выполняющих никакой полезной работы, из-за менее последовательного представления (требующего обхода большего количества указателей) и из-за накладных расходов на посещение дерева. [14]

Компиляция точно в срок

Еще больше размывает различие между интерпретаторами, интерпретаторами байт-кода и компиляцией компиляция «точно в срок» (JIT), метод, при котором промежуточное представление компилируется в машинный код во время выполнения. Это обеспечивает эффективность выполнения машинного кода за счет времени запуска и увеличения использования памяти при первой компиляции байт-кода или AST. Самый ранний опубликованный JIT-компилятор обычно приписывается работе Джона Маккарти над LISP в 1960 году. [15] Адаптивная оптимизация — это дополнительный метод, при котором интерпретатор профилирует запущенную программу и компилирует ее наиболее часто выполняемые части в машинный код. Последний метод существует несколько десятилетий и появился в таких языках, как Smalltalk, в 1980-х годах. [16]

В последние годы компиляция JIT привлекла всеобщее внимание разработчиков языков, в том числе Java , .NET Framework , большинство современных реализаций JavaScript и Matlab , которые теперь включают JIT-компиляторы. [ необходима ссылка ]

Интерпретатор шаблонов

Еще больше размывает различие между компиляторами и интерпретаторами специальная конструкция интерпретатора, известная как шаблонный интерпретатор. Вместо того, чтобы реализовать выполнение кода посредством большого оператора switch, содержащего все возможные байт-коды, при работе с программным стеком или обходом дерева, шаблонный интерпретатор поддерживает большой массив байт-кода (или любого эффективного промежуточного представления), напрямую сопоставленного с соответствующими машинными инструкциями, которые могут быть выполнены на оборудовании хоста как пары ключ-значение (или в более эффективных конструкциях прямые адреса к машинным инструкциям), [17] [18], известный как «Шаблон». Когда выполняется определенный сегмент кода, интерпретатор просто загружает или переходит к сопоставлению кода операции в шаблоне и напрямую запускает его на оборудовании. [19] [20] Благодаря своей конструкции интерпретатор шаблонов очень сильно напоминает компилятор just-in-time, а не традиционный интерпретатор, однако технически он не является JIT из-за того, что он просто транслирует код из языка в собственные вызовы по одному коду операции за раз, а не создает оптимизированные последовательности исполняемых инструкций ЦП из всего сегмента кода. Благодаря простой конструкции интерпретатора, заключающейся в простой передаче вызовов непосредственно на оборудование, а не в их прямой реализации, он намного быстрее любого другого типа, даже интерпретаторов байт-кода, и в некоторой степени менее подвержен ошибкам, но в качестве компромисса его сложнее поддерживать из-за того, что интерпретатор должен поддерживать трансляцию в несколько различных архитектур вместо независимой от платформы виртуальной машины/стека. На сегодняшний день единственными существующими реализациями интерпретатора шаблонов широко известных языков являются интерпретатор в официальной эталонной реализации Java, Sun HotSpot Java Virtual Machine, [17] и интерпретатор Ignition в движке выполнения javascript Google V8.

Самоинтерпретатор

Самоинтерпретатор — это интерпретатор языка программирования , написанный на языке программирования, который может интерпретировать сам себя; примером является интерпретатор BASIC , написанный на BASIC. Самоинтерпретаторы связаны с саморазмещающимися компиляторами .

Если для интерпретируемого языка не существует компилятора , создание самоинтерпретатора требует реализации языка на хостовом языке (который может быть другим языком программирования или ассемблером ). При наличии первого интерпретатора, такого как этот, система загружается , и новые версии интерпретатора могут быть разработаны на самом языке. Именно таким образом Дональд Кнут разработал интерпретатор TANGLE для языка WEB де-факто стандартной системы набора TeX .

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

Важным аспектом проектирования при реализации самоинтерпретатора является то, реализована ли функция интерпретируемого языка с помощью той же функции в языке-хозяине интерпретатора. Примером может служить реализация замыкания в языке типа Lisp с использованием замыканий в языке-интерпретаторе или реализация «вручную» с помощью структуры данных, явно хранящей среду. Чем больше функций реализовано одной и той же функцией в языке-хозяине, тем меньше контроля у программиста интерпретатора; например, нельзя реализовать другое поведение для обработки переполнений чисел, если арифметические операции делегированы соответствующим операциям в языке-хозяине.

Некоторые языки, такие как Lisp и Prolog, имеют элегантные самоинтерпретаторы. [21] Много исследований самоинтерпретаторов (особенно рефлексивных интерпретаторов) было проведено в языке программирования Scheme , диалекте Lisp. Однако в целом любой полный по Тьюрингу язык позволяет писать свой собственный интерпретатор. Lisp является таким языком, потому что программы на Lisp представляют собой списки символов и других списков. XSLT является таким языком, потому что программы XSLT написаны на XML. Подобласть метапрограммирования — написание предметно-ориентированных языков (DSL).

Клайв Гиффорд ввел [22] меру качества самоинтерпретатора (собственное отношение), предел отношения между временем компьютера, потраченным на запуск стека из N самоинтерпретаторов, и временем, потраченным на запуск стека из N − 1 самоинтерпретаторов, когда N стремится к бесконечности. Это значение не зависит от запускаемой программы.

В книге «Структура и интерпретация компьютерных программ» представлены примеры метациклической интерпретации для Scheme и его диалектов. Другими примерами языков с самоинтерпретатором являются Forth и Pascal .

Микрокод

Микрокод — это очень часто используемая техника, «которая навязывает интерпретатор между аппаратным обеспечением и архитектурным уровнем компьютера». [23] Таким образом, микрокод — это слой инструкций аппаратного уровня, которые реализуют инструкции машинного кода более высокого уровня или внутреннюю последовательность состояний машины во многих элементах цифровой обработки . Микрокод используется в центральных процессорах общего назначения , а также в более специализированных процессорах, таких как микроконтроллеры , цифровые сигнальные процессоры , контроллеры каналов , контроллеры дисков , контроллеры сетевых интерфейсов , сетевые процессоры , графические процессоры и в другом оборудовании.

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

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

Процессор компьютера

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

Понимание эффективности работы переводчика

Интерпретаторы, такие как написанные на Java, Perl и Tcl, теперь необходимы для широкого спектра вычислительных задач, включая двоичную эмуляцию и интернет-приложения. Производительность интерпретаторов по-прежнему вызывает беспокойство, несмотря на их адаптивность, особенно в системах с ограниченными аппаратными ресурсами. Расширенные подходы к инструментированию и трассировке дают представление о реализациях интерпретаторов и использовании ресурсов процессора во время выполнения посредством оценок интерпретаторов, адаптированных для набора инструкций MIPS и языков программирования, таких как Tcl, Perl и Java. Характеристики производительности зависят от сложности интерпретатора, как показано в сравнении с скомпилированным кодом. Очевидно, что производительность интерпретатора больше зависит от нюансов и потребностей в ресурсах интерпретатора, чем от конкретного приложения, которое интерпретируется.

Приложения

Смотрите также

Ссылки

  1. ^ В этом смысле ЦП также является интерпретатором машинных инструкций.
  2. ^ Хотя эта схема (объединяющая стратегии 2 и 3) использовалась для реализации некоторых интерпретаторов BASIC уже в 1970-х годах, например, эффективного интерпретатора BASIC ABC 80 .
  3. ^ Беннетт, Дж. М.; Принц, Д. Г.; Вудс, М. Л. (1952). «Интерпретативные подпрограммы». Труды Национальной конференции ACM, Торонто .
  4. ^ Согласно сообщению Пола Грэма в Hackers & Painters , стр. 185, Маккарти сказал: «Стив Рассел сказал: слушай, почему бы мне не запрограммировать этот eval ..., и я сказал ему: хо-хо, ты путаешь теорию с практикой, этот eval предназначен для чтения, а не для вычислений. Но он пошел дальше и сделал это. То есть, он скомпилировал eval из моей статьи в машинный код IBM 704 , исправив ошибку , а затем разрекламировал это как интерпретатор Lisp, которым он, безусловно, и был. Так что в тот момент Lisp имел по сути ту форму, которую он имеет сегодня...»
  5. ^ «Почему первый компилятор был написан раньше первого интерпретатора?». Ars Technica . 8 ноября 2014 г. Получено 9 ноября 2014 г.
  6. ^ abc Эта статья основана на материале, взятом из Interpreter в Free On-line Dictionary of Computing до 1 ноября 2008 года и включенном в соответствии с условиями «перелицензирования» GFDL версии 1.3 или более поздней.
  7. ^ "Компиляторы против интерпретаторов: объяснение и различия". IONOS Digital Guide . Получено 2022-09-16 .
  8. ^ Теодор Х. Ромер, Деннис Ли, Джеффри М. Фолькер, Алек Уолман, Уэйн А. Вонг, Жан-Лу Баер, Брайан Н. Бершад и Генри М. Леви, Структура и эффективность работы переводчиков
  9. ^ Теренс Парр, Йоханнес Любер, Разница между компиляторами и интерпретаторами Архивировано 06.01.2014 на Wayback Machine
  10. ^ Кюнель, Клаус (1987) [1986]. «4. Кляйнкомпьютер — Eigenschaften und Möglichkeiten» [4. Микрокомпьютер - Свойства и возможности. В Эрлекампфе, Райнер; Монк, Ханс-Иоахим (ред.). Mikroelektronik in der любительской практики [ Микроэлектроника для практического любителя ] (на немецком языке) (3-е изд.). Берлин: Militärverlag der Deutschen Demokratischen Republik  [de] , Лейпциг. п. 222. ИСБН 3-327-00357-2. 7469332.
  11. ^ Хейн, Р. (1984). «Basic-Compreter für U880» [BASIC-компьютер для U880 (Z80)]. радио-фернсен-электроник  [ де ] (на немецком языке). 1984 (3): 150–152.
  12. ^ Промежуточные представления AST, форум Lambda the Ultimate
  13. ^ Кистлер, Томас; Франц, Майкл (февраль 1999 г.). «Деревообразная альтернатива байт-кодам Java» (PDF) . Международный журнал параллельного программирования . 27 (1): 21–33. CiteSeerX 10.1.1.87.2257 . doi :10.1023/A:1018740018601. ISSN  0885-7458. S2CID  14330985 . Получено 20.12.2020 . 
  14. ^ Surfin' Safari - Архив блога » Анонс SquirrelFish. Webkit.org (2008-06-02). Получено 2013-08-10.
  15. ^ Aycock 2003, 2. Методы компиляции JIT, 2.1 Genesis, стр. 98.
  16. ^ Л. Дойч, А. Шиффман, Эффективная реализация системы Smalltalk-80, Труды 11-го симпозиума POPL, 1984.
  17. ^ ab "openjdk/jdk". GitHub . 18 ноября 2021 г.
  18. ^ "Обзор среды выполнения HotSpot". Openjdk.java.net . Получено 2022-08-06 .
  19. ^ «Разоблачение JVM: варианты JVM, Cppinterpreter и TemplateInterpreter». metebalci.com .
  20. ^ "Интерпретатор шаблонов JVM". ProgrammerSought .
  21. ^ Бондорф, Андерс. «Logimix: самоприменяемый частичный оценщик для Пролога». Синтез и преобразование логических программ. Springer, Лондон, 1993. 214-227.
  22. ^ Гиффорд, Клайв. «Собственные отношения самоинтерпретаторов». Blogger . Получено 10 ноября 2019 г.
  23. ^ Кент, Аллен; Уильямс, Джеймс Г. (5 апреля 1993 г.). Энциклопедия компьютерных наук и технологий: Том 28 - Приложение 13. Нью-Йорк: Marcel Dekker, Inc. ISBN 0-8247-2281-7. Получено 17 января 2016 г. .

Источники

Внешние ссылки