Т. Пратт, М. Зелковиц - Языки программирования - разработка и реализация (4-е издание_ 2002) (1160801), страница 31
Текст из файла (страница 31)
Этапы трансляции 107 зе более высокого уровня. Лексический анализатор должен идентифицироваты ип каждой лексемы (число, ограничитель, идентификатор, оператор и т. д.) и номе- тить ее как принадлежащую соответствующему типу. Кроме того, часто делается перевод во внутреннее ма<аинное представление для таких элементов, как чис.ла (они преобразуются во внутреннюю двоичную форму записи с фиксированной или плавающей точкой) и идентификаторы (они заносятся в таблицу символов, и затем вместо строки символов используется адрес из этой таблицы).
Формальной моделью, используемой для создания лексического аналш<атора, являются конечные автомиты. Их краткое описание вы найдете в разделе 3.3.2. Хотя концепция лексического анализа достаточ но проста, фактически этот этап трансляции занимает больше времени, чем любой другой. Частично зто происходит просто потому, что транслятор вынужден скан ироват ь и анализировать исходную программу символ за символом.
Имеет значение н то, что на практике иногда бывает трудно определить границы между лексемами, не прибегая к помощи достаточно сложных контекстно-зависимых алгоритмов. Например, два оператора ГОРСТКА)<1 зоы <=15 00 1О 1 = 1.5 состоят из совершенно различных синтаксических структур. Первый оператор— это оператор цикла 00, а второй — оператор присваивания значения переменной 00101, Но разница между ними станет понятна транслятору только после того, каков дойдет до символа между цифрами 1 и 5, то есть до зацятой в первом случае и точки — во втором, так как в языке ГОВТКА)<1 пробелы игнорируются. Синтаксический анализ, или разбор.
Вторым этапом трансляции является синтпксический иналяз, или разбор. На этом этапе лексем ы, нилуче< иые в резульз ате лексического анализа, используются для идентификации более крупных нрограм иных структур: операторов, обьявлений, выражений и т, и. Синтаксический анализ обычно чередуется с семантическим.
Сначала синтаксический анализатор идентифицирует последовательность лексем, формирук<щих синтаксическую единицу — выражение, оператор, вызов иоднрограь<ли < илн обьявление. Затсм вызывается семантический анализатор для обработки этой синтаксической единицы. Обычно для связи синтаксического и семантического анализаторов используется стек. Сшп аксичсский анализатор заносит в стек различные элементы обнаруженной синтаксической единицы, а затем они извлека<отея из стека семантическим анализатором и обрабатываются. Основная зада п, стоящая перед разработчиками языков в этой области, заключается в том, чт<еы найти эффективные методы синтаксического анализа, в частности, использующие теорию формальных грамматик (см. раздел 3.3.1).
Семантический анализ. Семантический анализ является, возможно, самым важным этапом трансляции. На этом этане обрабатываются структуры, которые были идентифицированы синтаксическим анализатором, и на <инает формироваться структура выполняемого объектного кода. Таким образом, сеь<аит«ческий анализ является мостом, соединяющим две часли трансляции — анализ и синтез. На этом этапе <кущсствляется также ряд других важных вспомогательных функций, в том числе поддержка таблицы символов, обнаружение большинства ошибок, заме- 108 Глава 3. Вопросы трансляции языка на макросов их определениями и выполнение операторов времени компиляции, В случае простых трансляций семантический анализатор может создать готовый к выполнению объектный код, но чаше результатом работы семантического анализатора становится некая внутренняя форма окончательной выполняемой программы, кспорая оптимизируется транслятором, прежде чем будет сгенерировап настоящий выполняемый код.
Семантический анализатор обычно разделен на ряд более мелких анализаторов, каждый из которых обрабатывает какую-либо определенную программную конструкцию. Эти анализаторы взаимодействуют между собой при помощи информации, хранящейся в различных структурах данных, в частное~и в центральной таблице символов. Например, семантический анализатор, обрабатывающий объявления простых переменных, занимается в основном тем, что п(юсто заносит объявленные типы в таблицу символов. Затем семантический анализатор, обрабатывающий арифметические выражения, может использовать описанные типы для генерации соответствующих операций, присупгих данному типу, в объектом коде.
Конкретные функции семантических анализаторов значительно варьируются в зависимости от языка и логической организации транслятора. Некоторые наиболее общие функции можно описать слсдукццим образом. 1. Подг)ержкп таблицы сияволоо. Таблица символов — это одна из центральных структур данных для каждого транслятора. Таблица символов состоит из элементов, каждый из которых соответствует какому-либо идентификатору, име«лце муся в программе, при этом в таблице отражены все такие идентификаторы.
Лексический анализатор создает начальные элементы таблицы по мере того, как он сканирует исходную программу. Но элементы таблицы символов — это не просто идентификаторы; каждый элемент содержит также дополнительную информацию об атрибутах этого идентификатора: его тип (простая переменная, имя массива, имя подпрограммы, формальный параметр и т, д.), тип значения (целочисленный, вещественный и т. д.), та или иная среда ссылок — и вообще всю информацию, которую можно извлечь из объявления и применения этого идентификатора в исходной программе. Семантический анализатор заносит эту информацию в таблицу символов по мере обработки объявлений, заголовков подпрограмм и операторов программы.
Другие части транслятора используют эту информацию для создания эффективного машинного кода. Таблица символов в трансляторах компилируемых языков обычно уничтожается после окончания трансляции. Но она может сохраняться и во время выполнения программы (например, в языках, которые допускают введение новых идентификаторов во время выполнения или отладки). Во всех реализациях языков М(., Рго1ой и 1.1 зР таблица символов создается во время трансляции и затем используется во время выполнения программы как центральная структура данных, определенная системой. В П~(Х имеется весьма популярная программа дЬ«, которая использует таблицу символов для отладки программ на языке С. 2. Включение неявной информации.
Часто бывает так, что информация, неявно содержащаяся в исходной программе, должна стать явной в объектной про- 3,2. Этапы трансляции 109 грамме низкого уровня. Большая часть такой неявной информации относится к так называемым соалашениям по умолчанию, которые представляют собой определенные интерпретации, вступающие в силу в том случае, когда программист не задает явные спецификации Например, в г ОКТКАХ, если тип переменной, используемой в программе, не определен в объявлении, то по умолчанию этой переменной приписывается некоторый тип в зависимости от начальнога символа в ее имени, 3. Обнаружение ошибок.
И синтаксический, и семантический анализаторы должны быть готовы к тому, что нм придется взаимодействовать ие только с корректными программами, но и с такими, в которых встречаются ошибки. В любой момент лексический анализа~ар может послать синтаксическому анализатору какую-нибудь лексему, которая не вписывается в окружающий контекст (например, разделитель операторов в середине выражения, объявление среди последовательности оператора, символ операции на том месте, где ожидалось появление идентификатора).
Ошибка мажет быть менее заметнои: например, вещественная переменная вместо требуемой целой переменной или индексированная переменная с тремя индексами вместо элемента двухмерного массива, Множества подобных ошибок может быть обнаружено на каждом этапе трансляции. Семантический анализатор должен не только распознавать встречающиеся подобные ошибки и выдавать соответствующие сообщения о них, но также должен во всех таких случаях (кроме самых серьезных) определять, каким образом можно продолжить синтаксический анализ оставшейся части программы.
4. Макрообработка и операции, выполняемые во время компиляции. Эти функции предусмотрены нс во всех языках, но если они присутствуют, то обычно соответствующая обработка проводится семантическим анализатором. Микрос в простейшей форме — это часп текста программы, которая определена отдельно и должна быть вставлена в программу во время трансляции, когда в программе встречается соответствующий вызов. Таким образом, макрос напоминает подпрограмму, с тем лишь отличием, чта подпрограмма транслируется отдельно и вызывается во время выполнения (то есть связывание имени подпрограммы с ес семантикой происходит во время выполнения), а в случае с макросом вместо каждого его вызова во время трансляции просто подставляется его тело (то есть связывание происходит во время трансляции), Макросы могут являться простымн с~роками, подставляемыми в месте их вызова (например, в случае вызова макроса Р! всегда полставляется значение 3.1416).
Но чаще макросы похожи на подпрограммы с параметрами, которые требуется обработать, прежде чем произойдет подстановка в месте вызова макроса, В тех языках, в которых допускаются макросы, семантический анализатор должен идентифицировать вызовы макросов в исходной программе и осуществить соответствующую подстановку тела макроса. В такам случае часта приходится прерывать работу лексического и синтаксического анализаторов и переключать нх на анализ строки, представляющей тело макроса, и лишь после этого продолжать анализ оставшейся части исходной программы.