А.В. Ахо, М.С. Лам, Р. Сети, Дж. Д. Ульман - Компиляторы - принципы, технологии и инструментарий (1114947), страница 234
Текст из файла (страница 234)
2.37. В то время как класс Еехег отображает строки на слова, класс Епч отображает токены на объекты класса 1с(, определенного в пакете ТпГег вместе с классами для выражений и инструкций. 1) распаде з)апЬо1з; // Файл Елч/ача 2) 1прогг ззча.иг11.*; ътрогг 1ехег.*; ьпрогг ъпгег.*; 3) риЫъс с1ззз Епч ( 4) ргъчасе НззпсзЬ1е ГаЬ1еп 5) ргопеспеп( Епч ргеч; б) риЬ11с Епч(Епч и) ( ГаЬ1е = печ НазппаЬ1е()п ргеч - и! ! 7) риЫьс чоМ рип(тохеп ч, 1п( 1! ( ГаЫе.рип(ы, 1) и ) 8) риЬ11с 1й дег(то)геп ы! 9) аког( Епч е = Гпъз; е != пи11; е = е.ргеч ) ( 1О) тг( гоипо = (то)(е.паЫе.деп(ы!)и 11) 18( Тоипо != пи11 ) гесигп Топил(! 12) 13) гегигп пи11; 14) ! 15) ! Класс Туре определен как подкласс (п)огс(, поскольку имена фундаментальных типов наподобие хпс являются зарезервированными словами, лексемы которых отображаются лексическим анализатором на соответствующие объекты. Объектами для фундаментальных типов являются Туре .
1пк, Туре . Р1оаГ, Туре . С))аг и Туре. Воо1 (строки 8 — 11). У всех них наследованное поле гад устанавливается равным Тад. ВАБ1С, так что синтаксический анализатор обрабатывает их одинаково. // Файл Туре/ага 1) рзскзде зутЬо1зп 2) 1прогг 1ехег.*; 3) риЬ11с с1ззз Туре ехсепп(з Иогп( ( 4) риЫ1с 1пп МЫТЬ = 0; // Используется при выделении памяти 5) риЬ)тс Туре(впг1пд з, 1пс Гзд, ьпг ы! б) ( зирег(з, Гад); ч1ЖЬ = кч ) 7) риЫ1с зпзпъс Гапа1 Туре 8) тпс = пеы туре( "Тпс", тзд.ВХВТС, 4 1, 1140 9) >О) (!) Функции пцшегйс (строки 12-1б) и шах (строки 17-25) используются прн преобразовании типов. риЫьс веаГ1с Ьоо1еап пиеегьс(Туре р) 18 (р == Туре.СЬаг >! р == Туре.тпе р == Туре.Г1оас> гееигп Ггие! е1яе гееигп Та1яе! > риЫьс веаеьс Туре пах(туре р1, Туре р2 ) ( ТГ ( ! пиеегьс(р1) >! ! пиеег1с(р2> ) гееигп пи11! е1яе 18 ( р1 == Туре.у1оас >! р2 == Туре.Р1оае ) гегигп Туре.Р1оаг! е1яе ТТ ( р1 == Туре.тпе >! р2 == Туре.1пс ) гееигп Туре.тпс! е1яе гесигп Туре.СЬаг; Преобразования разрешены между "числовыми" типами Туре .
С]заг, Туре. 1пг и Туре. Е1оаг. Г1ри применении арифметического оператора к двум числовым типам результат имеет "максимальный" из этих двух типов. Массивы — единственный конструируемый тип в исходном языке программирования. Вызов функции вцрег в строке 7 устанавливает поле МЕЖ]з, которое очень важно при вычислении адресов. Он также устанавливает 1ехеп!е и по]с равными не используемым значениям по умолчанию. !) расхаде вупЬо1в! // Файл Аггау/аии 2) 1ерогг 1ехег.*! 3) риЫТс с1авя Аггау ехеепоз Туре 4) риЬ11с Туре оу! 5) риЫ1с 1пе втяе = 1! 6) риЬ11с Аггау(1пе вг, Туре р) ( 7) вирег("[]", Тач.типкх, вг*р.еьСГЬ)! 8) заве = яв; оя = р; 9) ) >О) риЫьс Бгг1пд ГоЯГггпд() ( гесигп "[" + ваге + (!) + о!.Ьозсгьпд(»; >2) // Массив типа *от* // Количество элементов А.5 Промежуточный код выражений Пакет Тпсег содержит иерархию классов Моде. Этот класс имеет два подкласса: Ехрг для узлов, представляющих выражения, и Ясп!с для узлов, представляющих инструкции.
В этом разделе мы рассмотрим класс Ехрг и его подклассы. (2) >3) >4) >5) (6) (7) >8) >9) 20) 2!) 22) 23) 24) ) 25) ) Приложение А. Завершенный пример начальной стадии компилятора Г1оаи = пее Туре( УТ1оае", Тад.ВА81С, 8 ), СЬаг = пее Туре( "сиаг", Тая.ВАВТС, 1 ), Воо1 = пеи Туре( "Ьоо1", Тад.ВА81С, 1 )! 1141 А.5. Промежуточный код выражений Некоторые из методов Ехрг предназначены для работы с условными выражениями и безусловными переходами; они будут рассмотрены в разделе А.б вместе с остальными подклассами Ехрг.
Узлы в синтаксическом дереве реализованы как объекты класса з)обе. Для сообщения об ошибках в поле 1ех21пе (строка 4 в файле А/оЫе/пил) сохраняется номер строки в исходном тексте, соответствующий данной конструкции. Строки 8-13 предназначены для вывода трехадресного кода. // Файл Моаа/апа !) расхаде Тппег; 2) 1зрогг 1ехег.*; 3) рпЬ11с с1авв Нобе ( 4) Тпс 1ех11пе = О; 5) кобе() ( 1ех11пе = Ьехег.11пе; ) 6) оозб еггог(всг1пч з) ( 7) ГЬгоя пея Еггог("пеаг 1гпе 5+1ех11пе+": "+в); ) 8) зпасьс Тпе 1аЬе1в = О; 9) рпЬ11с Тпг пея1аЬе1() ( гегпгп ++1аЬе1в; ) (0) рпЬ11с поьб ееТГ1аЬе1(гпс 1) ( )1) Вувсез.опс.ргьпе("Ь" + 1 + ":"): ) (2) рпЬ11с оо1б езТГ(ЯГг1пд в) ( 13) Бувпея.опп.рг1пГ1п("1Г" + з): ) !4) ) Конструкции выражений реализованы подклассами Ехрг.
Класс Ехрг имеет поля ор и Гуре (строки 4 и 5 файла Ехрг/ага), которые представляют в узле соответственно оператор и тип. () расхаде Тпсег; // Файл Екрг/апа 2) 1зрогс 1ехег.*; 1зрогп зуяЬо1в.*; 3) рпЬ11с с1азз Ехрг ехсепбз кобе ( 4) рантье Тохеп ор; 5) рпЬ11с Туре Гуре; 6) Ехрг(тохеп сох, Туре р) ( ор = сох; Гуре = р; ) Метод реп (строка 7) возвращает "член", который может находиться в правой части трехадресной команды.
Для выражения Е = Е(+ Е2 метод реп возвращает член х(+хз, где г( и х2 — адреса значений Е( и Ез соответственно. Возвращаемое значение Е)таз корректно, если этот объект является адресом; подклассы Ехрг обычно заново реализуют метод реп. Метод гестасе (строка 8) вычисляет, или "сворачивает", выражение в единственный адрес; т.е. он возвращает константу, идентификатор или временное имя. Для данного выражения Е метод гебпсе возвращает временную переменную 1, хранящую значение Е.
Здесь также возвращаемое значение Е)зхя корректно, если этот объект является адресом. Рассмотрение методов 3 п(арзнев и еп(1с3 шаря (строки 9 — 21) мы отложим до раздела А.б. Они предназначены для генерации кода логических выражений. 1142 Приложение А. Завершенный пример начальной стадии компилятора 7) 8) 9) (о) (!) ]2) (3) ]4) (5) (6) (7) (8) (9) 20) 2!) ] 22) риЫьс Япгьпд ГоЯГгьпд() ( гепигп ор.повпгтпд(); ] 23) ] риЫьс Ехрг деп() ( гепигп ГЫяз риЬ11с Ехрг гес(исе(] ( гепигп ГЬТвз ) риЫьс чо1с( зиарьпд(ьпс Г, ьпс Т] ( еатсзиарв(совегтпд(], Г, Т) з ) риЫьс чоьг] еаьпзиарв(ЯГгьпд Гевп, 1пе Г, ьпп Т] ( 1Т( г != 0 ьа Т (= О ) ( еаз.е("ТТ " + Гевг + " дого 1." + Г)З еаьп("допо Ь" + й)з е1ве 1Т( Г з= 0 ) еа1Г("11 " + Геяп + " депо !" + Г); е1ве 18( Т (= 0 ) еаас("1тта1яе " + севе + " допо 1" + Т)з е1ве з // погЫпд яьпсе Ьогн г апс( Т Та11 глгоидЬ !) рас]саде 1ппегз // Файл ИУача 2) 1арогг 1ехег.*з ьарогг яуаЬо1я.*з 3) риЬ11с с1аяя тс( ехсепс(в Ехрг ( 4) риЫ1с ьпг оттвегз // Относительный адрес 5) риЬ11с тс((нпгс( 1с(, Туре р, ьпп Ь) 6) верех(1с(, р)з оятяеп = Ьз ) 7) Узел для идентификатора класса 1с( является листом.
Вызов вцрег (1с(, р) (строка б файла И/ача) сохраняет зс( и р в наследованных полях Ор и суре соответственно. Поле ОГТяес 1строка 4) хранит относительный адрес идентификатора. Класс Ор реализует метод гес(цсе тстроки 5-10 файла О/з/ача), который наследуется подклассами АгйГ]з для арифметических операций, Ппагу — для унарных и Ассеяя — для обращения к массивам.
В каждом случае ге()цсе вызывает деп для генерации члена, выводит команду присваивания члена новой временной переменной и возвращает зту временную переменную. !) расхаде Тппегз // Файл О/з/ача 2) Тарогп 1ехег.*з ьарогс вуаЬо1в.*з 3) риЬ11с с1авв Ор ехеепс(в Ехрг ( 4) риЬ11с Ор(Тохеп Гох, Туре р] ( яирег(пох, р)з 5) риЫТс Ехрг гес(исе(] ( 6) Ехрг х = деп()з 7) Теар Г - пее Теар(суре)з 8) еаз.г( г.гояпгз.пд() + " = " + х.гояггзпд() )з 9) гегигп гз (0) ] ]!) Класс 1с( наследует реализацию по умолчанию методов деп и гес]цсе из класса Ехрг, поскольку идентификатор представляет собой адрес.
1143 А.5. Промежуточный код выражений 1) рас)саде гпгегг // Файл Аг(гйуага 2) 1шроге 1ехег.*г гшрогГ вушЬо1в.*г 3) рцЬ11с с1авв Агаси ехсепг(в Ор ( 4) рпЬ11с Ехрг ехрг1, ехрг2г 5) рпЬ11с Аг1ГЬ(тойеп Гой, Ехрг х1, Ехрг х2] ( 6) вирек(Го)г, пп11)з ехрг1 - х1г ехрг2 - х2г 7) Гуре = Туре.шах(ехрг1.Гуре, ехрг2.Гуре)г 8) 1Й (Гуре == пп11 ) еггог(5гуре еггог")г 9) ) 10) рпЬ11с Ехрг деп() ( 11) геецгп пеи Агаси(ор, ехрг1.гес(псе(), 12) ехрг2.гег(псе())г 13) 14) 15) 16) 17) ) 18) ) ) рпЬ1зс Ясгтпд Говтгзпд() гегпгп ехрг1.гозггтпдО+" "+ор.гозггзпд()+ юьехрг2.Гоясг1пдО; Метод сгеп конструирует правую часть трехадресной команды путем свертки подвыражений в адреса н применения к ним оператора (строки 11 и 12 файла Аг(й./аиз).
Предположим, например, что деп вызывается в корне для а+Ь*с. Вызовы гелосе вернут а как адрес подвыражения а и с как адрес Ъ*с. Одновременно гес(цое генерирует команду с=Ь*с. Метод реп возвращает новый узел АгзгЬ с оператором * и адресами а и Г в качестве операндов.' Стоит заметить, что временные переменные типизированы, как и все выражения. Следовательно, конструктор Тешр вызывается с типом в качестве параметра (строка б, файл Тетруапа).2 'Для сообщения об ошибке поле 1ех11пе класса Нос(е хранит номер текущей лексической строки при построении узла. Отслеживание номеров строк при построении узлов в процессе генерации промежуточного кода остается читателю в качестве упражнения. Другой подход заключается в передаче в качестве параметра конструктора узла выражения, чтобы конструктор мог скопировать тип и лексическое положение узла выражения.
Класс АгТГЬ реализует бинарные операторы наподобие + и *. Конструктор АгггЬ начинается с вызова япрег(го)с, пп).1) (строка б), где со)с — токен, представляющий оператор, а пп11 — заполняющее значение для типа. Тип определяется в строке 7 путем использования метода Туре . гпах, который определяет, могут ли два операнда быть приведены к одному общему числовому типу; код Туре. гпах приведен в разделе А.4. Если приведение возможно, суре устанавливается равным результирующему типу; в противном случае выводится сообщение об ошибке типа (строка 8).