Реализация атрибутов в RBNF-грамматиках системы SYNTAX
А.С. Лукичев Кафедра информатики, мат.-мех. факультет СПбГУ [email protected]
Выбор систем автоматической генерации анализаторов очень широк. Большинство из этих систем реализуют генерацию алгоритмов анализа по регулярным или контекстно-свободным грамматикам, например, LEX/YACC. В первом случае получаются весьма эффективные приложения, однако класс языков, к которым применима подобная техника, достаточно узок. Во втором случае класс языков шире, однако и сложность даже алгоритмов распознавания может быть O(n ) из-за недетерминизма используемого механизма. Задача анализа может потребовать и дополнительного, обратного, просмотра для однозначного определения синтаксической структуры входной цепочки.
Одной из целей при разработке системы SYNTAX [5] было совместить применимость к широкому классу языков с высокой эффективностью распознавателей. Методика спецификации и реализации трансляций, применяемая здесь, основана на идее аппроксимации контекстно-свободного языка регулярными множествами, подобно сплайнам. Средства задания языков базируются на так называемых RBNF-грамматиках, правила которых определяют порождения для нетерминалов при помощи регулярных выражений. Анализатор — сплайновый процессор — на отдельных участках входной цепочки ведет себя, как конечный автомат, используя магазин лишь для сопряжения аппроксимирующих регулярных множеств. При этом конечный автомат строится по регулярному выражению, заданному в соответствующем правиле грамматики. Данный подход позволяет добиться высокой эффективности реализации трансляций [5].
Применение только лишь контекстно-свободных грамматик не позволяет описать полностью синтаксис используемых на практике искусственных языков. Описания таких языков зачастую содержат ограничения, требующие контекстно-зависимого анализа. Для формального описания таких языков можно использовать двухуровневые грамматики. Наиболее известными являются W-грамматики А. ван Вейнгаардена [6] и аффиксные грамматики [3].
Еще одной задачей является описание синтаксически-управляемой трансляции. В системах синтаксически-управляемой обработки данных используется метод привязки семантических процедур к правилам грамматики. Семантические процедуры зависят друг от друга по данным, при этом последовательность их вызова определяется синтаксической частью транслятора, задаваемой контекстно-свободной грамматикой. Д. Кнут предложил схему задания трансляции, в которой такие потоки данных описываются на уровне грамматики с помощью атрибутов [2].
В SYNTAX-технологии изначально аффиксы и атрибуты не были предусмотрены, однако для реализации контекстной зависимости и спецификации трансляции использовались предикатные символы (резольверы) и семантические процедуры (семантики), соответственно. Резольверы и семантики не имели параметров, что означало, что весь обмен контекстной информацией мог осуществляться лишь через глобальную операционную среду. Это выводило разработку операционной среды из-под контроля технологии. Введение атрибутов в SYNTAX-технологию равносильно введению параметризации упомянутых семантических процедур и предикатных функций, а также терминалов. Это придает данной технологии большую надежность и предоставляет пользователю больше удобств и дополнительной информации при проектировании средств синтаксической обработки данных. Целью данной работы является рассмотрение одного из возможных способов реализации такой поддержки, формальное построение трансляции и описание модели вычислений в такой системе.
При введении атрибутов для контекстно-свободных грамматик [2] наборы атрибутов связываются с вхождениями грамматических символов (нетерминалов и терминалов) в правила грамматики, а правила вычислений значений атрибутов связываются с самими правилами грамматики. Так, правилу грамматики Л^В1В2, где Л имеет синтезируемый атрибут с1, а В1 и В2 — наследуемые атрибуты с2 и с3, соответственно, можно сопоставить правило вычисления атрибута с1:=/(с1,с1). Важно отметить, что атрибуты здесь характеризуются не только своим именем, но и вхождением связанного с ними грамматического символа в правило. Так, в правиле Л^ВВ атрибуты, связанные с вхождениями нетерминалов В будут различны, поскольку различны эти вхождения. Т.е. правило для вычисления с1,
атрибута А, будет выглядеть как сь=/(с2,сз), где с2 и с3 — атрибуты первого и второго вхождений В, соответственно. Если рассматривать соответствующий узел в дереве разбора как запись с полями для хранения информации, то атрибут грамматического символа можно считать именем поля [1].
Рассмотрим возможность применения аналогичного подхода к RBNF-грамматикам. Управляющая RBNF-грамматика определяется [5] как формальная система 0=(Ум,Ут, £Н,Х,Р,8), где Ум и Ут — множества нетерминалов и терминалов соответственно, 5Н и Е — множества резолъверов и семантик, 8е Ум — начальный нетерминал, а Р — множество правил вида N : где NeVN, а RN — регулярное выражение над объединенным алфавитом Ук=УмиУти Значение такого
регулярного выражения ^(Я) определяется следующим образом:
Взяв в качестве примера правило А: В+ RBNF-грамматики и применив предложенную выше схему введения атрибутов, можно связать, например, атрибут с1 с вхождением А, а атрибут с2 — с вхождением В и задать правило вычисления с1 как с1:=/(с2). Однако в таком случае вычисление с1 не будет зависеть от того, какому из предложений В, ВВ, ВВВ и т.д. соответствует разбираемая по данному правилу синтаксическая конструкция. Оказывается удобно вычислять значения атрибутов итеративно в процессе анализа входной цепочки по соответствующему правилу грамматики. Т.е. в данном примере можно некоторым образом задать правило с1:=^(с1,с2), которое исполняется всякий раз при разборе очередной
и и т-\
подконструкции входной цепочки, соответствующей очередному вхождению В. Таким образом, в случае, если разбираемая синтаксическая конструкция соответствует ВВВ, то значение атрибута с1 будет вычисляться в данном случае по правилу сь=/(Со,С1,С2,Сз), где /(х0,х1,х2,хз)=^(^(^(х0,х1),х2),хз), Сь С2, Сз — значения синтезируемых атрибутов для каждого вхождения В в цепочку ВВВ, С0 — начальное значение с1, задаваемое при инициализации. С0 — константа, поэтому значение с1 в данном случае зависит от трех параметров: значений синтезируемых атрибутов вхождений В.
Для реализации такого подхода рассмотрим следующую схему: со всяким вхождением нетерминала в левые части правил свяжем набор формальных атрибутов, а со всяким вхождением нетерминала, семантики или резольвера в правые части правил свяжем набор фактических атрибутов. Формальные атрибуты можно разделить на 3 класса:
■ наследуемые атрибуты (их значения вычисляются в объемлющей конструкции и поступают в подконструкцию);
■ синтезируемые атрибуты (их значения вычисляются в подконструкции и возвращаются в объемлющую конструкцию);
■ локальные атрибуты (используются только в пределах подконструкции). Набор формальных атрибутов изображается следующим образом:
Здесь 1ь..., In — имена наследуемых атрибутов, Ob...,Om — имена синтезируемых атрибутов, L1,.,Lk — имена локальных атрибутов. Фактические атрибуты можно разделить на 2 класса:
■ наследуемые атрибуты;
■ синтезируемые атрибуты.
Набор фактических атрибутов изображается следующим образом:
Пусть Ф=(т Ii,..., In, out Oi,..., Om, local Lb..., Lk). Набор фактических
атрибутов (Xi,..., Xs) называется определенным в контексте набора формальных
атрибутов Ф, если Xie{I1,., In}u{Ob..., Om}u{Lb..., Lk}. Пустой набор
фактических атрибутов является определенным в контексте любого набора
формальных атрибутов. Пусть D^) — множество всех наборов фактических
ф ф
атрибутов, определенных в контексте Ф, тогда правило NO : R v, где Rjv — регулярное выражение над операндами из множества VND(0)uVTu 9iD(0)uZD(0), называется аннотированным правилом. RBNF-грамматику с аннотированными правилами будем называть атрибутной RBNF-грамматикой. Сигнатурой грамматического символа называется описание типов и порядка следования атрибутов, связанных с этим символом.
При изучении свойств RBNF-грамматик и построении сплайновых процессоров удобно бывает рассматривать представление RBNF-грамматики в виде граф-схемы — аналога диаграмм Вирта. Граф-схема представляет собой направленный граф, вершины которого помечены символами из VNuVT, а дуги — цепочками из iH и X, , описывающими условия принятия входных символов и выполняемые при этом семантические действия соответственно. Каждому регулярному выражению в правой части правила RBNF-грамматики можно сопоставить компоненту граф-схемы и наоборот. Граф-схема для атрибутной грамматики строится аналогичным образом, при этом пометки вершин компоненты для аннотированного правила
NO : R* берутся из множества VND(0)uVT, а пометки дуг являются цепочками из (9Ш(Ф)) и (SD(O)) .
Определим теперь атрибутную трансляционную RBNF-грамматику GT=(GC,E), где GC — атрибутная управляющая RBNF-грамматика, а E — операционная среда. E=(£,F,I <нДх,ео), где <£ — пространство состояний
операционной среды, F — множество значений атрибутов грамматических
*
символов (нетерминалов, семантик и резольверов), IsH={ip: ip: £xF —»{false, true}}
— множество предикатов, ассоциированных с резольверами и параметризованных
* *
значениями атрибутов, Ii={ia : ia:£xF—>£xF } — множество преобразований операционной среды, ассоциированных с семантиками и параметризованных значениями атрибутов.
Определим рекурсивное построение трансляции. Пусть имеется правило 7tw=NO : Rw, где 0=(in Ii,..., In, out Oi,..., Om, local Lb..., Lk). Построим функцию
Vt xixF —>2 exF . Будем строить множество x„w (x,e/) для некоторых xeVT , * -_^
ее i, /eF . При этом /=i|.. ,inO|.. ,oml|.. — цепочка длины n+m+k, в противном случае x„w (х,е/)=0. Пусть ^(Rw) содержит цепочку Х1ф1...Х3ф3, фь.. ,,ф3еВ(Ф),
ф]=е, если XjEVT. Каждый набор фактических атрибутов фj=(Yl,.. ,,YP), можно
(1) * * (2) * * * ассоциировать с двумя отображениями фj( .F ^F и
, а именно:
Ф[Р{1) = У1---УР,
г
Не умаляя общности, можно считать, что в наборе фj Y1,...,Yt — наследуемые атрибуты, а Yt+1,.,Yp — синтезируемые (для некоторого 0<1<р). Тогда:
Говоря неформально, фj(1) выбирает значения наследуемых атрибутов грамматического символа Xj из набора текущих значений контекста формальных атрибутов /к), а ф|(2) присваивает некоторым элементам из этого набора значения соответствующих синтезируемых атрибутов Xj.
Построим недетерминированным образом последовательность троек ((/(j),ej,xj))
5=о цепочек длины n+m+k из F, состояний операционной среды eje <£ и
*
Xj£(VTU(Z}) , ZéVt:
f G'+i) ej+ L
- Л 2)
ФГ(.1и\П
= e
если
определена и равна (е', /'),
Тогда (e'/)ei„w (x,ef), если и только если существует разбиение x=x1.. ,xs такое, что можно построить хотя бы одну последовательность ((/^,ej,Xj))i=o, где e'=es,/=/s|.
Пусть nileF$ — специальное значение, означающие отсутствие присвоенного значения. Потребуем, чтобы начальный нетерминал S имел только локальные
формальные атрибуты. Тогда трансляцией, задаваемой трансляционной
k *
грамматикой GC называется {(x,e) : (e,f) ei„w(x,e0,nil ), feF }, где л — правило для S.
Для определения трансляции потребовалось ввести ряд ограничений, которые можно переформулировать следующим образом:
1) Все вхождения нетерминала в правила имеют одинаковую сигнатуру, при этом если имеется вхождение Ne VN, имеющее фактический набор атрибутов (li,...,Im,Oi,...,On), где Ii,...,Im — наследуемые атрибуты, a Oi,...,On — синтезируемые, то N имеет набор формальных атрибутов (in ib...,in, out ob...,om, local lb...,lk).
2) Терминальные символы не имеют атрибутов.
3) Начальный нетерминал грамматики не имеет формальных синтезируемых и наследуемых атрибутов.
4) Вхождение резольвера имеет пустое множество синтезируемых атрибутов (с целью исключить побочные эффекты).
Рассматривая модель вычислений в данной системе, имеет смысл говорить о магазине контекстов, двигающемся вместе с магазином сплайнового процессора. Контекстом в таком случае является некоторое множество объектов {/1,.../Р}, а текущим контекстом — контекст, находящийся на вершине магазина контекстов. Каждый объект fi=(ni,vi) имеет имя n(/i)=ni и значение v(/i)=vieF (F — множество значений атрибутов грамматических символов). Пусть начальный нетерминал S имеет набор формальных атрибутов (local li,...,lk) (в силу ограничения (3)). Механизм построения сплайнового процессора подробно описан в [5], воспользуемся лишь результатами данного процесса. В начале работы в магазин сплайнового процессора помещается магазинный символ X={Begin-S} (начальная вершина компоненты граф-схемы для правила начального нетерминала), а в магазин контекстов — множество неинициализированных объектов {(l1,nil),.,(lk,nil)}. Пусть теперь на вершине магазина контекстов находится
контекст Y-{/i,...fP}, а сплайновый процессор помещает на вершину магазина магазинный символ X={v1,.,vs} (помещение в магазин цепочки символов можно разбить на последовательное помещение одиночных символов). При этом каждая вершина граф-схемы vj помечена Nj(n(fw(i)),...,n(fw(n+m))), где w : l:n+m—>l:p — некоторая функция, задающая набор атрибутов, параметризующих грамматический символ. В дальнейшем будем называть w функцией выбора атрибутов. Тогда, в силу ограничения (1), нетерминал Nj имеет набор формальных атрибутов (in ii,...,in, out 0i,...,0m, local li,...,lt). В магазин контекстов помещается контекст Y"={Ib...,In, Ob...,Om, Li,.,Lk}, причем n(I1)=i1, n(Oi)=Oi, n(Li)=li. При этом v(I;)=v(fW(i)), а объекты O1,.,Om, Lb...,Lk не инициализированы. При выполнении перехода из подавляемого состояния, снятии символа с вершины магазина и, таким образом, возврате к контексту Y' происходит установка значений синтезируемых атрибутов: vfw(i+m))=v(Oi).
При вычислении резольвера, имеющего фактический набор атрибутов ( 1)),...,п(^(П))) в том же контексте У, происходит вызов ассоциированной с ним предикатной функции с параметрами (^^Х...,^^))). При совершении семантического действия, связанного с семантическим символом, имеющим фактический набор атрибутов . .,п(^у(П П11) ) в контексте У, где первые п
атрибутов синтезируемые, а оставшиеся т — наследуемые, происходит обращение к ассоциированной с данной семантикой процедуре со входными параметрами
(4^1)),. • - ,Ч^п))) и выходными — (у(^п+1)),.. .,Ч^п+т))).
Анализ входной цепочки в технологии SYNTAX [5] осуществляется челночным процессором. Данный механизм состоит из двух сплайновых процессоров, применяемых последовательно. На прямом просмотре, который был только что рассмотрен, входом служит входная цепочка, а результатом является измененное состояние операционной среды и набор «параллельных» путей в граф-схеме, терминальный след которых составляет входную цепочку с учетом контекстных условий. Для более точного выделения пути в граф-схеме, соответствующего данной входной цепочке, используется обратный просмотр, контекстные условия на котором позволяют отбросить неподходящие параллельные пути. Кроме того, результатом обратного просмотра также является изменение состояния операционной среды при применении семантических операций обратного просмотра. Использование атрибутов в обратном просмотре не только дает преимущества аналогичные получаемым на прямом просмотре, но и позволяет решить задачу передачи контекстной информации между просмотрами.
В каждом из 3 классов атрибутов выделяются атрибуты, используемые на прямом и на обратном просмотре. Для обеспечения передачи контекстной информации между просмотрами выделяется специальный тип локальных атрибутов — двусторонние атрибуты. Такие атрибуты инициализируются на прямом просмотре, причем их значение (полученное к моменту завершения анализа подконструкции языка, соответствующей данному регулярному выражению) сохраняется и может быть использовано на обратном просмотре.
Работу сплайнового процессора прямого просмотра можно представить как исполнение программы некоторого конечного автомата с вызовами «подпрограмм»-конечных автоматов. Для реализации такого стека вызовов используется магазин сплайнового процессора. На обратном просмотре происходит обход пройденных состояний конечных автоматов в обратном направлении. Поэтому обратный просмотр также может быть реализован с помощью сплайнового процессора. Введение атрибутов происходит здесь аналогично прямому просмотру.
Значения двусторонних атрибутов можно рассматривать как входные данные обратного просмотра. Поэтому имеет смысл хранить эти значения вместе с входными символами обратного просмотра. Причем только вместе с теми
символами, при принятии которых происходит смена текущего контекста сплайнового процессора обратного просмотра, а значит, изменение (пополнение) магазина процессора. Входными символами при выполнении обратного просмотра являются номера состояний прямого просмотра. При этом согласно алгоритму построения управляющих таблиц обратного просмотра [5], помещение очередного магазинного символа в магазин на обратном просмотре происходит при принятии номера возвратного состояния (состояния объемлющего конечного автомата, в которое он переходит при возврате сплайнового процессора из анализа «вызванного» конечного атвомата) Таким образом, значения двусторонних атрибутов достаточно хранить только вместе с номерами возвратных состояний. При принятии такого входного символа происходит помещение в магазин процессора возвратного состояния обратного просмотра, а в магазин контекстов помещается очередной контекст, который становится текущим. При этом элементы данного контекста, соответствующие наследуемым и синтезируемым атрибутам обратного просмотра, инициализируются значениями соответствующих элементов предыдущего контекста. Элементы, соответствующие локальным атрибутам, инициализируются значениями по умолчанию. А элементы, соответствующие двусторонним атрибутам, получают значения, вычисленные на прямом просмотре и сохраненные вместе с текущим входным символом.
Выше уже были сформулированы некоторые ограничения, налагаемые на атрибутную спецификацию трансляции. Дополним этот список новыми ограничениями, которые также учитывают введение атрибутов обратного просмотра.
В приведенном выше определении трансляции никак не учитывался тот факт, что в наборе значений наследуемых фактических атрибутов могут присутствовать неопределенные (nil) значения. Чтобы избежать подобной ситуации необходимо удовлетворить следующие ограничения. Для удобства сформулируем их в терминах граф-схем:
5) Если формальный атрибут прямого просмотра используется как наследуемый фактический атрибут, то
а) либо на всяком пути в компоненте граф-схемы из начальной вершины в данную соответствующий формальный атрибут хотя бы однажды
использовался как синтезируемый фактический атрибут прямого просмотра;
б) либо соответствующий формальный атрибут является формальным наследуемым атрибутом нетерминала, соответствующего данной компоненте.
6) Если формальный атрибут обратного просмотра используется как наследуемый фактический атрибут, то
а) либо на всяком пути в компоненте граф-схемы из конечной вершины в данную соответствующий формальный атрибут хотя бы однажды использовался как синтезируемый фактический атрибут обратного просмотра;
б) либо соответствующий формальный атрибут является формальным наследуемым атрибутом нетерминала, соответствующего данной компоненте;
в) либо соответствующий формальный атрибут является двусторонним атрибутом.
7) Для всякого синтезируемого формального атрибута прямого просмотра нетерминала, соответствующего данной компоненте, на всяком пути в компоненте граф-схемы из начальной вершины в конечную существует вершина, у которой данный формальный атрибут используется как синтезируемый фактический атрибут прямого просмотра.
8) Для всякого синтезируемого формального атрибута обратного просмотра нетерминала, соответствующего данной компоненте, на всяком пути в компоненте граф-схемы из конечной вершины в начальную существует вершина, у которой данный формальный атрибут используется как синтезируемый фактический атрибут обратного просмотра.
9) Для всякого двустороннего формального атрибута нетерминала, соответствующего данной компоненте, на всяком пути в компоненте граф-схемы из начальной вершины в конечную существует вершина, у которой
и 1 и и 1 и
данный формальный атрибут используется как синтезируемый фактический атрибут прямого просмотра.
Из описания модели вычислений видно, что для обеспечения детерминированности процедуры, необходимо, чтобы все нетерминалы N помечающие вершины V граф-схемы, составляющие некоторый магазинный символ Х={уь...,^}, имели одинаковые сигнатуры, соответствующие наследуемые атрибуты имели одинаковые значения, а синтезируемые атрибуты следовали в одинаковом порядке. Будем считать, что два набора фактических атрибутов эквивалентны, если соответствующие им функции выбора атрибутов совпадают. Таким образом, можно сформулировать следующее ограничение:
10)Если в разложении состояния по некоторому терминалу на одном уровне расположены вершины, помеченные нетерминалами N^,...,N3, то все N должны иметь одинаковые сигнатуры, а их наборы фактических атрибутов должны быть эквивалентны.
Следующие два ограничения отделяют атрибуты прямого и обратного просмотра:
11)Семантики и резольверы прямого просмотра могут быть помечены только атрибутами прямого просмотра или двусторонними.
12)Семантики и резольверы обратного просмотра могут быть помечены только атрибутами обратного просмотра или двусторонними.
В [5] введено требование семантической балансировки, заключающееся в том, что все пути от начальной вершины к листьям в разложении состояния по некоторому терминалу помечены одинаковыми цепочками семантик. Данное требование должно быть дополнено условием того, что соответствующие друг другу вхождения семантик в цепочки должны иметь одинаковые наборы значений фактических атрибутов:
13)Все пути от начальной вершины к листьям в разложении состояния прямого просмотра по некоторому терминалу, помеченные одинаковыми цепочками резольверов прямого просмотра, должны быть помечены не просто одинаковыми цепочками семантик прямого просмотра, эти семантики должны иметь одинаковые сигнатуры, а их наборы фактических атрибутов должны быть эквивалентны.
14)Все дуги из вершин, составляющих состояние обратного просмотра, в множество вершин, составляющих состояние прямого просмотра,
помеченные одинаковыми цепочками резольверов обратного просмотра, должны быть помечены не просто одинаковыми цепочками семантик обратного просмотра, эти семантики должны иметь одинаковые сигнатуры, а их наборы фактических атрибутов должны быть эквивалентны.
Список литературы
1. Ахо А., Сети Р., Ульман Дж. Компиляторы: принципы, технологии, инструменты. М., 2001 г.
2. Knuth D.E., Semantics of context-free languages // Mathematical Systems Theory 2:2. 1968, pp. 127-145
3. Koster C.H.A., Affix Grammars // In: Proceedings of IFIP Conference on ALGOL 68 Implementation. 1970. Munich, pp. 95-109
4. Koster C.H.A., Affix Grammars for Programming Languages // In: Attribute Grammars, Applications and Systems. International Summer School SAGA. 1991. Prague.
5. Мартыненко Б.К., Синтаксически управляемая обработка данных. СПб, 1997 г.
6. van Wijngaarden A., Orthogonal Design and Description of a Formal Language. Report MR76, Mathematisch Centrum, Amsterdam, 1965.