КАТЕГОРИИ:
АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника
|
Виды семантических действий и способы их реализации в компиляторах.Для проверки семантической правильности исходной программы необходимо иметь всю информацию о найденных лексических единицах языка. Эта информация помещается в таблицу лексем на основе конструкций, найденных синтаксическим распознавателем. Примерами таких конструкций являются блоки описания констант и идентификаторов (если они предусмотрены семантикой языка) или операторы, где тот или иной идентификатор встречается впервые (если семантика языка предусматривает описание идентификатора по факту его первого использования). Поэтому семантический анализ входной программы может быть произведен только после завершения ее синтаксического анализа. Таким образом, входными данными для семантического анализа служат: · таблица идентификаторов; · результаты разбора синтаксических конструкций входного языка. Результаты выполнения синтаксического анализа могут быть представлены в одной из форм внутреннего представления программы в компиляторе. Как правило, на этапе семантического анализа используются различные варианты синтаксических деревьев, построенных в результате синтаксического разбора, поскольку семантический анализатор интересует прежде всего структура исходной программы. Семантический анализ обычно выполняется на двух этапах компиляции: на этапе синтаксического разбора и в начале этапа подготовки к генерации кода. В первом случае всякий раз по завершении анализа определенной синтаксической конструкции входного языка выполняется ее семантическая проверка на основе имеющихся в таблице идентификаторов данных (такими конструкциями, как правило, являются процедуры, функции и блоки операторов входного языка). Во втором случае, после завершения всей фазы синтаксического анализа, выполняется полный семантический анализ программы на основании данных в таблице идентификаторов (сюда попадает, например, поиск неописанных идентификаторов). Иногда семантический анализ выделяют в отдельный этап (фазу) компиляции. В каждом компиляторе обычно присутствуют оба варианта семантического анализатора. Конкретная их реализация зависит от версии компилятора и семантики входного языка.
Этапы семантического анализа Семантический анализатор выполняет следующие основные действия: · проверку соблюдения во входной программе семантических соглашений входного языка; · дополнение внутреннего представления программы в компиляторе операторами и действиями, неявно предусмотренными семантикой входного языка; · проверку элементарных семантических (смысловых) норм языков программирования, напрямую не связанных со входным языком. Проверка соблюдения во входной программе семантических соглашений Эта проверка заключается в сопоставлении входных цепочек исходной программы с требованиями семантики входного языка программирования. Каждый язык программирования имеет четко заданные и специфицированные семантические соглашения, которые не могут быть проверены на этапе синтаксического разбора. Именно их в первую очередь проверяет семантический анализатор. Примерами таких соглашений являются следующие требования: · каждая метка, на которую есть ссылка, должна один раз присутствовать в программе; · каждый идентификатор должен быть описан один раз и ни один идентификатор не может быть описан более одного раза (с учетом блочной структуры описаний); · все операнды в выражениях и операциях должны иметь типы, допустимые для данного выражения или операции; · типы переменных в выражениях должны быть согласованы между собой; · при вызове процедур и функций число и типы фактических параметров должны быть согласованы с числом и типами формальных параметров. Это только примерный перечень такого рода требований. Конкретный состав требований, которые должен проверять семантический анализатор, жестко связан с семантикой входного языка (например, некоторые языки допускают не описывать идентификаторы определенных типов). Дополнение внутреннего представления программы Это дополнение внутреннего представления программы связано с добавлением Таким образом, и в этом случае действия, выполняемые семантическим анализатором, существенным образом влияют на порождаемый компилятором код результирующей программы.
Проверка смысловых норм языков программирования Проверка элементарных смысловых норм языков программирования, напрямую не связанных со входным языком, — это сервисная функция, которую предоставляют разработчикам большинство современных компиляторов. Эта функция обеспечивает проверку компилятором соглашений, выполнение которых связано со смыслом как всей исходной программы в целом, так и отдельных ее фрагментов. Эти соглашения применимы к большинству современных языков программирования. · каждая переменная или константа должна хотя бы один раз использоваться в программе; · каждая переменная должна быть определена до ее первого использования при любом ходе выполнения программы (первому использованию переменной должно всегда предшествовать присвоение ей какого-либо значения); · результат функции должен быть определен при любом ходе ее выполнения; · каждый оператор в исходной программе должен иметь возможность хотя бы один раз выполниться; · операторы условия и выбора должны предусматривать возможность хода выполнения программы по каждой из своих ветвей; · операторы цикла должны предусматривать возможность завершения цикла.
Конечно, это только примерный перечень основных соглашений. Конкретный состав проверяемых оглашений зависит от семантики языка.
Идентификация лексических единиц языков программирования Идентификация переменных, типов, процедур, функций и других лексических единиц языков программирования — это установление однозначного соответствия между лексическими единицами и их именами в тексте исходной программы. Идентификация лексических единиц языка чаще всего выполняется на этапе семантического анализа. Как правило, большинство языков программирования требуют, чтобы в исходной программе имена лексических единиц не совпадали как между собой, так и с ключевыми словами синтаксических конструкций языка. Но чаще всего этого бывает недостаточно, чтобы установить однозначное соответствие между, лексическими единицами и их именами, поскольку существуют дополнительные смысловые (семантические) ограничения, накладываемые языком на употребление этих имен. Например, локальные переменные в большинстве языков программирования имеют так называемую «область видимости», которая ограничивает употребление имени переменной рамками того блока исходной программы, где эта переменная описана. Это значит, что, с одной стороны, такая переменная не может быть использована вне пределов своей области видимости. С другой стороны, имя переменной может быть не уникальным, поскольку в двух различных областях видимости допускается существование двух различных переменных с одинаковым именем (причем в большинстве языков программирования, допускающих блочные структуры, области видимости переменных могут перекрываться). Другой пример такого рода ограничений на уникальность имен — это тот факт, что в языке программирования С две разные функции или процедуры с различными аргументами могут иметь одно и то же имя. Безусловно, полный перечень таких ограничений зависит от семантики языка программирования. Все они четко заданы в описании языка и не могут допускать неоднозначности в толковании, но они также не могут быть полностью определены на этапе лексического анализа, а потому требуют от компилятора дополнительных действий на этапах синтаксического разбора и семантического анализа. Общая направленность этих действий такова, чтобы дать каждой лексической единице языка уникальное имя в пределах всей исходной программы и потом использовать это имя при синтезе результирующей программы. Можно дать примерный перечень действий компиляторов для идентификации переменных, констант, функций, процедур и других лексических единиц языка: · имена локальных переменных дополняются именами тех блоков (функций, процедур), в которых эти переменные описаны; · имена внутренних переменных и функций модулей исходной программы дополняются именами самих модулей (это касается только внутренних имен); · имена процедур и функций, принадлежащих объектам (классам) в объектно-ориентированных языках программирования дополняются наименованиями типов объектов (классов), которым они принадлежат; · имена процедур и функций модифицируются в зависимости от типов их формальных аргументов. Конечно, это далеко не полный перечень возможных действий компилятора, каждая реализация компилятора может предполагать свой набор действий.
|