Інтерпретатор — Вікіпедія

Інтерпретатор мови програмування (interpreter) — програма чи технічні засоби, необхідні для виконання інших програм, вид транслятора, який здійснює пооператорну (покомандну, построкову) обробку, перетворення у машинний код та виконання програми або запиту (на відміну від компілятора, який транслює у машинні коди всю програму без її виконання).

Інтерпретатори можуть працювати як з початковим кодом програми (англ. source code), написаним мовою програмування, так і з байт-кодом (інтерпретатори байт-коду).

Типи інтерпретаторів[ред. | ред. код]

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

Інтерпретатор компілюючого типу — це система з компілятора, який перекладає текст програми в проміжне представлення, наприклад, в байт-код або p-код, і власне інтерпретатора, який виконує отриманий проміжний код (так звана віртуальна машина). Перевагою таких систем є більша швидкодія виконання програм (за рахунок винесення аналізу початкового коду в окремий, разовий прохід, і мінімізації цього аналізу в інтерпретаторі). Недоліки — більші вимоги до ресурсів і вимога на коректність тексту програми. Застосовується в таких мовах, як Java, Tcl, Perl (використовується байт-код), REXX (зберігається результат синтаксичного аналізу), а також у різних СУБД (використовується p-код).

Інтерпретатор компілюючого типу складається з компілятора мови і простого інтерпретатора з мінімізованим аналізом початкового коду; цей код у такому випадку не обов'язково повинен мати текстовий формат — це може бути машинний код якоїсь наявної апаратної платформи. Наприклад, віртуальні машини типу QEMU, Bochs, VMware містять у собі інтерпретатори машинного коду процесорів сімейства x86.

Деякі інтерпретатори (наприклад, для мов Lisp, Scheme, Python, Basic та інших) можуть працювати в режимі діалогу або так званого циклу читання-обчислення-друку (англ. read-eval-print loop, REPL). У такому режимі інтерпретатор зчитує закінчену конструкцію мови (наприклад, s-expression у мові Lisp), виконує її, друкує результати, після чого переходить до очікування введення користувачем наступної конструкції.

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

Слід також зазначити, що режими інтерпретації можна знайти не тільки в програмному, а й апаратному забезпеченні. Так, багато мікропроцесорів інтерпретують машинний код за допомогою вбудованих мікропрограм, а процесори сімейства x86, починаючи з Pentium (наприклад, на архітектурі Intel P6), під час виконання машинного коду попередньо транслюють його у внутрішній формат (в послідовність мікрооперацій).

Порівняння інтерпретатора та компілятора[ред. | ред. код]

Програми, як правило, пишуть мовою високого рівня, яка повинна бути перетворена в машинний код для виконання центральним процесором. Це перетворення виконує компілятор або інтерпретатор.

Розробка програмного забезпечення[ред. | ред. код]

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

Розповсюдження[ред. | ред. код]

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

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

Ефективність[ред. | ред. код]

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

Інтерпретація коду відбувається повільніше, ніж запуск скомпільованого коду, тому що інтерпретатор повинен аналізувати кожну інструкцію у програмі кожного разу, коли вона виконується, а потім виконувати потрібну дію, в той час як скомпільований код просто виконує фіксовані дії, визначені під час компіляції. Цей аналіз під час виконання відомий як «додаткові витрати інтерпретації». Доступ до змінних в інтерпретованих програм також повільніший, тому що операція зв'язування ідентифікаторів з місцями зберігання повторюється під час виконання, тоді як компілятором виконується один раз під час компіляції.

Існують різні компроміси між швидкістю розробки програмного забезпечення при використанні інтерпретатора і швидкістю виконання програми при використанні компілятора. Деякі системи (наприклад, Lisp) дозволяють інтерпретованому і скомпільованому коду викликати один одного і обмінюватися змінними. Це означає, що поточний код, який був протестований і налагоджений інтерпретатором, може бути скомпільований, і таким чином отримати більшу швидкість виконання, в той час як інший код розробляється. Багато інтерпретаторів не виконують оператори програми безпосередньо, а приводять його до більш компактної внутрішньої форми. Багато інтерпретаторів мови BASIC замінюють зарезервовані слова одним байтом токена, який може бути використаний для пошуку команди в таблиці переходів.

Проміжний код[ред. | ред. код]

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

Проміжний код для Java[ред. | ред. код]

Початковий код на Java компілюється в проміжний код, який буде інтерпретовано.

Початковий код Проміжний код Інтерпретатор

Проміжною мовою для Java є байт-код, інтерпретатор — Java Virtual Machine (JVM). Файл байт-коду є універсальним, тоді як інтерпретатор є унікальним для кожної платформи.

Приклад коду[ред. | ред. код]

Розглянемо наступний приклад на мові Java.

  outer:   for (int i = 2; i < 1000; i++) {       for (int j = 2; j < i; j++) {           if (i % j == 0)               continue outer;       }       System.out.println (i);   } 

Компілятор Java може транслювати цей код в наступний байт-код:

  0:   iconst_2   1:   istore_1   2:   iload_1   3:   sipush  1000   6:   if_icmpge       44   9:   iconst_2   10:  istore_2   11:  iload_2   12:  iload_1   13:  if_icmpge       31   16:  iload_1   17:  iload_2   18:  irem   19:  ifne    25   22:  goto    38   25:  iinc    2, 1   28:  goto    11   31:  getstatic       #84; //Field java/lang/System.out:Ljava/io/PrintStream;   34:  iload_1   35:  invokevirtual   #85; //Method java/io/PrintStream.println:(I)V   38:  iinc    1, 1   41:  goto    2   44:  return 

Проміжний код для мов .NET Framework[ред. | ред. код]

Процес компіляції програм для мов .NET Framework в CIL та інтерпретації за допомогою CLR

Мови, сумісні з платформою .NET Framework, такі як Visual Basic, C#, Visual F#, VB.NET, J#, компілюються в Common Intermediate Language (CIL) — проміжну мову для платформи .NET Framework. Отриманий проміжний код інтерпретується Common Language Runtime (CLR) — загальномовним середовищем виконання. Специфікація CIL та CLR є реалізацією специфікації Common Language Infrastructure (CLI) — специфікації загальномовної інфраструктури компанії Microsoft.

Код на CIL генерують всі компілятори для платформи .NET Framework. Мова CIL по структурі та мнемоніці нагадує мову асемблер. Проте CIL містить деякі високорівневі конструкції, і писати на CIL значно легше, ніж на асемблері.

Приклад коду[ред. | ред. код]

Приклад програми на C#:

static void Main(string[] args) { outer:     for (int i = 2; i < 1000; i++)     {          for (int j = 2; j < i; j++)          {               if (i % j == 0)                    goto outer;          }          Console.WriteLine(i);      } } 

Вигляд цієї ж програми на CIL:

.method private hidebysig static void  Main(string[] args) cil managed {   .entrypoint   .maxstack  2   .locals init ([0] int32 i,            [1] int32 j)   IL_0000:  ldc.i4.2             stloc.0             br.s       IL_001f   IL_0004:  ldc.i4.2             stloc.1             br.s       IL_0011   IL_0008:  ldloc.0             ldloc.1             rem             brfalse.s  IL_0000             ldloc.1             ldc.i4.1             add             stloc.1   IL_0011:  ldloc.1             ldloc.0             blt.s      IL_0008             ldloc.0             call       void [mscorlib]System.Console::WriteLine(int32)             ldloc.0             ldc.i4.1             add             stloc.0   IL_001f:  ldloc.0             ldc.i4     0x3e8             blt.s      IL_0004             ret }

Parrot[ред. | ред. код]

Parrot VM — це віртуальна машина, призначена для ефективної компіляції та виконання байт-коду для динамічних мов. В Parrot на даний момент реалізована підтримка багатьох мов, серед яких Tcl, Javascript, Ruby, Lua, Scheme, PHP, Python, Perl 6, APL і .NET транслятор байт-коду.

Віртуальна машина Parrot аналогічна віртуальним машинам Java і .NET платформи. Проте, на відміну від вказаних двох, які розроблені для статично типізованих мов як Java чи C#, Parrot розроблено для динамічно типізованих мов програмування.

Віртуальна машина Parrot написана на мові C. Саме тому, що Parrot призначена для підтримки різноманітних мов високого рівня, її архітектура доволі загальна та багатофункціональна.

Основні компоненти Parrot[ред. | ред. код]

Парсери PASM і PIR[ред. | ред. код]

Для компілювання початкового коду у PIR(Parrot Intermediate Representation) — проміжне представлення Parrot — доступно два парсери. IMCC використовується зараз, але є неефективним. PIRC є ефективнішим, але поки що нестабільний. Планується зробити PIRC основним парсером для PIR до виходу версії Parrot 1.0.

Компілятор байт-коду та оптимізатор[ред. | ред. код]

Компілятор байт-коду — складова Parrot, що відповідає за перетворення вхідних коду на PASM або PIR у байт-код Parrot. Цей байт-код виконується швидко і ефективно.

Іншим компонентом Parrot є оптимізатор байт-коду, який відповідає за низькорівневі оптимізації байт-коду Parrot.

Ітерпретатор[ред. | ред. код]

Тоді як компілятор байт-коду приймає вхідний код після обробки парсерами PIRC або IMCC і перетворює його в байт-код для зберігання і подальшого виконання, функцією інтерпретатора є безпосереднє виконання отриманого PIR та PASM коду. Це означає, що немає ніякого проміжного етапу компіляції, і скрипт можна виконати швидко, без необхідності компіляції.

Приклад коду[ред. | ред. код]

PIR[ред. | ред. код]

Приклад циклу:

.sub loopy         .local int counter         counter = 0 LOOP:   if counter > 10 goto DONE         print counter         print " "         inc counter         goto LOOP DONE:         print "counter\n"         end .end 
PASM[ред. | ред. код]

Приклад циклу:

set     I1, 1 REDO:   gt      I1, 10, END   print   I1   print   " "   inc     I1   branch  REDO END:   print "\n"   end 
Результат[ред. | ред. код]
0 1 2 3 4 5 6 7 8 9 10 

Див. також[ред. | ред. код]

Джерела[ред. | ред. код]

  1. Jeff Fox. Chapter 2. More Interpretation. Thoughtful Programming and Forth (англ.). UltraTechnology. Архів оригіналу за 22 серпня 2011. Процитовано 12 травня 2013.