Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Компиляторы.doc
Скачиваний:
120
Добавлен:
04.11.2018
Размер:
5.13 Mб
Скачать

6. Введение в семантику

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

6.1. Внутренние формы исходной программы

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

Конечно, следует помнить, что каждое частное внутреннее представление зависит от исходного языка и от назначения транслятора. Например, в языке Паскаль нет необходимости включать во внутреннюю форму исходной программы оператор описания переменных VAR, так как вся информация, содержащаяся в нем, попадает в таблицу идентификаторов и никакие команды генерироваться не будут. Следует также решить насколько подробным должно быть начальное внутреннее представление. Включать ли в него, например, операции преобразования значений из одного типа в другой или это делать позже? Представлять ли цикл эквивалентной группой присваиваний, сравнений, условных и безусловных переходов, или его можно задать с меньшей степенью детализации и транслировать уже на фазе генерации кода? Вообще говоря, первоначальная форма программы лаконичнее и короче, но более полное представление открывает новые возможности для оптимизации и существенно облегчает последующие фазы трансляции.

Все внутренние представления программы обычно содержат элементы двух типов: операторы и операнды. Различия представлений состоят лишь в том, как эти элементы объединяются между собой. В дальнейшем мы будем использовать такие традиционные операторы, как +, , , MOD, DIV, , AND, OR, >, <, = и т. п., а также БП (Безусловный Переход) и УПЛ (Условный Переход по Лжи), точнее условный переход в том случае, когда значение операнда (логического выражения) – ложь (FALSE, 0). Внутри компилятора, конечно же, все они представляются соответствующими лексемами или целочисленными кодами.

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

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