Інтерпретатор
Інтерпретатор мови програмування (interpreter) — програма чи технічні засоби, необхідні для виконання інших програм, вид транслятора, який здійснює пооператорну (покомандну, построкову) обробку, перетворення у машинні коди та виконання програми або запиту (на відміну від компілятора, який транслює у машинні коди всю програму без її виконання).
Інтерпретатори можуть працювати як з вихідним кодом програми (англ. source code), написаним мовою програмування, так і з байт-кодом (інтерпретатори байт-коду).
Зміст
1 Типи інтерпретаторів
2 Порівняння інтерпретатора та компілятора
2.1 Розробка програмного забезпечення
2.2 Розповсюдження
2.3 Ефективність
3 Проміжний код
3.1 Проміжний код для Java
3.1.1 Приклад коду
3.2 Проміжний код для мов .NET Framework
3.2.1 Приклад коду
3.3 Parrot
3.3.1 Основні компоненти Parrot
3.3.1.1 Парсери PASM і PIR
3.3.1.2 Компілятор байт-коду та оптимізатор
3.3.1.3 Ітерпретатор
3.3.2 Приклад коду
3.3.2.1 PIR
3.3.2.2 PASM
3.3.2.3 Результат
4 Див. також
5 Джерела
Типи інтерпретаторів |
Простий інтерпретатор аналізує і відразу виконує (власне інтерпретація) програму покомандно (або порядково), по мірі надходження її вихідного коду на вхід інтерпретатора. Перевагою такого підходу є миттєва реакція. Недолік — такий інтерпретатор виявляє помилки в тексті програми тільки при спробі виконання команди (або рядка) з помилкою.
Інтерпретатор компілюючого типу — це система з компілятора, який перекладає вихідний код програми в проміжне представлення, наприклад, в байт-код або 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 замінюють зарезервовані слова[en] одним байтом токена, який може бути використаний для пошуку команди в таблиці переходів.
Проміжний код |
Зазвичай вихідний код мови програмування високого рівня компілюється у проміжну мову, яка буде скомпільована або інтерпретована у машинний код.
Проміжний код для 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, такі як 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 "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
Див. також |
- Компілятор
Джерела |
↑ Jeff Fox. Chapter 2. More Interpretation. Thoughtful Programming and Forth (en). UltraTechnology. Архів оригіналу за 2011-08-22. Процитовано 12 травня 2013.
|