А.В. Ахо, М.С. Лам, Р. Сети, Дж. Д. Ульман - Компиляторы - принципы, технологии и инструментарий (1114947), страница 30
Текст из файла (страница 30)
С массивами можно работать с использованием команд следующих двух видов: х [у1=я х=у1х1 Первая из них помещает значение х в ячейку памяти х ~у~, а вторая помещает значение из ячейки памяти у [х~ в переменную х. Трехадресные команды выполняются в числовой последовательности, если только иное не требуется командой условного или безусловного перехода. Для управления потоком управления используются следующие команды. хй8'а1ае х алого Ь Если х ложно, следующей выполняется команда с меткой Ь ххТхце т алого 1, Если х истинно, следующей выполняется команда с меткой 1 Следующей выполняется команда с меткой Ь алого 1 Метка 1.
может быть назначена любой команде при помощи префикса 2,:. Одна команда может иметь несколько меток. Наконец, следующая команда копирует значение д в переменную ас Трансляция инструкций Инструкции транслируются в трехадресный код с применением команд перехода для управления потоком выполнения. Схема на рис. 2.42 иллюстрирует трансляцию 11 (ехрг1 гпеп нтй.
Команда условного псрсхота ххРа1ае х алого ц 1ег 146 Глава 2. Простой синтаксически управляемый транслятор адег — ~. Рис. 2.42. Схема кода для конструкции й' осуществляет "прыжок" через код для вычисления вГтгн если вычисленное значение ехрг ложно. Другие конструкции транслируются аналогично с использованием соответствующих переходов для обхода кода их компонентов. Для конкретности на рис.
2.43 приведен псевдокод класса!Я Класс 11" является подклассом класса йтй как и классы для других конструкций. Каждый подкласс Е~т~ имеет конструктор, в нашем случае — 11, и функцию яеп, которая вызывается для генерации трехадресного кода для данного вида инструкций. с1ааа (1" ехтепця Ят~ ( Ехрг Е; Ятг Я; рцЫ(с Ц(Ехрг и, Бгт! у) ( Е = х; 5 = у; а1)ег = пеи (аЬеЦ; ) риЬИс то(п яепО ( Ехрг и = Е.ли!иеЦ; етй( "ах я а1зе " + плоЕ!ппЯ + " досо " + а1!ег); В.реп()' етй(а()ег + ": "); Рис. 2.43.
Функция яеп класса 1Г генерирует трехвдресиый код Конструктор (1' на рис. 2.43 создает узел синтаксического дерева для инструкции!й Он вызывается с двумя параметрами, узлом выражения х и узлом инструкции у, которые сохраняются как атрибуты Е и Я. Конструктор также выполняет присваивание атрибуту аг)ег новой уникальной метки путем вызова функции пеипаЬе( (). Метка будет использоваться согласно схеме на рис.
2.42. После того, как синтаксическое дерево для исходной программы полностью построено, в его корне вызывается функция деп. Поскольку в нашем простом языке программа представляет собой блок, корень синтаксического дерева представ- 2.8. Генерация промежуточного кода лает последовательность инструкций в блоке. Все классы инструкций содержат функцию яеп. Псевдокод функции сел класса ]!' на рис.
2.43 вполне представителен. Он вызывает Е.гва!ие () для трансляции выражения Е (выраженне со значением типа Ьоо!еап, являющееся частью конструкции!Т) и сохраняет возвращенный Е узел (трансляция выражений будет рассмотрена в следующем подразделе). Затем функция дел генерирует условный переход и вызывает Язеп () для трансляции подынструкции В.
Трансляция выражений Теперь проиллюстрнруем трансляцию выражений, рассматривая выражения, содержащие бинарные операторы ор, обращение к массивам и присваивания в дополнение к константам и идентификаторам. Дпя простоты при рассмотрении обращений к массивам р Ц будет требоваться, чтобы р было идентификаторомзв Детальное обсуждение генерации промежуточного кода для выражений можно найти в разделе 6.4. Мы воспользуемся простым подходом генерации трехадресных команд для каждого узла оператора в синтаксическом дереве выражения.
Для констант и идентификаторов не генерируется никакой код, поскольку они входят в команды в качестве адресов. Если узел х класса Ехрг содержит оператор ор, то генерируется команда для вычисления значения в узле х в генерируемое компилятором "временное" имя, скажем, !. Таким образом, выражение 1-З+]с транслируется в команды с1 = 1 с2=с1+ При обращении к массивам и присваиваниях требуется различать ]-значения н г-значения. Например, выражение 2*а [1] может быть транслировано путем вычисления г-значения а [1] во временную переменную с1 = а с2 = 2 * с1 Однако нельзя просто использовать временную переменную вместо а [1], если а [1] появляется в левой части присваивания. Этот простой подход использует две функции, ]па]ие н гва!ие, представленные соответственно на рис.
2.44 и 2.45. При применении ко внутреннему узлу х функция гва]ие генерирует команды для вычисления х во временную переменную и возвращает новый узел, представляющий эту переменную. При применении ко внутреннему узлу функции ]па]ие она также генерирует команды для вычисления поддеревьев ниже х и возвращает узел, представляющий "адрес" х.. пнвш простой язык поддерживает выражения типа а[а[п] ], ~о ~с а[в] [и]. Заметим, что а [а [и] ] имеет вид а[Е], гдс Е представляет собой а[п]. 148 ! лава 2. Простой синтаксически управляемый транслятор Екрг!га!ие(х: Ехрг) ( Ю ( х является узлом И ) гетпгп х„. е1яе 11( х является узлом Ассегг (у, з), а у — узел И ) ( гегпгп пеи Ассекг (у, гга1ие(г)); е!зе еггог; Рис.
2.44. Псевдокод функции 1ка!ие Рассмотрим сначала функцию 1ка1ие, у которой меньше вариантов действий. При применении к узлу х функция 1ка1ие просто возвращает х, если это узел идентификатора (т.е. если х принадлежит классу И). В рассматриваемом простом языке единственный другой случай, когда выражение имеет 1-значение, — это когда х представляет обращение к массиву, такое как а [з ]. В этом случае х будет иметь вид Асеев (у, з), где класс Ассекг является подклассом Ехрг, у представляет имя массива, к которому выполняется обращение, а в — смещение (индекс) выбранного элемента массива.
В псевдокоде на рис. 2.44 функция 1га!ие вызывает гна1ие (з) для генерации необходимых команд для вычисления г-значения Затем она строит и возвращает новый узел Ассеаг с дочерними узлами для имени массива у и г-значения з. Пример 2.19. Если узел х представляет обращение к массиву а [2*]с], вызов Ма!не(х) генерирует команду 2 * ]с и возвращает новый узел х', представляющий 1-значение а [~], где ~ — новое временное имя.
Говоря более подробно, по достижении фрагмента кода гегпгп петг Ассеьк (у, гка1ие(з)); у представляет собой узел для а, а в — узел для выражения 2*!с. Вызов гна1ие (г) генерирует код для выражения 2*!с (т.е. трехвдресную команду с = 2 * ]с) и возвращает новый узел с', представляющий временное имя Г. Узел г' становится значением второго поля во вновь созданном узле х' типа Ассектс О Функция гка1ие на рис. 2.45 генерирует команды и возвращает новый узел, за исключением случая, когда узел х представляет идентификатор или константу (в этом случае функция гка1ие возвращает сам узел х).
Во всех остальных случаях функция возвращает узел И для новой временной переменной !. Эти случаи перечислены ниже. 149 2,8. Генерация промежуточного кода Ехрг гга1ие(х: Ехрг) [ Ы ( х — узел!й или Сопзгал! ) гетцгп х; е[ве И( х — узел Ор(ор,у,х) или !!е1(ор,у,х) ) ( 1 = новая временная переменная; Генерация строки для ! = гга1ие(у) ор гга!ие(х); гегцгп Новый узел !4 е1яе 11( х узел Ассехх(у,х) ) ( ! = новая временная переменная; Вызов !га!ие(х) возвращающий Ассехх (у, "); Генерация строки для ! = Ассехх (у, х'); гетпгп Новый узел 1; е!яе 11 ( х — узел Ахх1ял (у, х) ) ( х' = гга1ие(х); Генерация строки для !га!ие(у) = х'; гегигп х', Рнс.
2.45. Псевдокод функции гга!ие ° Если узел х представляет у ор х, код функции сначала вычисляет у' = = гга1ие (у) и х' = г а1ие (х). Он создает новую временную переменную 1, генерирует команду ! = у' ор г (точнее, команду, формируемую из строковых представлений 1, у', ор и х') и возвращает узел для идентификатора !. ° Если узел х представляет обращение к массиву у [ ], можно воспользоваться функцией !га!ие. Вызов Ьа1ие(х) возвращает обращение у (х'], где х' представляет идентификатор, хранящий смещение для обращения к массиву. Код создает новую временную переменную 1, генерирует команду для 1 = у (х'] и возвращает узел для !. ° Если узел х представляет присваиванне у = х, то код сначала вычисляет х' = гта1ие (-), затем генерирует команду для !па!не (у) = -' и возвращает узел х'.
Пример 2.20. При применении синтаксического дерева для а[1) = 2*а[2-К) функция гга1ие генерирует 150 Глава 2, Простой синтаксически управляемый транслятор СЗ = 3 - К с2 = а [ с3 с1 = 2 * с2 а[1]=С1 Иначе говоря, корень представляет собой узел Азий с первым аргументом а [1] и вторым аргументом 2*а[Э-к].
Таким образом, оказывается применим третий случай функции гта1ие, и она рекурсивно вычисляет 2*а [3-]с]. Корнем этого поддерева является узел Ор для *, что приводит к созданию новой временной переменной С1 перед тем, как будут вычислены левый операнд 2 и правый операнд. Константа 2 не генерирует трехадресный код, а ее г-значение, возвращаемое узлом Сопзгапб равно 2. Правый операнд а [3-]с] представляет собой узел Ассезз, который приводит к созданию новой временной переменной С2 перед тем, как для этого узла будет вызвана функция Ьи1ие. Для выражения 3-]с рекурсивно вызывается функция гга1ие.
В качестве побочного эффекта этого вызова после создания новой переменной СЗ генерируется трехадресная команда СЗ = 3 — ]с. Затем, после возврата в вызов Ьа]ие для а [З-]с] временной переменной с2 присваивается г-значение всего выражения присваивания.