А.В. Ахо, М.С. Лам, Р. Сети, Дж. Д. Ульман - Компиляторы - принципы, технологии и инструментарий (1114947), страница 235
Текст из файла (страница 235)
Наш простой компилятор проверяет типы, но не вносит операции преобразования типа. 1144 Приложение А. Завершенный пример начальной стадии компилятора 1) расхаде ьпгег! // Файл Татр/ага 2) 1врогТ 1ехег.*! Тврогг вупйю1в.*! 3) риЬ11с с1аяз Тевр енгелов Ехрг ( 4) вгагьс ьпг соипТ = О! 5) ьпг пивЬег О! 6) риЬ11с Тевр(Туре р) ( 7) вирек(иогб.тевр, р)! пивьег = ++сопит! ) 8) риЫ1с ЯТТ1пд Товгг1пч() ( гегигп "Т" + пивЬег! 9) ) Класс ()пату — однооперандный аналог класса Аг1Т)з. 1) раскаче 1пгег! // Файл (/иаеу/аеа 2) 1врогг 1ехег.*! 1врогг зувЬо1в.*! 3) риЬ11с с1авя Ппагу ехгепе)в Ор ( 4) риЬ11с Ехрг ехрг! 5) риЬ11с Плаху(тохеп Ток, Ехрг х) ( // Обрабатывает 6) // унарный минус; обработку 7) //олератора 1 см.
в классе НоТ 8) вирег(ток, пи11)! ехрг = х! 9) Туре = Туре.вах(Туре.тпг, ехрг.Туре)! 10) ТЕ (Туре == пи11 ) еггог("Туре еггог"); 1!) 12) риЫьс Ехрг чеп() ( 13) гетигп пен Ппагу(ор, ехрг.гее(исе())! ) 14) риЫ1с ЯТТ1пд Товггьпд() ( 15) гегигп ор.говгг1пч()+" "+ехрг.Товггзпп()! ) 16) ) А.б Переходы для булевых выражений Код переходов для булеза выражения В генерируется методом 3 шарзпе3, который получает в качестве параметров две метки, Т и Т', именуемые соответственно истинным и ложным выходами В. Код содержит переход к Т, если вычисление В дает значение "истина", и к й, если вычислено значение "ложь".
По соглашению специальная метка О означает, что управление передается следующей команде после кода В. Начнем с класса СопЯТапТ. Конструктор Сопвсапс в строке 4 получает в качестве параметра токен ТО)т и тнп р. Он строит лист синтаксического дерева с меткой ТО)т и типом р. Для удобства конструктор Сопвсапс перегружается (строка 5) для создания константного объекта из целого числа. 1) раскаде ьпгег! // Файл Сова!аа!,/аеа 2) ьврогг 1ехег.*! )врогг зувЬо1з.*! 3) риЬ11с с1аяя СопвТапТ ехгепе(в Ехрг ( 4) риЫьс Сопвгапг(токаи Ток, Туре р) ( вирек(гох, р)! ) 5) риЫьс Сопятапт(ьпт 1) ( вирек(пен Нив(1), Туре.тпг1! 1145 А.6.
Переходы для булевых выражений ринтьс всастс йьпа1 Сопвсапс Тгие = пел Сопвсапе(ноге.тгие, Туре.Воо1), Ра1ве = пен Сопвсапс(ногз(.ра1ве, Туре.Воо1)з риЬ11с чоас( зиертпч(ьпс С, зпГ Т) ТТ ( Гбав == Тгие ВВ Г 1= 0 ) еетг("чего 1," + г)з е1ве ТТ ( гньв == Ра1ве ьа Т 1= О) епзтс("чово 1," + Т)з 6) 7) 8) 9) 10) 1!) 12) !3) !4) ) 15) ) !) расхаче зпгегз // Файл 1оя(са//ага 2) ипрогс 1ехег.*; ппрогс вузпЬо1в.*з 3) риЬ11с с1авв Ьочъса1 ехсепз(в Ехрг ( 4) риЬ11с Ехрг ехрг1, ехрг2з 5) Ьочьса1(Токеп Гох, Ехрг х1, Ехрг х2) ( 6) вирег(со1с, пи11) з // Длл начала нулевой тнп 7) ехрг1 = х1з ехрг2 = х2з 8) Суре = спеск(ехрг1.суре, ехрг2.суре) 3 9) Тй (Гуре = пи11 ) еггог(теуре еггог") з 10) 1!) риЬ11с Туре снесх(туре р1, Туре р2) ( 12) 1г ( р1 == Туре.Воо1 ав р2 == Туре.Воо1 ) 13) гесигп Туре.Воо1з 14) е1ве гегигп пи11з 15) 16) риЬ11с Ехрг чеп() ( 17) Тпг Т = пен1аЬе1()з Тпс а = пен1аЬе1()з 18) телзр гелзр = пел теер(гуре)з 19) ГЬ1в.э~ппр1пч(О,Т)з 20) евзгс(селзр.соэсгтпч(] + " Ггие")3 21) елззс("чосо Ъ" + а); 22) ее1Г1аЬе1(Г)з евз1Г(севзр.совсгтпч(1 + " = Та1ве")з 23) евзгг1аЬе1(а)з 24) гегигп гетр! Метод 3цазрйпд (строки 9 — !4, файл Сотщл(/ага) получает два параметра, метки С и Т.
Если эта константа является статическим объектом Тгце (определенным в строке 7), а С не является специальной меткой О, то генерируется переход к метке С. В противном случае, если это обьекг Га1ве (определенный в строке 8), а й не равно О, генерируется переход к метке Е.
Класс 1.од1.са1 предоставляет некоторую общую функциональность для классов Ог, Апс( и !чог. Поля ехрг1 и ехрг2 (строка 4) соответствуют операндам логического оператора. (Хотя класс )з)ог и реализует унарный оператор, для удобства он сделан подклассом класса 3 од1са1) Конструктор ! одз са1 ( Со)с, а, Ь ) (строки 5-!0) строит синтаксический узел с оператором Со)г и операндами а и Ь. При этом для проверки, что и а, и Ь имеют логический тип, используется функция сЬес)г. Метод деп будет рассмотрен в конце этого раздела. 1146 Приложение А. Завершенный пример начальной стадии компилятора 25) 26) 27) 28) 29) ) 30) ) ) риЫьс Ясгьпд Говпгвпд() ( гегпгп ехрг1.Говпгьпд()+" "+ор.поапгтпд(! "+ехрг2.повсгтпд()З !) расваде Тппег; // Файл Ог/ача 2) 1пзрогс 1ехег.*з ьврогп еупаю1а.*з 3) рпЬ11с с1ава Ог ехпепз(п Ьодьса1 ( 4) рпЬ11с Ог(Тохеп Гох, Ехрг х1, Ехрг х2) 5) впрег(сох, х1, х2)з ) 6) рпЫТс чотз( 3ппзртпд(зпп Г, зпс Т) ( 7) Тпг 1аЬе1 = Г != 0 ? Г : пее1аЬе1()з 8) ехрг1.3пергпд(1аЬе1, 0)з 9) ехрг2.3ппзртпд(Г,Т)з (0) 1г( Г == 0 ) ееТГ1аЬе1(1аЬе1)з (!) ) (2) ) В общем случае Е, истинный выход В, может быть специальной меткой О.
Переменная 1аЬе1 (строка 7, файл Ог/ага) гарантирует корректную установку истинного выхода В! на конец кода В. Если Е равно О, то 1аЬе1 устанавливается равной новой метке, которая выводится после генерации юда для В! и В?. Код класса Апс( подобен коду класса Ог. !) расхаде ьппегз // Файл АпЦаза 2) Тврогп 1ехег.*з ипрогп ауапо1а.*з 3] рпЬ11с с1ааа Хпс( ехпепс(а Ьод1са1 ( 4) рпЫТс Апо(тохеп Гох, Ехрг х1, Ехрг х2) ( 5) апрег(ГОК, х1, х2); ) 6) рпЫТс чогз( 3шпрьпд(апп Г, Ьпс Т) ( 7) 1пп 1аЬе1 = г (= 0 ? Т : пее1аЬе1()з 8) ехрг1.3шпряпд(0, 1аЬе1)з 9) ехрг2.3ппзртпд(г,г)з (о) 1Т( г == 0 ) ев1Г1аЬе1(1аЬе1)з (!) ) )2) ) Класс )чос имеет достаточно много общего с другими логическими операторами, так что мы делаем его подклассом з.од1са1, несмотря на то что (чоЕ В классе Ог метод 3 шпрз.пд (строки 6-11) генерирует код перехода для булева выражения В = В(((В?.
Предположим на минуту, что ни истинный выход Е, ни ложный выход й выражения В не является специальной метюй О. Поскольку В истинно, если истинно В» истинный выход В! должен быть Е, а ложный выход соответствует первой команде В?. Истинный и ложный выходы В? те же, что и для В в целом.
1147 А.6. Переходы для булевых выражений 1) расхаце Тппег; // Файл 5(аг/ача 2) терогп 1ехег."; Тврогс зутпЬо1в.*п 3) рпЫТс с1авз Нос ехсепп(в Ьоц1са1 ( 4) рпЫ1с Нос(токеп Гок, Ехрг х2) ( 5) запрег(гок, х2, х2)п 6) рпЫ1с чо1п( 3птртпц(1пп Г, Тпс Т) ( 7) ехрг2.3шпртпц(т, Г); ) 8) рпЫтс Бсгтпц Говсгтпц() ( 9) геспгп ор.соапгтпц()+" "+ехрг2.Гозпгтпц(); ) 1О) Класс йе1 реализует операторы «,=, ==, ! =, >= и >, Функция с)лес)с (строки 6-11) проверяет, что два операнда имеют один и тот же тип и что они не являются массивами. Для простоты приведение типов не допускается. 1) раскаце 1ппег; // Файл )(е(/ага 2) ппрогс 1ехег.*; 1ерогп в1апЬо1в.*; 3) рпЫ1с с1авв Ве1 ехсепп(в Ьоцтса1 ( 4) рпЬ1гс Ве1(то)пеп сок, Ехрг х1, Ехрг х2) 5) зпрег(пох, х1, х2); ) 6) рпЬ11с Туре пиес)л(туре р1, Туре р2) ( 7) 1Т ( р1 1пзпапсеот Аггау 11 р2 1пзпапсеот Аггау ) 8) гегпгп пи11; 9) е1зе ТТ( р1 == р2 ) геппгп Туре.ноо1; 1О) е1ве гегпгп пп11; 11) 12) риЫтс чоян зиертпц(1пп Г, тпп Т) ( 13) Ехрг а ехрг1.геспсе()п 14) Ехрг Ь = ехрг2.геп(псе()п 15) Япгтпц зевс = а.соБГг1пц() + " " + ор.созпгтпц() 16) + " " + Ь.позпгтпц(); 17) ееяпзитрз(пезп, Г, г)п 18) ) 19) Метод Зшпрз.пд (строки 12 — 18, файл леЦаиа) начинается с генерации кода для подвыражений ехрг1 и ехрг2 1строки 13 и 14).
Затем вызывается метод е(пгЦшпр, определенный в строках 11 — 21 файла Ехрг/ага в разделе А.5. Если ни Е, ни б не является специальной меткой О, метод е(айЕЭцпзрв выполняет следующее: // Файл Елрг/ача 13) 14) еатг("1г " + гевг + " цого 1." + г) 7 еп1Г("цосе 1" + Т) и реализует унарный оператор. Налкласс ожидает два операнда, так что при вызове яцрег в строке 5 ему передаются два х2. В методах в строках 6-9 используется только переменная ехрг2 (объявленная в строке 4 файла Еофса(/ача). В строке 7 метод Зип)ргнф просто вызывает ехрг2. Зцп(ргнф с обращенными истинным и ложным выходами. 1148 Приложение А. Завершенный пример начальной стадии компилятора Если либо Е, либо й является специальной меткой О, то генерируется не более одной команды: е1яе 18( Г 1= 0 ) // Файл Ел/зг/ага евзс("з.в " + Геяс + " чосо 1," + Г); е1яе 18( с 1= 0 ) евгг("1тга1яе " + геяг е " чого ь" + Т)з е1яе ; // Ничего: и Г, и г неуспешны 16) 17) 18) 19) 20) 1) раскаче 1псегз // Файл Ассезз/ага 2) 1врогс 1ехег.*з 1врогс яушЬо1я.*з 3) рпЬ11с с1аяя Ассеяя ехсепбя Ор ( 4) рпЬ11с тб аггауз 5) риЫТс Ехрг Тппехз 6) риЬ11с Ассеяя(тс( а, Ехрг 1, Туре р) 7) // р — тип элемента после выравнивания массива 8) япрег(пен Нпгс(("[1", Тач.
1Н1)ЕХ), р)! 9) аггау = аз 1пс(ех = з.з 10) !1) риЫТс Ехрг чеп() ( 12) гесигп пен Ассеяя(аггау, 1пс(ех.гес(псе(), Гуре); ) 13) рпЫьс чо1с( зпвртпч(ьпГ Г,ьпс Т) ( 14) ев1ГЗпвря(гебчсе() .Гоасгтпч(),Г,Г) з ) !5) риЬ1зс ЯГг1п9 Говсг1пч() ( 16) геспгп аггау.созсгтпд() + " [ " + 17) 1пс(ех.гозгггпя() + 18] ) 19) ) Код с переходами может использоваться и для возврата булева зчачения. Класс т,одхса1, описанный в этом разделе выше, имеет метод реп (строки 1б-25), который возвращает временную переменную сешр, значение которой определяется потоком управления, проходящим по коду с переходами, сгенерированному для этого выражения.
На истинном выходе этого булева выражения переменной Еетр присваивается значение Етое; на ложном выходе — значение Та1ве. Временная переменная объявлена в строке 18. Код переходов для выражения генерируется в строке 19; истинный выход при этом представляет собой следующую команду, Рассмотрим еще одно применение ет1ЕЗ цтрв в коде класса Ассевв. Исходный язык позволяет присваивать булевы значения идентификаторам и элементам массива, так что булево выражение может представлять собой обращение к массиву.
Класс Ассе в в имеет метод деп для генерации "нормального" кода и метод зцшрхпс! для генерации кода переходов. Метод Зцтрхпд (строки 13 и 14) вызывает етзЕЗцпзра после свертки данного обращения к массиву во временную переменную. Конструктор (строки б-10) вызывается с передачей выровненного массива а, индекса 1 и типа р элемента в выровненном массиве. Проверка типа выполняется в процессе вычисления адреса массива. 1149 А.7.