Можно ли классифицировать каждый язык как скомпилированный или интерпретированный?

Можно ли классифицировать каждый язык как скомпилированный или интерпретированный?

Ответ на ваш вопрос:

Можно ли классифицировать каждый язык как скомпилированный или интерпретированный?

«Нет», но не по той причине, по которой вы думаете. Причина не в том, что существует третья пропущенная категория, а в том, что сама классификация бессмысленна .

Нет такой вещи как «скомпилированный язык» или «интерпретируемый язык». Эти термины даже не ошибочны, они бессмысленны.

Языки программирования — это наборы абстрактных математических правил, определений и ограничений. Языки программирования не компилируются и не интерпретируются. Языки программирования как раз есть . [Благодарность идет к Шрираму Кришнамурти, который сказал это в интервью на Channel 9 лет назад (около 51: 37-52: 20). ]

На самом деле, язык программирования может прекрасно существовать без какого-либо интерпретатора или компилятора! Например, Plankalkül Конрада Цузе , который он разработал в 1930-х годах, так и не был реализован при его жизни. Вы все еще могли бы писать программы в нем, вы могли анализировать эти программы, рассуждать о них, доказывать свойства о них … вы просто не могли их выполнять. (Ну, на самом деле, даже это неправильно: вы, конечно, можете запустить их в своей голове или ручкой и бумагой.)

Компиляция и интерпретация — это черты компилятора или интерпретатора (дух!), А не языка программирования. Компиляция и интерпретация живут на ином уровне абстракции, чем языки программирования: язык программирования — это абстрактное понятие, спецификация, лист бумаги. Компилятор или интерпретатор — это конкретная часть программного обеспечения (или аппаратного обеспечения), которая реализует эту спецификацию. Если бы английский был типизированным языком, термины «скомпилированный язык» и «интерпретируемый язык» были бы ошибками типа. [Опять же, заслуга Шрирама Кришнамурти.]

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

Есть интерпретаторы для C и для C . С другой стороны, каждая текущая основная реализация ECMAScript, PHP, Python, Ruby и Lua имеет компилятор. Первоначальная версия движка Google V8 ECMAScript представляла собой чистый машинный код. (Они прошли несколько различных проектов, и в текущей версии есть интерпретатор, но в течение многих лет у него не было его.) XRuby и Ruby.NET были чисто скомпилированными реализациями Ruby. IronRuby начинал как чисто скомпилированная реализация Ruby, а затем добавил интерпретатор для повышения производительности. Opal — это чисто скомпилированная реализация Ruby.

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

Например, вы можете автоматически и механически извлекать компилятор из интерпретатора, используя вторую проекцию Футамуры. Впервые он был описан профессором Ёсихико Футамурой в его работе 1971 года « Частичная оценка вычислительного процесса — подход к компилятору-компилятору» (на японском языке) , английская версия которой была переиздана 28 лет спустя. Он использует частичную оценку, частично оценивая сам частичный оценщик по отношению к интерпретатору, тем самым получая компилятор.

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

Другая возможность — идея «мета-JIT». (Это связано по духу с проекциями Футамуры.) Это, например, используется в среде RPython для реализации языков программирования. В RPython вы пишете интерпретатор для вашего языка, а затем среда RPython будет JIT-компилировать ваш интерпретатор во время интерпретации программы, создавая, таким образом, специализированную скомпилированную версию интерпретатора, которая может интерпретировать только эту единственную программу — которая снова неотличим от компиляции этой программы. Так что в некотором смысле RPython динамически генерирует JIT-компиляторы из интерпретаторов.

С другой стороны, вы можете заключить компилятор в оболочку, которая сначала компилирует программу, а затем непосредственно выполняет ее, что делает этот упакованный компилятор неотличимым от интерпретатора. Фактически это то, как реализованы Scala REPL, C♯ REPL (как в Mono, так и в .NET), Clojure REPL, интерактивный REPL GHC и многие другие REPL. Они просто берут одну строку / одно выражение / одно выражение, компилируют, немедленно запускают и выводят результат. Этот способ взаимодействия с компилятором настолько неотличим от интерпретатора, что некоторые люди фактически используют наличие REPL для языка программирования в качестве определяющей характеристики того, что значит быть «интерпретируемым языком программирования».

Обратите внимание, однако, что вы не можете запустить программу без переводчика. Компилятор просто переводит программу с одного языка на другой. Но это все. Теперь у вас одна и та же программа, просто на другом языке. Единственный способ получить результат программы — это интерпретировать его. Иногда язык является чрезвычайно простым двоичным машинным языком, и интерпретатор на самом деле жестко запрограммирован в силиконе (и мы называем это «CPU»), но это все еще интерпретация.

Некоторые люди говорят, что вы можете назвать язык программирования «интерпретируемым», если большинство его реализаций являются интерпретаторами. Что ж, давайте просто посмотрим на очень популярный язык программирования: ECMAScript. Существует ряд готовых, широко используемых, высокопроизводительных основных реализаций ECMAScript, и каждая из них включает по крайней мере один компилятор, некоторые даже несколько компиляторов. Итак, согласно этому определению, ECMAScript — это явно скомпилированный язык.

Возможно, вас также заинтересует мой ответ, который объясняет различия и различные способы объединения интерпретаторов, JIT-компиляторов и AOT-компиляторов, и этот ответ касается различий между AOT-компилятором и JIT-компилятором .

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

  • компиляторы и
  • интерпретаторы (если интерпретатор интерпретирует язык, который не предназначен для людей, его также часто называют виртуальной машиной )

Внутри группы компиляторов у нас есть временное различие при запуске компилятора:

  • Компиляторы Just-In-Time запускаются во время выполнения программы
  • Компиляторы с опережением времени запускаются до запуска программы

И затем у нас есть реализации, которые объединяют интерпретаторы и компиляторы, или объединяют несколько компиляторов, или (гораздо реже) несколько интерпретаторов. Некоторые типичные комбинации

  • механизмы выполнения в смешанном режиме, которые объединяют интерпретатор и JIT-компилятор, которые одновременно обрабатывают одну и ту же программу (примеры: Oracle HotSpot JVM, IBM J9 JVM)
  • многофазный [я изобрел этот термин, я не знаю широко используемого.] механизмы выполнения, где первый этап — это компилятор, который компилирует программу на язык, более подходящий для следующего этапа, а затем второй фаза, которая обрабатывает этот язык. (Может быть больше этапов, но два типичных.) Как вы, вероятно, можете догадаться, второй этап может снова использовать различные стратегии реализации:
    • интерпретатор: это типичная стратегия реализации. Часто интерпретируемый язык представляет собой некоторую форму байт-кода, оптимизированную для «интерпретируемости». Примеры: CPython, YARV (до 2.6) , Zend Engine
    • компилятор, который делает это комбинацией двух компиляторов. Как правило, первый компилятор переводит язык в некую форму байт-кода, оптимизированного для «компилируемости», а второй компилятор является оптимизирующим компилятором, специфичным для целевой платформы.
    • ВМ в смешанном режиме. Примеры: YARV post-2.6, Rubinius, SpiderMonkey, SquirrelFish Extreme, Chakra

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

RPython объединяет интерпретатор байт-кода и JIT, но JIT не компилирует пользовательскую программу, он компилирует интерпретатор байт-кода, пока он интерпретирует пользовательскую программу! Причина этого заключается в том, что RPython является платформой для реализации языков, и, таким образом, разработчик языка должен только написать интерпретатор байт-кода и получить JIT бесплатно. (Самым известным пользователем RPython является, конечно, PyPy .)

Среда Truffle интерпретирует AST, не зависящую от языка, но в то же время специализируется на конкретной AST, которая похожа на компиляцию, но также не похожа. Конечным результатом является то, что Truffle может выполнить код очень быстро, не зная слишком много о специфике языка. (Truffle также является общей структурой для языковых реализаций.) Поскольку AST не зависит от языка, вы можете смешивать и сопоставлять несколько языков в одной программе, а Truffle способен выполнять оптимизацию для разных языков, например, вставляя метод Ruby в Функция ECMAScript и т. Д.

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

  1. Использование интерпретатора: для макросов вы встраиваете интерпретатор в компилятор. Для eval вы встраиваете интерпретатор в скомпилированную программу или в библиотеки поддержки времени выполнения.
  2. Использование компилятора: для макросов вы сначала компилируете макрос, затем встраиваете скомпилированный макрос в ваш компилятор и компилируете программу, используя этот «расширенный» компилятор. Для eval вы встраиваете компилятор в скомпилированную программу или в библиотеки поддержки времени выполнения.
Понравилась статья? Поделиться с друзьями:
JavaScript & TypeScript
Adblock
detector