Схема работы транслятора с языка Ассемблера.
Сейчас мы рассмотрим, как транслятор преобразует входной модуль на "чистом" языке Ассемблера (уже без макросредств) в объектный модуль. Разумеется, мы изучим только общую схему этого, достаточно сложного процесса. Наша цель – рассмотреть основные принципы работы транслятора с языка Ассемблера и ввести необходимую терминологию. Более подробно этот вопрос, который относится к большой теме "Формальные грамматики и методы компиляции", Вы будете изучать в другом курсе.
Итак, как мы знаем, Ассемблер относится к классу системных программ, которые называются компиляторами [73] – переводчиками с одного алгоритмического языка на другой. Наш транслятор переводит модуль с языка Ассемблера на объектный язык. При трансляции выполняются следующие шаги.
§ Анализ входного модуля на наличие ошибок.
§ Выдача протокола работы транслятора – так называемого листинга, а также выдачу аварийных сообщений об ошибках.[74] Выдачу листинга при желании, как правило, можно заблокировать.
§ Генерация объектного модуля.
Разберём более подробно первый этап – анализ программы на наличие ошибок. Ясно, что найти ошибку можно, только просмотрев весь текст модуля, строка за строкой. Каждый просмотр текста программного модуля транслятором называется проходом. Наш транслятор с Ассемблера просматривает программу дважды, т.е. совершает два прохода. Такие трансляторы называются двухпроходными. Двухпроходная схема трансляции наиболее простая, можно, однако, усложнив алгоритм, производить трансляцию и за один проход (например, так работает транслятор с языка Турбо-Паскаль).
На первом проходе транслятор с Ассемблера, анализируя текст программы, находит многие (но не все) ошибки, и строит некоторые важные таблицы, данные из которых используются как на первом, так и на втором проходе. Вкратце алгоритм первого прохода состоит в следующем. Так как основной синтаксической единицей нашего языка является предложение Ассемблера, то рассмотрим, как происходит обработка предложений на первом проходе.
Сначала работает специальная программа, которая называется лексическим анализатором. Она разбивает каждое предложение программы на лексемы – логически неделимые единицы языка. О лексемах мы уже должны немного знать из языка Паскаль. Как и в Паскале, в языке Ассемблера существуют следующие классы лексем.
§ Имена. Как и в Паскале, это ограниченные последовательности букв и цифр, начинающиеся с буквы, при этом большие и маленькие буквы не различаются. Как мы помним, в Паскале к буквам относился также и символ подчёркивания, а в Ассемблере к буквам относятся и такие символы, как вопросительный знак, точка (правда, только в первой позиции и у служебных имён) и некоторые другие. Все имена Ассемблера делятся на служебные и имена пользователя.[75]
§ Числа. Язык Ассемблера, как и Турбо-Паскаль, допускает запись чисел в различных системах счисления (десятичной, двоичной, шестнадцатеричной и некоторых других). Не надо забывать и о вещественных числах.
§ Строки символов. Строки символов в Ассемблере ограничены либо апострофами, либо двойными кавычками. Ещё раз напомним, что в нашем упрощённом изложении алгоритма работы компилятора мы считаем, что макропроцессор, который рассматривал фактические параметры макрокоманд тоже как строки символов, ограниченных запятыми, пробелами или точной с запятой, уже закончил свою работу.
§ Разделители. Как и в Паскале, лексемы перечисленных выше классов не могут располагаться в тексте программы подряд, между ними обязательно должна находиться хотя бы одна лексема-разделитель. К разделителям относятся знаки арифметических операций, почти все знаки препинания, пробел и некоторые другие символы.
§ Комментарии. Эти лексемы не влияют на выполнения алгоритма, заложенного в программу, они переносятся в листинг, а из анализируемого текста удаляются. Исключением являются макрокомментарии, которые начинаются с двух символов ';;', как мы знаем, такие комментарии не переносятся в листинг.
В качестве примера ниже показано предложение Ассемблера, каждая лексема в нём выделена в прямоугольник:
Metka : mov ax , Mas + 67 [ bx ] ; Комментарий
На этапе лексического анализа выявляются лексические ошибки, когда некоторая группа символов не может быть отнесена ни к одному из классов лексем. Примеры лексических ошибок:[76]
134X 'A'38 B"C
Все опознанные (правильные) лексемы записываются в специальную таблицу лексем. Это делается в основном для того, чтобы на втором проходе уже двигаться по программе по лексемам, а не по отдельныс составляющим их символам, что позволяет существенно ускорить просмотр программного модуля.
После разбиения предложения Ассемблера на лексемы начинается второй этап обработки предложения – этап синтаксического анализа. Этот этап выполняет программа, которая называется синтаксическим анализатором. Алгоритмы синтаксического анализа весьма сложны, и здесь мы не будем их рассматривать, это, как уже упоминалось, тема отдельного курса. Если говорить совсем коротко, то синтаксический анализатор пытается из лексем построить более сложные конструкции предложения: поля метки, кода операции и операндов. Особое внимание на этом этапе уделяется в программе именам пользователя, они заносятся синтаксическим анализатором в специальную таблицу имён. Вместе с каждым именем в эту таблицу заносятся и атрибуты (свойства) имени. Всего в Ассемблере у имени пользователя различают четыре атрибута, ниже перечислены эти атрибуты (не у каждого имени есть все из них).
§ Атрибут сегмента Segment. Этот атрибут имеет формат i16, он задаёт адрес начала того сегмента, в котором описано имя, делённый на 16.
§ Атрибут смещения Offset, он также имеет формат i16 и задаёт смещение имени от начала того сегмента, в котором оно описано. Атрибуты Segment и Offset могут иметь только имена, определяющие области памяти и метки команд.
§ Атрибут типа Type. С этим атрибутом имени мы уже знакомы, для переменных он равен длине переменной в байтах, а для меток равен –1 и –2 для близкой и дальней метки соответственно. Все остальные имена имеют тип ноль (в некоторых учебниках по Ассемблеру это трактуется как отсутствие типа, при этом говорится, что у таких имён атрибута Type нет).
§ Атрибут значения Value. Этот атрибут определён только для имён сегментов, а также для констант и переменных периода генерации.
Приведём примеры описаний имён, которые имеют первые три из перечисленных выше атрибутов:
A db 13; Не имеет атрибута Value !
B equ A
C: mov B,1
D equ C
А теперь примеры имён, у которых есть только атрибут Value (и атрибут Type = 0):
N equ 100
M equ N+1
K=0
P equ K
Data segment
Рассмотрим теперь пример маленького, синтаксически правильного, но, вообще говоря, бессмысленного программного модуля, для которого построим таблицу имён пользователя:
Data segment
A dw 19
B db ?
Data ends
Code segment
extrnX:far
mov ax,Data
mov cx,R
jmp L
R equ -14
call X
L: ret
Code ends
End
На рис. 13.1 показана таблица имён, которая построится синтаксическим анализатором при просмотре программы до предложения
mov ax,Data
включительно. Атрибуты, которые не могут быть у данного имени, отмечены прочерком.
Как мы уже знаем, значения некоторых имён неизвестны на этапе компиляции, эти значения мы проставили в нашей таблице как i16=?. Для каждого заносимого в таблицу имени, кроме атрибутов, ещё запоминается и место в предложении, на котором встретилось имя. В Ассемблере имя пользователя может располагаться в поле метки (тогда это определение имени) и в поле операндов (тогда это использование имени).
Имя | Segment | Offset | Type | Value | ||
Data | ––– | ––– | i16=? | |||
A | Data | ––– | ||||
B | Data | ––– | ||||
Code | ––– | ––– | i16=? | |||
X | i16=? | i16=? | -2 | ––––– | ||
Рис. 13.1. Вид таблица имён модуля после анализа предложения mov ax,Data . | ||||||
Итак, теперь синтаксический анализатор рассматривает предложение
mov cx,R
и заносит в таблицу имён имя R. Про это имя ещё ничего неизвестно, поэтому и все поля атрибутов в таблице имеют неопределённые значения:
R | ? | ? | ? | ? |
Соответственно, невозможно определить и точный код операции команды mov cx,R , так как второй операнд этой команды может иметь формат r16,m16 или i16. Здесь ярко видна необходимость второго просмотра программы: только на втором просмотре, после получения информации об атрибутах имени R, возможно определить правильный формат этой команды (а значит, определить и длину этой команды в байтах).
После полного просмотра программы синтаксический анализатор построит таблицу имён, показанную на рис. 13.2.
Имя | Segment | Offset | Type | Value | ||
Data | ––– | ––– | I16=? | |||
A | Data | ––– | ||||
B | Data | ––– | ||||
Code | ––– | ––– | I16=? | |||
X | i16=? | i16=? | -2 | ––– | ||
R | ––– | ––– | -14 | |||
L | Code | -1 | ––– | |||
Рис. 13.2. Вид таблицы имён после анализа всей программы. | ||||||
На этапе синтаксического анализа выявляются все синтаксические ошибки в программе, например:
mov ax,bl; Несоответствие типов
add A,A+2; Несуществующий формат команды
sub cx; Мало операндов
и т.д. Используя таблицу имён, синтаксический анализатор легко обнаруживает ошибки следующего вида:
1. Неописанное имя. Имя встречается только в поле операндов и не встречается в поле метки.
2. Дважды описанное имя. Одно и то же имя дважды (или большее число раз) встретилось в поле метки.[77]
3. Описанное, но не используемое имя. Это предупредительное сообщение может выдаваться некоторыми "продвинутыми" Ассемблерами, если имя определено в поле метки, но ни разу не встретилось в поле операндов (видимо, программист что-то упустил).
Кроме того, синтаксический анализатор может обнаружить и ошибки, относящиеся к модулю в целом, например, превышение максимальной длины некоторого сегмента.
Итак, на втором проходе синтаксический анализатор может обнаружить все ошибки и однозначно определить формат каждой команды. Теперь можно приступать к последнему этапу работы транслятора – генерации объектного модуля. На этом этапе все числа преобразуются во внутреннее машинное представление, выписывается битовое представление всех команд, оформляется паспорт объектного модуля. Далее полученный объектный модуль записывается в библиотеку объектных модулей (обычно куда-нибудь в дисковую память).
На этом мы завершим наше краткое знакомство со схемой трансляции исходного модуля с языка Ассемблера на объектный язык.
Дата добавления: 2015-10-05; просмотров: 1685;