
- •Глава 13 Основные
- •Определение компилятора. Отличие компилятора от транслятора
- •Определение интерпретатора. Разница между интерпретаторами и трансляторами
- •Назначение трансляторов, компиляторов и интерпретаторов. Примеры реализации
- •Этапы трансляции. Общая схема работы транслятора
- •Понятие прохода. Многопроходные и однопроходные компиляторы
- •Интерпретаторы. Особенности построения интерпретаторов
- •Трансляторы с языка ассемблера («ассемблеры»)
- •Реализация компиляторов с языка ассемблера
- •Макроопределения и макрокоманды
- •Простейшие методы построения таблиц идентификаторов
- •Построение таблиц идентификаторов по методу бинарного дерева
- •Построение таблиц идентификаторов на основе хэш-функций
- •Построение таблиц идентификаторов по методу цепочек
- •Комбинированные способы построения таблиц идентификаторов
- •Принципы построения лексических анализаторов
- •Определение границ лексем
- •Выполнение действий, связанных с лексемами
- •Построение лексических анализаторов
- •Автоматизация построения лексических анализаторов (программа lex)
- •Дерево разбора. Преобразование дерева разбора в дерево операций
- •1 Повтор того, что сказано до начала Шагов. — Примеч. Ред.
- •Автоматизация построения синтаксических анализаторов (программа yacc)
Определение интерпретатора. Разница между интерпретаторами и трансляторами
Кроме схожих между собой понятий «транслятор» и «компилятор» существует принципиально отличное от них понятие интерпретатора.
Интерпретатор — это программа, которая воспринимает входную программу на исходном языке и выполняет ее.
B отличие от трансляторов интерпретаторы не порождают результирующую программу (и вообще какого-либо результирующего кода) — и в этом принципиальная разница между ними. Интерпретатор, так же как и транслятор, анализирует текст исходной программы. Однако он не порождает результирующей программы, а сразу же выполняет исходную в соответствии с ее смыслом, заданным семантикой входного языка. Таким образом, результатом работы интерпретатора будет результат, заданный смыслом исходной программы, в том случае, если эта программа правильная, или сообщение об ошибке, если исходная программа неверна.
Конечно, чтобы исполнить исходную программу, интерпретатор так или иначе должен преобразовать ее в язык машинных кодов, поскольку иначе выполнение программ на компьютере невозможно. Он и делает это, однако полученные машинные коды не являются доступными — их не видит пользователь интерпретатора. Эти машинные коды порождаются интерпретатором, исполняются и унич-
Следует особо упомянуть, что сейчас в современных системах программирования стали появляться компиляторы, в которых результирующая программа создается не на языке машинных команд и не на языке ассемблера, а на некотором промежуточном языке. Сам по себе этот промежуточный язык не может непосредственно исполняться на компьютере, а требует специального промежуточного интерпретатора для выполнения написанных на нем программ. Хотя в данном случае термин «транслятор» был бы, наверное, более правильным, в литературе употребляется понятие «компилятор», поскольку промежуточный язык является языком очень низкого уровня, будучи родственным машинным командам и языкам ассемблера.
Здесь возникает извечный вопрос «о курице и яйце». Конечно, в первом поколении самые первые компиляторы писались непосредственно на машинных командах, но потом, с появлением компиляторов, от этой практики отошли. Даже самые ответственные части компиляторовсоздаются, как минимум, с применением языка ассемблера — а он тоже обрабатывается компилятором.
тожаются по мере надобности — так, как того требует конкретная реализация интерпретатора. Пользователь же видит результат выполнения этих кодов — то есть результат выполнения исходной программы (требование об эквивалентности исходной программы и порожденных машинных кодов и в этом случае, безусловно, должно выполняться).
Более подробно вопросы, связанные с реализацией интерпретаторов и их отличием от компиляторов, рассмотрены далее в соответствующем разделе.
Назначение трансляторов, компиляторов и интерпретаторов. Примеры реализации
Первые программы, которые создавались еще для ЭВМ первого поколения, писались непосредственно на языке машинных кодов. Это была поистине адская работа. Сразу стало ясно, что человек не должен и не может говорить на языке машинных команд, даже если он специалист по вычислительной технике. Однако и все попытки научить компьютер говорить на языках людей успехом не увенчались и вряд ли когда-либо увенчаются (на что есть определенные объективные причины, рассмотренные в первой главе этого пособия).
C тех пор все развитие программного обеспечения компьютеров неразрывно связано с возникновением и развитием компиляторов.
Первыми компиляторами были компиляторы с языков ассемблера или, как они назывались, мнемокодов. Мнемокоды превратили «филькину грамоту» языка машинных команд в более-менее доступный пониманию специалиста язык мнемонических (преимущественно англоязычных) обозначений этих команд. Создавать программы уже стало значительно проще, но исполнять сам мнемокод (язык ассемблера) ни один компьютер неспособен, соответственно, возникла необходимость в создании компиляторов. Эти компиляторы элементарно просты, но они продолжают играть существенную роль в системах программирования по сей день. Более подробно о языке ассемблера и компиляторах с него рассказано далее в соответствующем разделе.
Следующим этапом стало создание языков высокого уровня. Языки высокого уровня (к ним относится большинство языков программирования) представляют собой некоторое промежуточное звено между чисто формальными языками и языками естественного общения людей. От первых им досталась строгая формализация синтаксических структуру предложений языка, от вторых — значительная часть словарного запаса, семантика основных конструкций и выражений (с элементами математических операций, пришедшими из алгебры). Появление языков высокого уровня существенно упростило процесс программирования, хотя и не свело его до «уровня домохозяйки», как самонадеянно полагали некоторые авторы на заре рождения языков программирования1. Сначала
1 Вопрос о творчестве в процессе написания программ — вопрос спорный. Автор не склонен сводить процесс программирования к простому «кодированию», как это предполагают некоторые. Однако даже если и так, то творческий процесс выходит на другой уровень — на уровень постановки задачи, создание алгоритмов ее решения. B общем, этот процесс ориентирован на человека, на пользователя и без его участия тут не обойтись. Все системы искусственного интеллекта пока не продвинулись дальше уровня мухи.
таких языков были единицы, затем десятки, сейчас, наверное, их насчитывается более сотни. Процессу этому не видно конца. Тем не менее по-прежнему преобладают компьютеры традиционной, «неймановской», архитектуры, которые умеют понимать только машинные команды, поэтому вопрос о создании компиляторов продолжает быть актуальным.
Как только возникла массовая потребность в создании компиляторов, стала развиваться и специализированная теория. Co временем она нашла практическое приложение во множестве созданных компиляторов. Компиляторы создавались и продолжают создаваться не только для новых, но и для давно известных языков. Многие производители от известных, солидных фирм (таких, как Microsoft или Inprise) до мало кому знакомых коллективов авторов выпускают на рынок все новые и новые образцы компиляторов. Это обусловлено рядом причин, которые будут рассмотрены далее.
Наконец, с тех пор как большинство теоретических аспектов в области компиляторов получили свою практическую реализацию (а это, надо сказать, произошло довольно быстро, в конце 60-х годов), развитие компиляторов пошло по пути их дружественности человеку — пользователю, разработчику программ на языках высокого уровня. Логичным завершением этого процесса стало создание систем программирования — программных комплексов, объединяющих в себе кроме непосредственно компиляторов множество связанных с ними компонентов программного обеспечения. Появившись, системы программирования быстро завоевали рынок и ныне в массе своей преобладают на нем (фактически, обособленные компиляторы — это редкость среди современных программных средств). O том, что представляют собой и как организованы современные системы программирования, см. в главе «Современные системы программирования».
Ныне компиляторы являются неотъемлемой частью любой вычислительной системы. Без их существования программирование любой прикладной задачи было бы затруднено, а то и просто невозможно. Да и программирование специализированных системных задач, как правило, ведется если не на языке высокого уровня (в этой роли в настоящее время чаще всего применяется язык C), то на языке ассемблера, следовательно, применяется соответствующий компилятор. Программирование непосредственно на языках машинных кодов происходит исключительно редко и только для решения очень узких вопросов.
Несколько слов о примерах реализации компиляторов и интерпретаторов, а также о том, как они соотносятся с другими существующими программными средствами.
Компиляторы, как будет показано далее, обычно несколько проще в реализации, чем интерпретаторы. По эффективности они также превосходят их — очевидно, что откомпилированный код будет исполняться всегда быстрее, чем происходит интерпретация аналогичной исходной программы. Кроме того, не каждый язык программирования допускает построение простого интерпретатора. Однако интерпретаторы имеют одно существенное преимущество — откомпилированный код всегда привязан к архитектуре вычислительной системы, на которую он ориентирован, а исходная программа — только к семантике языка программирования, которая гораздо легче поддается стандартизации. Этот аспект первоначально не принимали во внимание.
Первыми компиляторами были компиляторы с мнемокодов. Их потомки — современные компиляторы с языков ассемблера — существую практически для всех известных вычислительных систем. Они предельно жестко ориентированы на архитектуру. Затем появились компиляторы с таких языков, как FORTRAN, ALGOL-68, PL/1. Они были ориентированы на большие ЭВМ с пакетной обработкой задач. Из вышеперечисленных только FORTRAN, пожалуй, продолжает использоваться по сей день, поскольку имеет огромное количество библиотек различного назначения [7]. Многие языки, родившись, так и не получили широкого распространения — ADA, Modula, Simula известны лишь узкому кругу специалистов. B то же время на рынке программных систем доминируют компиляторы языков, которым не прочили светлого будущего. B первую очередь, сейчас это C и C++. Первый из них родился вместе с операционными системами типа UNIX, вместе с нею завоевал свое «место под солнцем», а затем перешел под OC других типов. Второй удачно воплотил в себе пример реализации идей объектно-ориентированного программирования на хорошо зарекомендовавшей себя практической базе1. Еще можно упомянуть довольно распространенный Pascal, который неожиданно для многих вышел за рамки чисто учебного языка для университетской среды.
История интерпретаторов не столь богата (пока!). Как уже было сказано, изначально им не предавали существенного значения, поскольку почти по всем параметрам они уступают компиляторам. Из известных языков, предполагавших интерпретацию, можно упомянуть разве что Basic, хотя большинству сейчас известна его компилируемая реализация Visual Basic, сделанная фирмой Microsoft [3, 63]. Тем не менее сейчас ситуация несколько изменилась, поскольку вопрос о переносимости программ и их аппаратно-платформенной независимости приобретает все большую актуальность с развитием сети Интернет. Самый известный сейчас пример — это язык^ѵа (сам по себе он сочетает компиляцию и интерпретацию), а также связанный с HHMjavaScript. B конце концов, язык HTML, на котором зиждется протокол HTTP, давший толчок столь бурному развитию Всемирной сети, — это тоже интерпретируемый язык. По мнению автора, в области появления новых интерпретаторов всех еще ждут сюрпризы, и появились уже первые из них — например, язык C# («си-диез», но название везде идет как «Си шарп»), анонсируемый фирмой Microsoft.
Об истории языков программирования и современном состоянии рынка компиляторов можно говорить долго и много. Автор считает возможным ограничиться уже сказанным, поскольку это не является целью данного пособия. Желающие могут обратиться к литературе [7, 8, 14, 23, 30, 45, 66, 77, 81].