Чи робить перекладач машинний код?


42

Я інтенсивно вивчаю теми упорядників та перекладачів. Я хочу перевірити правильність мого базового розуміння, тому припустимо наступне:

У мене мова називається "Foobish" і її ключові слова є

<OUTPUT> 'TEXT', <Number_of_Repeats>;

Отже, якщо я хочу надрукувати на консолі 10 разів, я б написав

OUTPUT 'Hello World', 10;

Привіт World.foobish-файл.

Тепер я пишу перекладача мовою, яку я обрав - C # у цьому випадку:

using System;

namespace FoobishInterpreter
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            analyseAndTokenize(Hello World.foobish-file)//Pseudocode
            int repeats = Token[1];
            string outputString = Token[0];
            for (var i = 0; i < repeats; i++)
            {
                Console.WriteLine(outputString);
            }
        }
    }
}

На дуже простому рівні перекладача інтерпретатор аналізував би файл-скрипт тощо і виконував мову foobish у спосіб реалізації інтерпретатора.

Чи створив би компілятор машинну мову, яка працює безпосередньо на фізичному обладнанні?

Отже, перекладач не виробляє машинну мову, але чи робить це компілятор для свого введення?

Чи є в основному непорозуміння, як працюють компілятори та перекладачі?


21
Як ви думаєте, що робить компілятор C #? Як підказка, він не виробляє машинного коду.
Філіп Кендалл

3
Компілятор Java виробляє код для JVM. Отже, цільова машина компілятора може бути віртуальною машиною, яка не виконується безпосередньо апаратним забезпеченням. Основна відмінність інтерпретатора від компілятора полягає в тому, що компілятор спочатку перевіряє і переводить весь вихідний код на мову цільової машини. Потім цей компільований код виконується машиною, для якої він був призначений. З іншого боку, перекладач буде перекладати та виконувати шматки вашої програми на льоту.
Джорджіо

@Giorgio: Ти маєш на увазі, як JIT?
Роберт Харві

2
@RobertHarvey: Я мав на увазі компілятор Java (javac): наскільки я знаю, він виробляє байт-код для JVM. І знову AFAIK, пізніше JIT (під час виконання) збирає байт-код, який дуже часто використовується в рідній машинній мові.
Джорджіо

4
компілятор означає переклад. Він може випускати всі види мови: c, збірка, javascript, машинний код.
Есбен Сков Педерсен

Відповіді:


77

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

Але сьогодні існує багато варіацій щодо використання "компілятора" та "інтерпретатора". Наприклад, VB6 "компілює" байтовий код (форма проміжної мови ), який потім "інтерпретується" програмою VB Runtime. Аналогічний процес відбувається в C #, який виробляє CIL, який потім виконується компілятором Just-in-Time (JIT), який у старі часи вважався інтерпретатором. Ви можете «заморозити-висушити» вихід JIT у фактичний бінарний виконуваний файл, використовуючи NGen.exe , продукт якого в старі часи був би результатом компілятора .

Тож відповідь на ваше запитання не настільки проста, як колись.

Подальше читання
укладачів проти перекладачів у Вікіпедії


6
@Giorgio: Більшість перекладачів сьогодні фактично не виконують вихідний код, а скоріше вихід AST або щось подібне. У компіляторів є подібний процес. Відмінність не настільки чітка, як ви думаєте.
Роберт Харві

5
"Ви можете" заморозити-висушити "вихід JIT у фактичний бінарний виконуваний файл, використовуючи NGen.exe, продукт якого був би результатом компілятора за старих часів.": Але це все ще є результатом сьогодні компілятора (а саме компілятора, що знаходиться в часі). Не важливо, коли працює компілятор, а що він робить. Компілятор приймає в якості подання фрагмент коду і виводить нове подання. Інтерпретатор видасть результат виконання цього фрагмента коду. Це два різні процеси, незалежно від того, як ви їх змішуєте та коли виконуєте.
Джорджіо

4
"Компілятор" - це просто термін, який вони обрали до GCC. Вони вирішили не називати NGen компілятором, навіть якщо він виробляє машинний код, вважаючи за краще приєднати цей термін до попереднього кроку, який, можливо, можна назвати інтерпретатором, навіть якщо він виробляє машинний код (це роблять і деякі інтерпретатори). Моя думка полягає в тому, що в наш час не існує принципу зобов’язання, який можна посилатись, щоб остаточно називати щось компілятором чи інтерпретатором, окрім "це те, що вони завжди називали".
Роберт Харві

4
Як я розумію, в наші дні x86 процесори все одно знаходяться на апаратних двигунах JIT, при цьому збірка має постійно згасаюче відношення до того, що саме виконується.
Левшенко

4
@RobertHarvey, хоча я погоджуюся, що між методами, які використовуються в інтерпретаторі та компіляторі, немає чіткого розмежування: у функціях є досить чіткий поділ: якщо результатом виконання даного інструменту з кодом програми є вхід, це виконання цього програма, інструмент - перекладач. Якщо результатом є вихід перекладу програми в менш абстрактну форму, це компілятор. Якщо результат - переклад на більш абстрактну форму, це якщо декомпілятор. Однак випадки, коли більше ніж один із цих результатів, неоднозначні.
Жуль

34

Резюме, яке я даю нижче, базується на "Укладачах, принципах, техніці та інструментах", Ахо, Лам, Сетхі, Уллман, (Pearson International Edition, 2007), сторінки 1, 2, з додаванням деяких власних ідей.

Два основні механізми обробки програми - складання та інтерпретація .

Компіляція приймає як вхід вихідну програму на даній мові та виводить цільову програму цільовою мовою.

source program --> | compiler | --> target program

Якщо мова мови є машинним кодом, він може бути виконаний безпосередньо на деякому процесорі:

input --> | target program | --> output

Компіляція включає сканування та переклад усієї програми введення (або модуля) і не передбачає її виконання.

Інтерпретація приймає як вхід вихідну програму та її вхід і виробляє вихід вихідної програми

source program, input --> | interpreter | --> output

Інтерпретація зазвичай включає обробку (аналіз та виконання) програми по одній заяві за раз.

На практиці багато мовних процесорів використовують поєднання двох підходів. Наприклад, програми Java спочатку перекладаються (компілюються) в проміжну програму (байт-код):

source program --> | translator | --> intermediate program

вихід цього кроку виконується (інтерпретується) віртуальною машиною:

intermediate program + input --> | virtual machine | --> output

Щоб ще більше ускладнити речі, JVM може виконати своєчасну компіляцію під час виконання для перетворення байтового коду в інший формат, який потім виконується.

Крім того, навіть при компіляції на машинну мову є інтерпретатор, який працює за вашим бінарним файлом, який реалізований базовим процесором. Тому навіть у цьому випадку ви використовуєте гібрид компіляції + інтерпретації.

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

Тим не менш, складання та інтерпретація - це два різних види обробки, як описано на діаграмах вище,

Щоб відповісти на початкові запитання.

Компілятор створив би машинну мову, яка працює безпосередньо на фізичному обладнанні?

Не обов'язково компілятор перекладає програму, написану для машини M1, на еквівалентну програму, написану для машини M2. Цільова машина може бути реалізована апаратно або бути віртуальною машиною. Концептуально різниці немає. Важливим моментом є те, що компілятор розглядає фрагмент коду і перекладає його на іншу мову, не виконуючи його.

Отже, перекладач не виробляє машинну мову, але компілятор робить це для свого введення?

Якщо, виробляючи, ви посилаєтесь на вихід, тоді компілятор виробляє цільову програму, яка може бути машинною мовою, інтерпретатор цього не робить.


7
Іншими словами: інтерпретатор приймає програму P і виробляє її вихід O, компілятор приймає P і виробляє програму P ', яка виводить O; інтерпретатори часто включають компоненти, що є компіляторами (наприклад, до байтового коду, проміжного представлення або машинних інструкцій JIT), а також компілятор може включати інтерпретатора (наприклад, для оцінки обчислень часу компіляції).
Джон Перді

"компілятор може включати перекладача (наприклад, для оцінки обчислень часу компіляції)": Добре. Я думаю, що макроси Lisp та шаблони C ++ можуть бути попередньо оброблені таким чином.
Джорджіо

Ще простіше, препроцесор C компілює вихідний код C з директивами CPP в звичайний C і включає інтерпретатор булевих виразів, таких як defined A && !defined B.
Джон Перді

@JonPurdy Я погодився би з цим, але я також додав би клас "традиційних перекладачів", який не використовує проміжні уявлення за межами, можливо, токенізованої версії джерела. Прикладами можуть бути оболонки, багато BASIC, класичний Lisp, Tcl до 8.0 та bc.
варення

1
@naxa - див. відповідь Лоуренса та коментарі Пола Дрейпера щодо типів компілятора. Ассемблер - це особливий вид компілятора, де (1) мова виводу призначена для безпосереднього виконання машиною або віртуальною машиною, і (2) існує дуже проста відповідність один на один між вхідними операторами та інструкціями виведення.
Жуль

22

Компілятор створив машинну мову

Ні . Компілятор просто програма , яка приймає в якості вхідних даних програми , написаної на мові A і виробляє в якості своєї продукції семантично еквівалентну програму на мові B . Мова B може бути будь-якою, вона не повинна бути машинною мовою.

Компілятор може компілювати з мови високого рівня на іншу мову високого рівня (наприклад, GWT, яка компілює Java до ECMAScript), з мови високого рівня на мову низького рівня (наприклад, Gambit, який компілює схему до C), від мови високого рівня до машинного коду (наприклад, GCJ, який компілює Java до рідного коду), від мови низького рівня до мови високого рівня (наприклад, Clue, який компілює C на Java, Lua, Perl, ECMAScript та загальний Lisp), від мови низького рівня до іншої мови низького рівня (наприклад, Android SDK, яка компілює байт-код JVML в байт-код Dalvik), від мови низького рівня до машинного коду (наприклад, компілятор C1X, який є частиною HotSpot, який компілює байт-код JVML до машинного коду), машинний код на високому рівні (будь-який так званий "декомпілятор", також Emscripten, який компілює машинний код LLVM до ECMAScript),машинного коду до мови низького рівня (наприклад, компілятор JIT в JPC, який компілює початковий код x86 в байт-код JVML) і власний код до нативного коду (наприклад, компілятор JIT в PearPC, який компілює нативний код PowerPC до нативного коду x86).

Зауважте також, що "машинний код" - це дійсно нечіткий термін з кількох причин. Наприклад, є центральні процесори, які споконвічно виконують байт-код JVM, а також є інтерпретатори програмного забезпечення для машинного коду x86. Отже, що робить один "рідний машинний код", а не інший? Також кожна мова є кодом для абстрактної машини для цієї мови.

Існує багато спеціалізованих імен для компіляторів, які виконують спеціальні функції. Незважаючи на те, що це спеціалізовані імена, все це все-таки компілятори, просто спеціальні види компіляторів:

  • якщо мова A сприймається приблизно на такому ж рівні абстракції, що і мова B , компілятор може бути названий транспілятором (наприклад, перекладачем Ruby-ECMAScript або ECMAScript2015-ECMAScript5-транспілятором)
  • якщо мова A сприймається на нижчому рівні абстракції, ніж мова B , компілятор може бути названий декомпілятором (наприклад, x86-машина-код-до-C-декомпілятор)
  • якщо мова == мову Б , компілятор можна було б назвати оптимізатор , обфуськатор або Minifier ( в залежності від конкретної функції компілятора)

що працює безпосередньо на фізичному обладнанні?

Не обов'язково. Це може бути запущено у перекладача або у ВМ. Це може бути додатково складено на іншій мові.

Отже, перекладач не виробляє машинну мову, але компілятор робить це для свого введення?

Перекладач нічого не дає. Він просто запускає програму.

Компілятор щось виробляє, але це не обов'язково має бути машинною мовою, це може бути будь-яка мова. Це може бути навіть та ж мова, що і мова введення! Наприклад, Supercompilers, LLC має компілятор, який приймає Java як свій вхід і виробляє оптимізовану Java як вихід. Є багато компіляторів ECMAScript, які приймають ECMAScript як свої вхідні дані і виробляють оптимізовані, мінімізовані та затуманені ECMAScript як їх вихід.


Вас також можуть зацікавити:


16

Я думаю, вам слід повністю скинути поняття "компілятор проти інтерпретатора", оскільки це хибна дихотомія.

  • Компілятор є трансформатором : Він перетворює комп'ютерну програму , написану на мові джерела та видає еквівалент в цільовому мовою . Зазвичай мова-джерело вищого рівня, ніж мова-мова, і якщо це навпаки, ми часто називаємо такий трансформатор декомпілятором .
  • Перекладач є виконання двигуна . Він виконує комп'ютерну програму, написану однією мовою, відповідно до специфікації цієї мови. Ми в основному використовуємо цей термін для програмного забезпечення (але певним чином класичний процесор може розглядатися як апаратний "інтерпретатор" для його машинного коду).

Колективним словом для створення абстрактної мови програмування, корисної у реальному світі, є реалізація .

Раніше реалізація мови програмування часто складалася з просто компілятора (і ЦП, для якого він згенерував код) або просто інтерпретатора - тому, можливо, схоже, що ці два інструменти взаємовиключні. Сьогодні ви чітко бачите, що це не так (і це ніколи не було з початку). Виконання складної реалізації мови програмування та спроба підсунути йому ім'я "компілятор" або "інтерпретатор", часто призводить до непереконливих чи непослідовних результатів.

Реалізація єдиної мови програмування може залучати будь-яку кількість компіляторів та інтерпретаторів , часто в декількох формах (окремий, на ходу), будь-яку кількість інших інструментів, таких як статичні аналізатори та оптимізатори та будь-яку кількість кроків. Він може навіть включати цілі реалізації будь-якої кількості проміжних мов (які можуть бути не пов'язані з реалізованою).

Приклади схем впровадження включають:

  • Компілятор змінного струму, який перетворює C на машинний код x86, і процесор x86, який виконує цей код.
  • Компілятор змінного струму, який перетворює C у IR LLVM, компілятор резервного копіювання LLVM, який перетворює LLVM IR у машинний код x86, та процесор x86, що виконує цей код.
  • Компілятор змінного струму, який перетворює C в LLVM IR, і інтерпретатор LLVM, який виконує ІР LLVM.
  • Компілятор Java, який перетворює Java в байт-код JVM, і JRE з інтерпретатором, який виконує цей код.
  • Компілятор Java, який перетворює Java в байт-код JVM, і JRE з інтерпретатором, який виконує деякі частини цього коду, і компілятором, який перетворює інші частини цього коду в машинний код x86, і процесором x86, який виконує цей код.
  • Компілятор Java, який перетворює Java в байт-код JVM, і процесор ARM, який виконує цей код.
  • AC # компілятор, який перетворює C # в CIL, CLR з компілятором, який перетворює CIL в машинний код x86, і процесор x86, який виконує цей код.
  • Перекладач Ruby, який виконує Ruby.
  • Середовище Ruby з інтерпретатором, який виконує Ruby, і компілятором, який перетворює Ruby в машинний код x86, і процесором x86, який виконує цей код.

...і так далі.


+1 для вказівки, що навіть кодування, які були розроблені для проміжного представлення (наприклад, байт-код Java), можуть мати апаратні реалізації.
Жуль

7

Хоча лінії між компіляторами та інтерпретаторами з часом нечіткі, все одно можна провести межу між ними, дивлячись на семантику того, що повинна робити програма, і що робить компілятор / інтерпретатор.

Компілятор генерує іншу програму (як правило, мовою нижчого рівня, як машинний код), яка, якщо ця програма буде запущена, зробить те, що повинна зробити ваша програма.

Перекладач зробить те, що має зробити ваша програма.

За допомогою цих визначень місця, де стає нечітко, - це випадки, коли ваш компілятор / перекладач може вважатись тим, що робить різні речі залежно від того, як ви на це дивитесь. Наприклад, Python бере ваш код Python і компілює його у складений байт-код Python. Якщо цей байт-код Python запускається через інтерпретатор байт-кодів Python , він робить те, що мала робити ваша програма. Однак у більшості ситуацій розробники Python думають, що обидва ці кроки виконуються одним великим кроком, тому вони вирішують вважати інтерпретатора CPython як інтерпретацію їх вихідного коду, а той факт, що він складений по дорозі, вважається детальною частиною реалізації. . Таким чином, це все питання перспективи.


5

Ось проста концептуальна розбіжність між компіляторами та інтерпретаторами.

Розгляньте 3 мови: мова програмування , P (на якій програмі написано); мова домену , D (для роботи з запущеною програмою); і мова цілі , T (деяка третя мова).

Концептуально,

  • компілятор переводить P в T , так що можна оцінити T (D); тоді як

  • інтерпретатор оцінює P (D) безпосередньо.


1
Більшість сучасних перекладачів насправді не оцінюють мову джерела безпосередньо, а скоріше проміжне представлення мови-джерела.
Роберт Харві

4
@RobertHarvey Це не змінює концептуальної відмінності між термінами.
Лоуренс

1
Отже, те, що ви насправді посилаєтесь на перекладача, - це частина, яка оцінює проміжне подання. Частина, яка створює проміжне подання, - це компілятор , за вашим визначенням.
Роберт Харві

6
@RobertHarvey Не дуже. Умови залежать від рівня абстракції, над яким ви працюєте. Якщо ви подивитеся внизу, інструмент може робити все, що завгодно. За аналогією, скажімо, ви їдете в чужу країну і привозите з собою двомовного друга Боба. Якщо ви спілкуєтесь з місцевими жителями, розмовляючи з Боб, який, у свою чергу, розмовляє з місцевими жителями, Боб виступає перекладачем для вас (навіть якщо він перед тим, як розмовляти, їх писати мовою). Якщо ви запитаєте у Боба фрази і Боб пише їх іноземною мовою, а ви спілкуєтесь з місцевими жителями, посилаючись на ці твори (а не на Боб), Боб виступає компілятором для вас.
Лоуренс

1
Відмінна відповідь. Варто зауважити: сьогодні ви можете почути "транспілер". Це компілятор, де P і T схожі рівні абстракції, для деякого визначення схожих. (Наприклад, транспілер ES5 - ES6.)
Пол Дрейпер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.