Студопедия КАТЕГОРИИ: АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция |
Особенности построения интерпретаторов
Интерпретатор − это программа, которая воспринимает входную программу на исходном языке и выполняет её. Термин «интерпретатор», как и «транслятор» означает «переводчик». Но с точки зрения формальных языков, отличаются они принципиально. Большинство интерпретаторов последовательно исполняют исходную программу по мере поступления ее на вход интерпретатора. При этом пользователю нет смысла ждать завершения компиляции всей исходной программы. Исходя из этой особенности (исполнение команд по мере их поступления) в интерпретаторах отсутствует фаза оптимизации. А также на последнем этапе − этапе генерации кода − машинные команды не записываются в объектный файл, а выполняются. Кроме того, далеко не все языки программирования допускают построение интерпретаторов, которые выполняли бы исходные программы по мере поступления команд. Последнее требование предусматривает существование компилятора, разбирающего исходную программу за один проход. Основное требование интерпретаторов − обработка программы по мере поступления − нарушается, если язык допускает обращения к функциям и структурам данных раньше их непосредственного описания. По этой причине не могут интерпретироваться такие языки, как Си и Паскаль. Из-за отсутствия в интерпретаторах фазы оптимизации выполнение программы с помощью интерпретатора менее эффективно, чем с помощью аналогичного компилятора. В добавок, интерпретируемая программа должна разбираться каждый раз при выполнении, а при компиляции она разбирается единожды. Затем используется объектный код или файл. Поэтому интерпретаторы всегда уступают компиляторам в производительности. Преимуществом интерпретаторов является независимость выполнения программы от архитектуры целевой вычислительной системы. При переходе на другую архитектуру в случае компилятора требуется откомпилировать программу заново, т.к. объектный код всегда ориентируется на определенную архитектуру. Долгое время интерпретаторы были менее распространены, чем компиляторы, и существовали для относительно простых языков программирования (Basic). Профессиональные средства разработки ПО с высокими требованиями к производительности строились на базе компиляторов. Такое положение существовало до момента широкого распространения глобальных вычислительных сетей. Как правило, такие сети представляют собой набор ЭВМ различной архитектуры. Из-за этого на первый план выходит требование единообразного выполнения на каждой ЭВМ сети исходной программы. Многие языки программирования, которые используются в сети Интернет, предусматривают механизм интерпретации исходного текста программы вместо компиляции. В качестве примера интерпретируемого языка широкого распространения выступает HTML (Hypertext Markup Language) язык описания гипертекста. Он лежит в основе функционирования большинства структур сети Интернет. Языки Java и Java Script сочетают функции компиляции и интерпретации. На первом этапе исходная программа компилируется в некоторый двоичный код, который является промежуточным и не зависит от архитектуры целевого компьютера. Этот код передается по сети и выполняется принимающим компьютером в виде интерпретации. Язык ассемблера − это язык низкого уровня. Структура и взаимосвязь цепочек этого языка близки к машинным командам целевой вычислительной системы, где должна выполняться результирующая программа. Применение языка ассемблера позволяет разработчику управлять ресурсами (процессором, оперативной памятью, внешними устройствами и т.п.) целевой вычислительной системы на уровне машинных команд. Каждая команда исходной программы на языке ассемблера в результате компиляции преобразуется в одну машинную команду. Транслятор с языка ассемблера всегда, безусловно, будет и компилятором, поскольку языком результирующей программы являются машинные коды. Транслятор с языка ассемблера зачастую просто называют «ассемблер» или «программа ассемблера». Язык ассемблера, как правило, содержит мнемонические коды машинных команд. Чаще всего используется англоязычная мнемоника команд, но существуют и другие варианты языков ассемблера (в том числе существуют и русскоязычные варианты). Именно поэтому язык ассемблера, раньше носил название «язык мнемокодов» (сейчас это название уже практически не употребляется). Все возможные команды в каждом языке ассемблера можно разбить на две группы: в первую входят обычные команды языка, которые в процессе трансляции преобразуются в машинные команды; вторую составляют специальные команды языка, которые в машинные команды не преобразуются, но используются компилятором для выполнения задач компиляции (таких, например, как задача распределения памяти). Синтаксис языка чрезвычайно прост. Команды исходной программы записываются обычно таким образом, чтобы на одной строке программы располагалась одна команда. Каждая команда языка ассемблера, как правило, может быть разделена на три составляющих, следующих последовательно одна за другой: поле метки, код операции и поле операндов. Компилятор с языка ассемблера обычно предусматривает и возможность наличия во входной программе комментариев, которые отделяются от команд заданным разделителем. Поле метки содержит идентификатор, представляющий собой метку, либо является пустым. Каждый идентификатор метки может встречаться в программе на языке ассемблера только один раз. Метка считается описанной там, где она непосредственно встретилась в программе (предварительное описание меток не требуется). Метка может быть использована для передачи управления на помеченную ею команду. Нередко метка отделяется от остальной части команды специальным разделителем (чаще всего − двоеточием ’’:”). Код операций всегда представляет собой строго определенную мнемонику одной из возможных команд процессора или также строго определенную команду самого компилятора. Код операции записывается алфавитными символами входного языка. Чаще всего его длина составляет 3-4, реже − 5 или 6 символов. Поле операндов либо является пустым, либо представляет собой список из одного, двух, реже − трех операндов. Количество операндов строго определено и зависит от кода операции − каждая операция языка ассемблера предусматривает жестко заданное число своих операндов. Соответственно, каждому из этих вариантов соответствуют безадресные, одноадресные, двухадресные или трёхадресные команды (большее число операндов практически не используется, в современныx ЭBM даже трёхадресные команды встречаются редко). Операндами могут быть идентификаторы или константы. Особенностью языка ассемблера является то, что ряд идентификаторов в нем выделяется специально для обозначения регистров процессора. Такие идентификаторы, с одной стороны, не требуют предварительного описания, но, с другой, они не могут быть использованы пользователем для иных целей. Набор этих идентификаторов предопределен для каждого языка ассемблера. Семантика языка ассемблера целиком и полностью определяется целевой вычислительной системой, на которую ориентирован данный язык. Семантика языка ассемблера определяет, какая машинная команда соответствует каждой команде языка ассемблера, а также то, какие операнды и в каком количестве допустимы для того или иного кода операции. Поэтому семантический анализ в компиляторе с языка ассемблера также прост, как и синтаксический. Основной его задачей является, проверить допустимость операндов для каждого кода операции, а также проверить, что все идентификаторы и метки, встречающиеся во входной программе, описаны и обозначающие их идентификаторы не совпадают с предопределенными идентификаторами, используемыми для обозначения кодов операции и регистров процессора. Схемы синтаксического и семантического анализа в компиляторе с языка ассемблера могут быть, таким образом, реализованы на основе обычного конечного автомата. Именно эта особенность определила тот факт, что компиляторы с языка ассемблера исторически явились первыми компиляторами, созданными для ЭВМ. Существует также ряд других особенностей, которые присущи именно языкам ассемблера и упрощают построение компиляторов для них. Компилятор с языка ассемблера является обычным компилятором, но значительно упрощенным по сравнению с любым компилятором с языка высокого уровня. Компиляторы с языка ассемблера реализуются чаще всего по двухпроходной схеме. На первом проходе компилятор выполняет разбор исходной программы, ее преобразование в машинные коды и одновременно заполняет таблицу идентификаторов. Но на первом проходе в машинных командах остаются незаполненными адреса тех операндов, которые размещаются в оперативной памяти. На втором проходе компилятор заполняет эти адреса и одновременно обнаруживает неописанные идентификаторы. Это связано с тем, что операнд может быть описан в программе после того, как он первый раз был использован. Тогда его адрес еще не известен на момент построения машинной команды, а поэтому требуется второй проход. Типичным примером такого операнда является метка, предусматривающая переход вперед по ходу последовательности команд. Разработка программ на языке ассемблера − достаточно трудоемкий процесс, требующий зачастую простого повторения одних и тех же многократно встречающихся операций. Примером может служить последовательность команд, выполняемых каждый раз для организации стекового дисплея памяти при входе в процедуру или функцию. Для облегчения труда разработчика были созданы так называемые макрокоманды. Макрокоманда представляет собой текстовую подстановку, в ходе выполнения которой каждый идентификатор определенного вида заменяется на цепочку символов из некоторого хранилища данных. Процесс выполнения макрокоманды называется макрогенерацией, а цепочка символов, получаемая в результате выполнения макрокоманды, − макрорасширением. Процесс выполнения макрокоманд заключается в последовательном просмотре текста исходной программы, обнаружении в нем определенных идентификаторов и их замене на соответствующие строки символов. Причем выполняется именно текстовая замена одной цепочки символов (идентификатора) на другую цепочку символов (строку). Такая замена называется макроподстановкой. Для того чтобы указать, какие идентификаторы на какие строки необходимо заменять, служат макроопределения. Макроопределения присутствуют непосредственно в тексте исходной программы. Они выделяются специальными ключевыми словами либо разделителями, которые не могут встречаться нигде больше в тексте программы. В процессе обработки все макроопределения полностью исключаются из текста входной программы, а содержащаяся в них информация запоминается для обработки при выполнении макрокоманд. Макроопределение может содержать параметры. Тогда каждая соответствующая ему макрокоманда должна при вызове содержать строку символов вместо каждого параметра. Эта строка подставляется при выполнении макрокоманды в каждое место, где в макроопределении встречается соответствующий параметр. В качестве параметра макрокоманды может оказаться другая макрокоманда, тогда она будет рекурсивно вызвана всякий раз, когда необходимо выполнить подстановку параметра. В принципе макрокоманды могут образовывать последовательность рекурсивных вызовов, аналогичную последовательности рекурсивных вызовов процедур и функций, но только вместо вычислений и передачи параметров они выполняют лишь текстовые подстановки. Макрокоманды и макроопределения обрабатываются специальным модулем, называемым макропроцессором или макрогенератором. Макрогенератор получает на вход текст исходной программы, содержащий макроопределения и макрокоманды, а на выходе его появляется текст макрорасширения исходной программы, не содержащий макроопределений и макрокоманд. Оба текста являются только текстами программы, никакая другая обработка не выполняется. Именно макрорасширение исходного текста поступает на вход компилятора. Синтаксис макрокоманд и макроопределений не является строго, заданным. Он может различаться в зависимости от реализации компилятора с языка ассемблера. Но сам принцип выполнения макроподстановок в тексте программы неизменен и не зависит от их синтаксиса. Макрогенератор чаще всего не существует в виде отдельного программного модуля, а входит в состав компилятора с языка ассемблера. Макрорасширение исходной программы обычно недоступно ее разработчику. Более того, макроподстановки могут выполняться последовательно при разборе исходного текста на первом проходе компилятора вместе с разбором всего текста программы, и тогда макрорасширение исходной программы в целом может и вовсе не существовать как таковое. Существует возможность создавать более сложные макроопределения с параметрами. Макроопределения и макрокоманды нашли применение не только в языках ассемблера, но и во многих языках высокого уровня. Там их обрабатывает специальный модуль, называемый препроцессором языка (например, широко известен препроцессор языка С). Принцип обработки остается тем же самым, что и для программ на языке ассемблера − препроцессор выполняет текстовые подстановки непосредственно над строками самой исходной программы. В языках высокого уровня макроопределения должны быть отделены от текста самой исходной программы, чтобы препроцессор не мог спутать их с синтаксическими конструкциями входного языка. Для этого используются либо специальные символы и команды (команды препроцессора), которые никогда не могут встречаться в тексте исходной программы, либо макроопределения встречаются внутри незначащей части исходной программы − входят в состав комментариев (такая реализация существует, например, в компиляторе с языка Pascal, созданном фирмой Borland). Макрокоманды, напротив, могут встречаться в произвольном месте исходного текста программы, и синтаксически их вызов может не отличаться от вызова функций во входном языке. Следует помнить, что, несмотря на схожесть синтаксиса вызова, макрокоманды принципиально отличаются от процедур и функций, поскольку не порождают результирующего кода, а представляют собой текстовую подстановку, выполняемую прямо в тексте исходной программы. Результат вызова функции и макрокоманды может из-за этого серьезно отличаться.
|
||
Последнее изменение этой страницы: 2018-05-10; просмотров: 228. stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда... |