Интерпретатор

Интерпретатор языки программирования (interpreter) - программа или технические средства, необходимые для выполнения других программ, вид транслятора, осуществляющего пооператорну (покомандно, построчно) обработку, преобразование в машинные коды и выполнения программы или запроса (в отличие от компилятора, который транслирует в машинные коды всю программу без ее выполнения).

Интерпретаторы могут работать как с исходным кодом программы ( англ. source code ), Написанным на языке программирования, так и с байт-кодом (интерпретаторы байт-кода).


1. Типы интерпретаторов

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

Интерпретатор компилируя типа - это система с компилятора, который переводит исходный код программы в промежуточное представление, например, в байт-код или 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), во время выполнения машинного кода предварительно транслируют его во внутренний формат (в последовательность микроопераций).



2. Сравнение интерпретатора и компилятора

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

2.1. Разработка программного обеспечения

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


2.2. Распространение

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


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

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

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

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


3. Промежуточный код

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

3.1. Промежуточный код для Java

Исходный код на Java компилируется в промежуточный код, который будет интерпретирован.

Text-x-java-source.svg
Application-x-executable.svg
Java.png
Исходный код Промежуточный код Интерпретатор

Промежуточной языке для Java является байт-код, интерпретатор - Java Virtual Machine (JVM). Файл байт-кода является универсальным, тогда как интерпретатор является уникальным для каждой платформы.

Text-x-java-source.svg

Application-x-executable.svg

Java.pngJava.pngJava.png

Windows icon.svg Apple Logo.svg Tux-shaded.svg


3.1.1. Пример кода

Рассмотрим следующий пример на языке 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 

3.2. Промежуточный код для языков. 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 значительно легче, чем на ассемблере.


3.2.1. Пример кода

Пример программы на 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  } 

3.3. Parrot

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

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

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


3.3.1. Основные компоненты Parrot

3.3.1.1. Парсеры PASM и PIR

Для компиляции исходного кода в PIR (Parrot Intermediate Representation) - промежуточное представление Parrot - доступно два парсеры. IMCC используется сейчас, но неэффективно. PIRC является ефетктивнишим, но пока нестабилен. Планируется сделать PIRC основным парсером для PIR к выходу версии Parrot 1.0.

3.3.1.2. Компилятор байт-кода и оптимизатор

Компилятор байт-кода - составная Parrot, отвечающий за преобразование входных кода на PASM или PIR в байт-код Parrot. Этот байт-код выполняется быстро и эффективно.

Другим компонентом Parrot является оптимизатор байт-кода, который отвечает за низкоуровневые оптимизации байт-кода Parrot.

3.3.1.3. Итерпретатор

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

3.3.2. Пример кода

3.3.2.1. 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 
3.3.2.2. PASM

Пример цикла:

 set  I1,  1  REDO: gt I1,  10  ,  END  print I1 print  ""  inc I1 bran  ch  REDO  END  : Print  "\ N"  end 
3.3.2.3. Результат
 0  1  2  3  4  5  6  7  8  9  10 

См.. также

Источники

  1. Jeff Fox. "Chapter 2. More Interpretation". Thoughtful Programming and Forth (en). UltraTechnology. Архив оригинала по 2011-08-22 . Проверено 12 мая 2013 .