- •6. Генерация команд в компиляторе 44
- •1. Языки и грамматики
- •1.1. Язык как множество
- •1.2. Порождающая грамматика
- •1.3. Процесс порождения
- •1.4. Классификация грамматик по Хомскому
- •1.5. Классификация языков по Хомскому
- •1.6. Задача распознавания цепочек языка
- •2. Принципы трансляции языков программирования
- •3. Лексический анализ
- •3.1. Конечный автомат
- •3.2. Построение детерминированного конечного автомата
- •3.3. Недетерминированный конечный автомат
- •3.4. Преобразование неоднозначной а-грамматики к однозначной
- •3.5. Удаление из грамматики бесполезных нетерминалов
- •3.6. Лексический анализатор
- •4. Синтаксический анализ
- •4.1. Дерево порождения для кс-грамматики
- •4.2. Автомат с магазинной памятью
- •4.4. Левая рекурсия и ее устранение
- •4.5. Преобразование кс-грамматики к обобщенной нормальной форме Грейбах
- •4.6. Детерминированный ll-разбор
- •5. Обратная польская строка, как промежуточная форма программы
- •5.1. Обратная польская строка для арифметических выражений
- •5.2. Генерация опс для арифметических выражений
- •5.3. Вычисление опс для присваиваний и арифметических выражений с индексами
- •5.4. Генерация опс для присваиваний и арифметических выражений с индексами
- •5.5. Опс для условных, циклических и составных операторов
- •5.6. Опс для стандартных операторов
- •5.7. Распределение памяти и описание переменных
- •5.8. Обработка ошибок
- •6. Генерация команд в компиляторе
- •6.1. Распределение памяти при генерации команд
- •6.2. Генерация команд для присваиваний и арифметических выражений
- •6.3. Генерация команд с индексными выражениями
- •6.4. Генерация команд сравнения и перехода
6. Генерация команд в компиляторе
6.1. Распределение памяти при генерации команд
Генерировать команды можно непосредственно при работе LL-анализатора, задав соответствующие семантические программы. Однако схема работы будет более гибкой, если генерировать команды на основе промежуточной формы транслируемой программы, например, на основе ОПС. При этом перед генерацией команд необходимо выполнить некоторые предварительные действия по распределению памяти для создаваемой программы.
Машинная программа создается в виде файла (исполняемого модуля), структура которого определяется той операционной системой, которая будет запускать программу на выполнение. В этом файле кроме собственно машинных команд содержится также служебная информация, необходимая для корректного выполнения программы, в том числе информация о размере области памяти для программы и используемых ею данных. Как минимум, необходимы области памяти для размещения:
1) машинных команд программы;
2) констант, используемых в машинных командах;
3) статических переменных, размер которых может быть подсчитан при трансляции программы;
4) динамических переменных, размер которых определяется в ходе исполнения программы, так как зависит от входных данных и не может быть определен заранее.
Операционная система запускает исполняемый модуль на выполнение следующим образом: считывает с внешнего носителя данных (диска) файл, содержащий машинные команды (программу) и константы, в оперативную память, выделяет область памяти для статических переменных и передает управление на первую команду программы. В процессе работы программа может выполнять специальные команды – запросы к операционной системе на выделение областей памяти для динамических переменных. Последней командой, которую исполняет программа, должна быть команда возврата управления в операционную систему. При этом операционная система освобождает все занятые программой области памяти. Возврат управления в операционную систему может случиться и преждевременно, из-за ошибочных ситуаций, возникших в ходе исполнения программы.
Таким образом, при генерации команд необходимо рассчитать три области памяти: команд, констант и статических переменных. Память для команд можно рассчитать только после генерации самих команд. Память для констант и статических переменных состоит из двух частей каждая. 1-я часть констант формируется заранее, на основе созданной анализатором таблиц констант, с учетом их типов. 2-я часть, состоящая из дополнительных констант, формируется в процессе генерации команд. 1-я часть памяти статических переменных рассчитывается заранее, на основе сформированной анализатором таблиц переменных, с учетом того, сколько байт требуется для каждой переменной в зависимости от ее типа. 2-я часть, состоящая из дополнительных временных переменных, рассчитывается в процессе генерации команд. Эти три области рассчитываются независимо, при этом в каждой генерируемой команде, содержащей адрес операнда, должен формироваться признак, к какой области памяти относится адрес. После завершения генерации команд все эти три области памяти можно «склеить», пересчитав адреса в командах таким образом, чтобы после команд сразу же располагались константы, а далее – статические переменные.
Распределение памяти для динамических переменных рассмотрим на примере массивов, количество элементов в которых вычисляется в процессе исполнения программы. Будем считать, что нумерация элементов массива по каждому из измерений начинается с нуля. Тогда для каждого массива при выделении ему памяти требуется сформировать паспорт, в котором записывается:
1) адрес начального элемента массива в выделенной для него области памяти,
2) длина (в байтах) одного элемента массива
3) размер массива по 1-му измерению,
4) размер массива по 2-му измерению, и т.д.
Место для паспорта каждого из динамических массивов выделяется в области статических переменных заранее, до генерации команд. Действия по заполнению паспорта производятся при выполнении программы группой команд, реализующих операцию выделения памяти массиву. Эти команды должны рассчитать размер области памяти, сделать запрос на выделение памяти к операционной системе, после чего заполнить паспорт массива.