А.В. Ахо, М.С. Лам, Р. Сети, Дж. Д. Ульман - Компиляторы - принципы, технологии и инструментарий (1114947), страница 26
Текст из файла (страница 26)
2.35 */ Рис. 2.34. Код лексического анализатора (часть 1) Класс Вехе г, предназначенный для лексического анализа, показан на рис. 2.34 и 2.35. Целочисленная переменная 11пе в строке 4 подсчитывает количество входных строк, а переменная рее)с в строке 5 хранит очередной входной символ. Зарезервированные слова обрабатываются в строках 6 — 11.
Таблица могбз обьявлена в строке 6. Вспомогательная функция гевегче в строке 7 помещает пару "строка — слово" в таблицу. Конструктор Ьехег в строках 9 и 10 инициализирует таблицу. Он использует конструктор (яогс( для создания объектов слов, которые передаются вспомогательной функции гезегче. Таким образом, таблица оказывается инициализированной зарезервированными словами "сгце" и "Та1зе" до первого вызова функции ясап. Код функции ясап на рис.
2.34 и 2.35 реализует фрагменты псевдокодов, приведенные ранее в этом разделе. Цикл (ог в строках 13 и 14 пропускает пробелы, 127 2.6. Лексический анализ символы табуляции и новой строки. Когда в переменной рее)с оказывается не пробельный символ, управление покидает цикл.
Код для чтения последовательностей символов располагается в строках! 8 — 25. Функция ТзРТдфс -- из встроенного класса 1ака СЬагасгег. Она используется в строке 18 для того, чтобы проверить, не содержит ли переменная рее)с цифру. Если содержит, код в строках 19 — 24 накапливает целочисленное значение из последовательности цифр во входном потоке и возвращает новый объект !чиль Строки 26-38 анализируют зарезервированные слова и идентификаторы. Ключевые слова !гце и Га!ае зарезервированы в строках 9 и 10. Таким образом, строка 35 достигается, только если строковая переменная з не представляет собой зарезервированное слово, так что она должна быть лексемой идентификатора. 18) !9) 20) 21) 22) 23) 24) 25) 26) 27) 28) 29) 30) 31) 32) 33) 34) 35) 36) 37) 38) 39) 40) 41) 42) 43) ) 1Т( СЬагассег.1зРТдфг(рее)с) ) ( Тпс о = 0; с(о зг = 10*зг + С)тагассег.с(1дфс(рее)с, 10); рее)с = (сЬаг)ЯузГеаз.фп.геас(()! ) иЬ11е( СЬагассег.фзР1д1с(рее)с) ); гесигп пеи 1чпаз(ч)! ТТ( СЬагасгег.1зРессег(рее)с) ) ( ЯсгТпдВцТТег Ь = пеи ЯГг1пдВцйТег()! с1о ( Ь.аррепс((рее)с)! рее)с = (сЬаг)Яузсеаз.фп.геас((); иМ1е( СЬагассег.1зРеггегРгРТдТГ(рее)с) ); Яггфпд з = Ь.соЯсгфпд()! (Яогс( и = (!Яогс()иогс(з.дес(з)! ТТ( и != пи11 ) геспгп и; и = пеи Иогс((Тад.1Р, з); иогс(з.рпг(з, и); гегцгп и; ) То)сеп С = пеи То)сеп(рее)с)! рее)с = гегцгп г! Рис.
2.35. Кол лексического анализатора (часть 2) 12З Глава 2. Простой синтаксически управляемый транслятор Строка 35 возвращает новый объект типа !яогс1, поле 1ехеве которого устанавливается равным в, а ~ад — равным Тад .? В. Наконец, строки 39-41 возвращают текущий символ как токен и сохраняют в переменной реей символ пробела, который будет пропущен при следующем вызове функции всап. 2.6.6 Упражнения к разделу 2.6 Упражнение 2.6.1. Расширьте лексический анализатор из раздела 2.6.5 так, чтобы он мог удалять комментарии, определенные следующим образом. а) Комментарий начинается с символов // и включает все символы до конца данной строки. б) Комментарий начинается с последовательности символов /* и включает все символы до последовательности */.
Упражнение 2.6.2. Расширьте лексический анализатор из раздела 2.6.5 так, чтобы он мог распознавать операторы отношений «,=, ==,! =, )=, ). Упражнение 2.6.3. Расшнрьте лексический анализатор нз раздела 2.6.5 так, чтобы он мог распознавать числа с плавающей точкой, такие как 2., 3 . 14 н . 5. 2.7 Таблицы символов Таблицы символов представляют собой структуры данных, которые используются компилятором для хранения информации о конструкциях исходной программы.
Информация накапливается инкрементно в фазе анализа компилятора и используется фазой синтеза для генерации целевого кода. Записи в таблице символов содержат информацию об идентификаторах„такую как их символьные строки (или лексемы), тип, местоположение в памяти и прочую связанную с ними информацию. Обычно таблицы символов должны поддерживать множественные объявления одного и того же идентификатора в программе. Из раздела 1.6.1 мы знаем, что область видимости объявления представляет собой часть программы, в которой может применяться данное объявление. Можно реализовать области видимости путем настройки отдельной таблицы символов для каждой области видимости. Программный блок с объявлениями'о будет иметь собственную таблицу символов с записями для каждого объявления в блоке.
Такой подход работает и для других конструкций с областями видимости; например, класс может иметь собственную таблицу с записями для каждого поля и метода. В этом разделе содержится модуль для работы с таблицей символов, подходящий для применения в фрагментах транслятора на языке программирования )ача ~сВ С, например, программные блоки являются либо функциями, либо отдсдсннымн фигурными скобками разделами функций, содсржящнмн одно нлн несколько обьяядсннй.
129 2.7. Таблицы символов из этой главы. Этот модуль будет использован в приложении А, когда фрагменты транслятора будут объединены в одно целое. Пока же для простоты основной пример в данном разделе будет представлять собой урезанный язык только с теми ключевыми конструкциями, которые имеют отношение к таблицам символов, а именно — с блоками, обьявлениями и идентификаторами. Все прочие инструкции и выражения опущены, так что мы можем сконцентрироваться исключительно на операциях с таблицей символов. Программа состоит из блоков с необязательными объявлениями и "инструкциями*', представляющих собой отдельные идентификаторы.
Каждая такая инструкция представляет использование идентификатора. Вот пример программы на таком языке; ( 1пГ х; спаг у; ( Ьоо1 у; х; у; ) х; уз ) (2.7) Примеры блочной структуры в разделе ).б.З работают с определениями и использованиями имен; входная строка (2.7) состоит исключительно из определений и использований имен. Задача, которую мы будем решать, заключается в выводе видоизмененной программы, из которой удалены объявления, и каждая "инструкция" содержит идентификатор, за которым следуют двоеточие и его тип. Пример 2.14. Приведенная выше входная строка (2.7) должна привести к выводу ( ( х:1пГ; у:Ьоо1; ) х:1пГ; у:сЬаг; ) Первые х и у — из внутреннего блока входной строки (2.7).
Поскольку данное использование х обращается к объявлению х во внешнем блоке, за ним следует тип 1пс из этого объявления. Использование у во внутреннем блоке соответствует объявлению у в этом же блоке, так что его тип — Ьоо1. Далее следуют использования х и у во внешнем блоке с типами, взятыми из объявлений во внешнем блоке, т.е. 1пг и сЬаг соответственно. а 2.7.1 Таблица символов для области видимости Термин "область видимости идентификатора г" фактически означает область видимости конкретного обьявления х.
Термин область видимости сам по себе означает часть программы, которая является областью видимости одного или нескольких объявлений. Области видимости весьма важны, поскольку один и тот же идентификатор может быть объявлен в разных частях программы для разных целей. Распространенные имена наподобие 1 или х очень часто многократно используются в одной и той же программе. В качестве другого примера можно привести перекрытие метода суперкласса в подклассе. юзо Глава 2.
Простой синтаксически управляемый транслятор Создание записей таблицы символов Записи таблицы символов создаются и используются в процессе фазы анализа лексическим, синтаксическим и семантическим анализаторами. В этой главе записи в таблице символов создает синтаксический анализатор. В связи с его знаниями о синтаксической структуре программы синтаксический анализатор зачастую куда лучше отличает различные объявления идентификаторов, чем лексический анализатор.
В некоторых случаях лексический анализатор способен, встречая образующие лексемы последовательности символов, создавать записи в таблице символов. Однако гораздо чаще лексический анализатор может только вернуть синтаксическому анализатору токен, скажем, И, вместе с указателем на лексему. Однако решить, следует ли использовать ранее созданную зались в таблице символов или для данного идентификатора следует создать новую запись, может только синтаксический анализатор. Если блоки могут быть вложенными, то несколько объявлений одного и того же идентификатора могут появляться в пределах одного и того же блока.
Приведенный далее синтаксис дает вложенные блоки, если чти может генерировать блок: Ыос!г — ! гас!з зппв ')' (Фигурные скобки заключены в кавычки для того, чтобы отличать их от фигурных скобок семантических действий.) В грамматике, приведенной на рис. 2.38, дес!я генерирует необязательную последовательность обьявлений, а зпли генерирует необязательную последовательность инструкций. Кроме того, инструкция может быть блоком, так что наш язык допускает наличие вложенных блоков, в которых возможно переобъявление идентификаторов. Правило последнего вложения (шояг-с!ояе!у пез1ес$) для блоков заключается в том, что идентификатор х находится в области видимости последнего по вложенности объявления х„т.е.