Подтвердить что ты не робот

В чем разница между реализацией компилятора и интерпретатором?

Недавно я прочитал всю книгу Дракона (просто для удовольствия, я на самом деле не планирую реализовать настоящий компилятор), и я остался с этим большим вопросом, болтающимся в моей голове.

Чем отличается реализация компилятора и интерпретатора?

Для меня компилятор состоит из:

  • Лексер
  • Parser (который создает дерево синтаксиса)
  • Сгенерировать промежуточный код (например, 3-х адресный код)
  • Сделайте все эти безумные вещи, чтобы оптимизировать, если хотите: -)
  • Сгенерировать "сборку" или "собственный код" из 3-х адресного кода.

Теперь, очевидно, интерпретатор также имеет тот же лексер и парсер, что и компилятор.
Но что он делает после этого?

  • Он "читает" дерево синтаксиса и выполняет его непосредственно? (вроде как указатель инструкции, указывающий на текущий node в дереве, а выполнение - один большой обход дерева плюс управление памятью для стека вызовов) (и если да, то как это делается? надеясь, что выполнение лучше, чем огромный оператор switch, который проверяет, какой тип node он)

  • Создает ли он 3 кода адреса и интерпретирует это? (если да, как это делается? Опять же, я ищу что-то более элегантное, чем инструкция с переключателем длиной в милю)

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

Также, в какой момент разрезается понятие "виртуальная машина"? Что вы используете на языке виртуальной машины? (чтобы быть понятным о моем уровне невежества, для меня виртуальная машина - VMWare, я понятия не имею, как концепция виртуальной машины применяется к языкам программирования/исполняющим программам).

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

Спасибо!
Daniel


EDIT: Спасибо за ваши ответы. Я понял, что мой титул вводит в заблуждение. Я понимаю "функциональную" разницу между компилятором и интерпретатором.
То, что я ищу, - это разница в том, как вы реализуете интерпретатор, а не компилятор.
Теперь я понимаю, как реализован компилятор, вопрос в том, как интерпретатор отличается от этого.

Например: VB6 явно является компилятором и интерпретатором. Теперь я понимаю часть компилятора. Тем не менее, я не могу понять, как при работе внутри IDE это может позволить мне остановить программу в любой произвольной точке, изменить код и возобновить выполнение с помощью нового кода. Это всего лишь один крошечный пример, это не тот ответ, который я ищу. То, что я пытаюсь понять, как я объясняю ниже, - это то, что происходит после того, как у меня есть дерево синтаксического анализа. Компилятор будет генерировать новый код из него на "целевом" языке. Что делает интерпретатор?

Благодарим за помощь!

4b9b3361

Ответ 1

короткий ответ:

  • компилятор преобразует исходный код в исполняемый формат для последующего выполнения
  • интерпретатор оценивает исходный код для немедленного выполнения.

существует большая свобода в том, как они реализованы. Интерпретатор может генерировать собственный машинный код, а затем выполнять его, тогда как компилятор для виртуальной машины может генерировать p-код вместо машинного кода. Нитевидные интерпретируемые языки, такие как Forth, ищут ключевые слова в словаре и сразу же выполняют соответствующую функцию родного кода.

компиляторы лучше оптимизировать, потому что у них больше времени для изучения кода и создания файла для последующего исполнения; у переводчиков есть меньше времени для оптимизации, потому что они имеют тенденцию выполнять код "как есть" с первого взгляда

также возможен интерпретатор, который оптимизирован в фоновом режиме, и более эффективные способы выполнения кода.

сводка: разница действительно сводится к "подготовке кода для последующего выполнения" или "прямому исполнению кода"

Ответ 2

Компилятор - это программа, которая переводит программу на одном языке программирования в программу на другом языке программирования. Это просто и просто.

Интерпретатор переводит язык программирования в его семантический смысл.

Чип x86 является интерпретатором для машинного языка x86.

Javac - это компилятор для java для виртуальной машины Java. java, исполняемое приложение, является интерпретатором для jvm.

Некоторые интерпретаторы разделяют некоторые элементы компиляции тем, что они могут переводить один язык на другой внутренний язык, который легче интерпретировать.

Интерпретаторы обычно, но не всегда, имеют цикл чтения-eval-print.

Ответ 3

A программа - это описание работы, которую вы хотите сделать.

A компилятор преобразует описание высокого уровня в более простое описание.

интерпретатор читает описание того, что делать и делает работу.

  • Некоторые интерпретаторы (например, Unix shells) читают описание одной маленькой части за раз и действуют на каждую часть, как они ее видят; некоторые (например, Perl, Python) читают все описание, внутренне конвертируют его в более простую форму и затем действуют на это.
  • Некоторые переводчики (например, Java JVM или чип Pentium 4) понимают только простой язык описания, который слишком утомительно для людей, чтобы работать с ним напрямую, поэтому люди используют компиляторы для преобразования своих описаний высокого уровня на этот язык.

Составители никогда не выполняют эту работу. Интерпретаторы всегда выполняют работу.

Ответ 4

Оба имеют много общего (например, лексический парсер), и есть разногласия относительно разницы. Я смотрю так:

Классическое определение будет заключаться в том, что компилятор анализирует и преобразует поток символов в поток байтов, который может выполняться процессором, тогда как интерпретатор делает то же самое, но переводит их в форму, которая должен выполняться на части программного обеспечения (например, JVM, CLR).

Тем не менее люди называют "javac" компилятором, поэтому неформальное определение компилятора - это что-то, что нужно сделать для исходного кода как отдельный шаг, в то время как у интерпретаторов нет шага "build" (например, PHP, Perl).

Ответ 5

Это не так ясно, как раньше. Раньше было построение дерева синтаксического анализа, привязка его и выполнение (часто привязка в последнюю секунду).

BASIC был в основном сделан таким образом.

Вы можете утверждать, что вещи, которые запускают байт-код (java/.net) без JIT, являются interpriters, но не в традиционном смысле, так как вам еще нужно "скомпилировать" байт-код.

Старая разница в школе была: если он генерирует код процессора, это компилятор. Если вы запускаете его непосредственно в среде редактирования и можете взаимодействовать с ним во время редактирования, это будет interpriter.

Это было гораздо менее формально, чем настоящая книга Дракона, но я надеюсь, что это будет информативным.

Ответ 6

Если мой опыт показывает что-либо,

  • Интерпретаторы не пытаются уменьшать/обрабатывать AST далее, каждый раз, когда ссылается блок кода, выполняется соответствующий AST node. Компиляторы пересекают блок не более нескольких раз, чтобы генерировать исполняемый код в определенном месте и выполняться с ним.
  • Символьная таблица интерпретаторов сохраняет значения и ссылается во время выполнения, таблица символов компиляторов сохраняет места переменных. Во время выполнения такой таблицы символов нет.

В кадре разница может быть такой же простой, как

case '+':
    symtbl[var3] = symtbl[var1] + symtbl[var2];
    break;

между

case '+':
    printf("%s = %s + %s;",symtbl[var3],symtbl[var1],symtbl[var2]);
    break;

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

Ответ 7

В отношении этой части вашего вопроса, на которую другие ответы на самом деле не обсуждались:

Кроме того, в какой момент концепция от "виртуальной машины"? Что вы используете виртуальную машину для язык?

Виртуальные машины, такие как JVM или CLR, представляют собой слой абстракции, который позволяет повторно использовать оптимизацию компилятора JIT, сборку мусора и другие детали реализации для совершенно разных языков, которые скомпилированы для работы на виртуальной машине.

Они также помогут вам сделать спецификацию языка более независимой от реального оборудования. Например, в то время как C-код теоретически переносится, вам постоянно приходится беспокоиться о таких вещах, как endianness, размер шрифта и выравнивание переменных, если вы действительно хотите создать переносимый код. В то время как с Java JVM очень четко указывается в этом отношении, поэтому разработчику языка и его пользователям не нужно беспокоиться о них; это задача реализатора JVM для реализации указанного поведения на реальном оборудовании.

Ответ 8

Как только дерево синтаксического анализа доступно, существует несколько стратегий:

1) непосредственно интерпретируют AST (Ruby, WebKit оригинал-интерпретатор) 2) преобразование кода → в байтовые коды или машинный код

Чтобы достигнуть Edit-and-Continue, счетчик программ или указатель инструкции должны быть пересчитаны и перемещены. Для этого требуется сотрудничество с IDE, поскольку код может быть вставлен до или после маленькой желтой стрелки.

Один из способов сделать это - вставить положение счетчика программ в дерево разбора. Например, может быть специальный оператор, называемый "break". Счетчик программ должен быть установлен только после того, как команда "break" продолжит работу.

Кроме того, вам нужно решить, что вы хотите сделать с текущим фреймом стека (и переменными в стеке). Возможно, вытащить текущий стек и скопировать переменные или сохранить стек, но патчем в GOTO и RETURN на текущий код.

Ответ 9

Учитывая ваш список шагов:

  • Лексер
  • Parser (который создает дерево синтаксиса)
  • Сгенерировать промежуточный код (например, 3-х адресный код)
  • Сделайте все эти безумные вещи, чтобы оптимизировать, если хотите: -)
  • Сгенерировать "сборку" или "собственный код" из 3-х адресного кода.

Очень простой интерпретатор (например, ранние BASIC или TCL) будет выполнять только шаги один и два по одной строке за раз. Затем отбросьте большую часть результатов, перейдя к следующей строке, которая будет выполнена. Остальные 3 шага никогда не будут выполнены вообще.

Ответ 10

Если вы ищете книгу, Структура и интерпретация компьютерных программ ( "Книга мастеров" ) - это хорошее место для начала с концепциями интерпретатора. Вы только когда-либо имеете дело с кодом Схемы, который может быть пройден, оценен и передан, как если бы это был АСТ.

Кроме того, Peter Norvig имеет краткий пример, объясняющий основную идею с использованием Python (со многими другими примерами в комментариях), а вот еще один небольшой пример в Википедии.

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

Обратите внимание, что вам не всегда нужно делать обход дерева явно: вы можете сгенерировать свой АСТ таким образом, чтобы он принимал посетителя (я думаю, SableCC делает это) или для очень маленьких языков, таких как малые арифметические грамматики используемый для демонстрации генераторов парсеров, вы можете просто оценить результат во время разбора.

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

Для виртуальных машин: цоколь и j_random_hacker описывают компьютерное оборудование как своего рода интерпретатор. И наоборот: переводчики - это машины; их инструкции оказались выше, чем у реальной ISA. Для интерпретаторов в стиле VM программы действительно напоминают машинный код, albiet для очень простой машины. Java-байт-код использует только несколько "регистров", один из которых содержит программный счетчик. Таким образом, интерпретатор VM больше похож на аппаратный эмулятор, чем интерпретаторы в приведенных выше примерах.

Но обратите внимание, что по соображениям скорости Java JVM по умолчанию работает, переведя проги из инструкций байткода Java в инструкции x86 ( "только во время компиляции" ).