Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Теория языков программирования и методы трансляции..pdf
Скачиваний:
28
Добавлен:
05.02.2023
Размер:
3.41 Mб
Скачать

186

Часто увеличение числа проходов компилятора используется искус-

ственно – для уменьшения памяти, занимаемой компилятором в оперативном запоминающем устройстве (ОЗУ). Кроме того, количество проходов зависит не только от особенностей транслируемого языка, но и от используемой ЭВМ и операционного окружения.

· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·

Обычные традиционные трансляторы используют от четы-

рех до восьми проходов, часть из которых тратится на оптимиза-

цию загрузочного кода. Однако для рассмотрения принципов компиляции достаточно остановиться на одно-, максимум – двухпроходных компиляторах.

· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·

7.2 ТАБЛИЦЫ СИМВОЛОВ

Информацию о типе (виде) идентификаторов синтаксический анализа-

тор хранит с помощью таблицы символов (имен) [2]. Этими таблицами также пользуется генератор кода для хранения адресов значений во время прогона.

В языках, имеющих конечное число типов (видов), информацией о типе мо-

жет быть простое целое число, представляющее этот тип, а в языках, имею-

щих потенциально бесконечное число типов (C++, Pascal и др.), – указатель на таблицу видов, элементами которого являются структуры, представляю-

щие вид.

Как уже отмечалось, для простых языков (FORTRAN, BASIC и др.),

имеющих конечное число типов, каждый из них может быть представлен це-

лым числом. Например, тип integer – посредством 1, а тип real – посредством

2. В этом случае таблица символов имеет вид массива с элементами identifier, type.

187

В языках, имеющих ограничение на число литер в идентификаторе,

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

Стаблицей символов ассоциируются следующие действия:

1)Идентификатор, встречающийся впервые, помещается в таблицу символов, его тип определяется по соответствующему описанию.

2)Если встречается идентификатор, который уже помещен в таблицу,

его тип определяется по соответствующей записи в таблице.

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

Всякий раз, когда анализатору встречается идентификатор, он проверяет,

есть ли уже этот идентификатор в таблице, и при его отсутствии в конец таб-

лицы вносится соответствующая запись. Если идентификатора в таблице нет,

то требуется ее полный просмотр; но даже в тех случаях, когда идентифика-

тор находится в таблице, поиск в среднем охватывает ее половину. Такой по-

иск в таблице обычно называют линейным. Естественно, что при больших размерах таблицы этот процесс может оказаться длительным.

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

ку каждый раз при добавлении новой записи уходило бы очень много време-

ни, что существенно снижает достоинства этого метода. Поэтому при созда-

нии таблицы символов и поиска в ней обычно используется метод хеширова-

ния.

Для многих языков программирования в общем случае число возмож-

ных идентификаторов хотя и конечно, но очень велико.

В общем случае для таблицы символов используется массив из боль-

шего числа элементов, чем максимальное число ожидаемых идентификато-

188

ров в программе, и определяется отображение каждого возможного иденти-

фикатора на элемент массива (функция отображения). Это отображение не является, конечно, отображением один к одному, а множество идентифика-

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

лении идентификатора проверяется наличие записи в соответствующем эле-

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

следуется следующий элемент и т.д. до тех пор, пока не обнаружится пустой элемент или запись. Таким образом, таблица символов просматривается ли-

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

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

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

должен с ее начала.

Самая простая функция хеширования подразумевает использование первой буквы каждого идентификатора для его отображения на элемент 26-

элементного массива. Идентификаторы, начинающиеся с A, отображаются на первый элемент массива, начинающиеся с B – на второй и т.д.

· · · · · · · · · · · · · · · · · · · · · · · ·

 

Пример · · · · · · · · · · · · · · · · · · · · · · ·

 

 

 

После встречи с идентификаторами

CAR DOG CAB ASC EGG

таблица примет вид табл. 7.1; позиции идентификаторов зависят от порядка их внесения в таблицу.

189

Таблица 7.1 – Таблица хеширования

Номер

Имя переменной

Тип

 

 

 

1

ASC

2

 

 

3

CAR

4

DOG

5

CAB

6

EGG

7

 

 

 

 

 

· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·

Если идентификатор не может быть внесен в ту позицию, которая зада-

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

дую запись), тем меньше вероятность конфликтов. При наличии в программе только вышеперечисленных идентификаторов и использовании рассмотрен-

ной функции хеширования таблица заполнялась бы неравномерно и имела бы место кластеризация. Функция хеширования, которая зависела бы от по-

следней литеры идентификатора, вероятно, меньше бы способствовала кла-

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

ния – важная задача при построении компиляторов.

Обычно выход из конфликтов осуществляется циклической проверкой одного за другим элементов таблицы до тех пор, пока не находится иденти-

фикатор или пустой элемент. Однако на практике используются и другие ме-

тоды. Обычно вырабатывается некоторое правило, последовательное приме-

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

190

полняемое функцией хеширования, называют первичным хешированием, а

вычисление последующих адресов в таблице – вторичным хешированием,

или перехешированием. Функция перехеширования, рассматриваемая в при-

мере, предполагает просто добавление единицы (циклично) к адресу табли-

цы. Эта функция, как и все функции хеширования, обладает следующим свойством: если n – некий адрес в таблице, а p – число элементов в таблице,

то

n, rehash(n), rehash2(n), …, rehashp–1(n)

все являются адресами и

rehashp(n) = n.

Описанная выше функция перехеширования определяется следующей процедурой:

int procedure rehash(int n) if n<p then n+1 else 1

У этой функции есть тенденция создавать в таблице кластеры в той же мере, в какой вероятность кластеризации возрастает в связи с перехешированием. Весьма желательно, чтобы функция перехеширования могла находить адреса подальше от того, с которого начала. Если элементы таблицы – простые числа (т.е. не имеют других делителей, кроме 1), то вме-

сто 1 функция перехеширования может добавлять к адресу любое положи-

тельное целое число h, такое, что h<p; в результате мы бы имели int procedure rehash(int n)

if n+h<p then n+h else n+hp

Подходящее значение h сводило бы кластеризацию к минимуму. Так,

если h – простое число, функция перехеширования выдаст последовательно все адреса в таблице, прежде чем она повторится.

Есть много других вариантов избежать перехеширования при создании таблиц идентификаторов. Рассмотрим некоторые из них.