УДК 519.682.1 +681.142.2
Мартыненко Борис Константинович
РЕГУЛЯРНЫЕ ЯЗЫКИ И КС-ГРАММАТИКИ
Аннотация
Существует множество технологических средств построения анализаторов формальных языков, используемых при создании разного вида трансляторов языков программирования. Все они, в конечном счёте, основываются на КС-грамматиках с теми или иными ограничениями, частным случаем которых являются регулярные (автоматные) грамматики. Как правило, эти технологические средства обеспечивают лишь проверку соответствующих требований, предъявляемых к грамматике, и выдачу диагностических сообщений об их нарушениях, тогда как существует множество способов эквивалентных преобразований КС-грамматик, которые могут быть выполнены автоматически и дать грамматики, удовлетворяющие требованиям метода анализа.
Цель этой статьи - описать способ исключения несамовставленных нетерминалов из КС-грамматик за счёт введения регулярных выражений в правые части правил грамматики. В предельном случае такая преобразованная грамматика включает единственное правило для начального нетерминала.
Ключевые слова: КС-грамматика, регулярное выражение, эквивалентное преобразование грамматики.
1. ВВЕДЕНИЕ
Впервые КС-грамматики были использованы для описания синтаксиса алгоритмических языков в [1]. Формализм металингвистических формул, названный позднее БМР-формой, в [2] был дополнен возможностью использовать регулярные выражения над терминалами, нетерминалами в правых частях правил и получил ещё одну букву в обозначении класса грамматик ЯБМР.
В 1970-х годах ЯБМР-грамматики были использованы в проекте реализации языка программирования Алгол 68 [3]. Несколько позже [4] в эту модель описания языков было введено понятие операционной среды, а в правила грамматики - контекстные символы (семантики и резольверы) с их интерпре-
© Мартыненко Б.К., 2012
тацией в виде семантических процедур и предикатных функций. Это нововведение дало возможность использовать RBNF-грамматики в качестве средства описания трансляций, учитывающего контекст конструкций входного языжа трансляции. Эта модель описания и реализации трансляций использовалась при реализации ряда нетривиальных языков программирования, таких как Алгол 68 и Ада. Несколько позже она быша воплощена в технологическом комплексе SYNTAX, который в течение ряда лет использовался в семинарских занятиях по технологии трансляции на математико-механическом факультете Санкт-Петербургского университета.
ТК SYNTAX включает средства эквивалентных преобразований на уровне граф-схем за счёт перевода несамовставленных нетерминалов в разряд вспомогательных понятий, определения которыгх подставляются вместо
примененных вхождении таких символов в правые части правил (метаподстановки). В остальном, ТК SYNTAX требует грамматик, уже приведенных к нужному виду, так же как большинство известных технологических систем генерации анализаторов.
К настоящему времени в теории формальных языков накоплено достаточно способов эквивалентных преобразовании грамматик. Применить их для автоматического предварительного приведения КС-грамматик в соответствие с требованиями инструментальных систем построения анализаторов представляется актуальнои и реальнои задачеи.
2. НЕКОТОРЫЕ ПРЕДВАРИТЕЛЬНЫЕ ЭКВИВАЛЕНТНЫЕ ПРЕОБРАЗОВАНИЯ КС-ГРАММАТИК
Считая, что основные понятия и факты теории формальных языков известны читателю, необходимые определения, алгоритмы и утверждения, на которых они основаны, а также соответствующие обозначения, будем напоминать по ходу изложения.
Пусть G = (VN,VT,P,S) - КС-грамматика, где VN - конечное множество нетерминалов, VT - конечное множество терминалов (VN n VT = 0), P - конечное множество правил вида A ® a, A е VN, ae V *. Здесь V * - бесконечное множество всех цепочек над алфавитом V = VN u VT, включая и пустую цепочку, обозначаемую символом е.
Пусть x = yxAyv где g1, g2 е V*, и A ® ae P. Считается, что из цепочки x непосредственно выводится цепочка y посредством указанного правила x ^ y, если У = TiaYT
Будем использовать обозначение x y для вывода цепочки y из цепочки x за счет использования нескольких правил грамматики, при этом считается, что любая цепочка x выводима сама из себя, и для этого не требуются никакие правила.
Тогда L(G) = {w I w eV*, S w} - язык,
' G
порождаемый грамматикой G.
Далее рассматриваются все эквивалентные преобразования КС-грамматик регулярного языка, предшествующие получению
регулярного выражения, его представляющего.
I) ПРОДУКТИВНОСТЬ НЕТЕРМИНАЛОВ
Пусть G = (Уы, Ут, Р, Б) - произвольная КС-грамматика. Нетерминал А е Уы называется продуктивным, если существует вывод вида А == м, где м е УТ*.
G
Известно, что любой КС-языгк может быгтъ порождён КС-грамматикой, каждыгй нетерминал которой продуктивен.
Для получения такой грамматики используется следующий вспомогательный алгоритм.
Алгоритм 1. Проверка пустоты языка
Вход: G = (Ум,Ут,Р,Б) - КС-грамматика.
Выгход: Цв) = 0.
Метод:
Шаг 1. Начать построение коллекции деревьев вывода с единственного дерева, представленного только корнем - узлом с меткой
Шаг 2. Добавлять к этой коллекции любое дерево, которое может быть получено из дерева, уже имеющегося в коллекции, посредством применения одного правила, при условии, что образующееся дерево не имеет ни одной ветви длиннее, чем число нетерминалов данной грамматики.
Шаг 3. Если в построенной коллекции есть хотя бы одно дерево, представляющее вывод терминальной цепочки, то язык Ц(в) не пуст. Иначе - язык Ц(в) пуст.
Алгоритм 2. Исключение непродуктивных нетерминалов
Вход: G = (У^,УТ, Р, Б) - КС-грамматика.
Выгход: G1 = (Ум 1, Ут, Р1, Б), причём Ь^) = ЦО).
Метод: Для каждого нетерминала А е Ук рассмотрим грамматику вА = (У№ Ут, Р, А).
Если язык Ц(СА) пуст, то мы удалим А из множества У№ а также все правила, использующие А в правой или левой части.
После удаления из в всех таких нетерминалов и правил мы получим новую грамматику в1 = (У^, Ут, Р1, 5), где У^ и Р1 - оставшиеся нетерминалы и правила.
II) ДОСТИЖИМОСТЬ НЕТЕРМИНАЛОВ
Говорят, что нетерминал А gFn достижим, если существует вывод вида S ajAa2 для некоторых цепочек
aj,a2 е V*.
Известно, что любой КС-языгк может быгтъ порождён КС-грамматикой, каждыгй нетерминал которой достижим. Для получения такой грамматики можно использовать следующий алгоритм.
Алгоритм 3. Исключение недостижимых нетерминалов
Вход: Gj = (VN VT,Pj,S) - КС-грамматика, все нетерминалы которой продуктивны.
Выгход: G2 = (VN2,VT,P2,S) - эквивалентная КС-грамматика, все нетерминалы которой используются в выводах цепочек языка.
Метод: Мы можем эффективно построить множество VN всех нетерминалов A, таких что существует вывод вида S а1Аа2, где aj ,а2 е V*.
Для начала поместим S в искомое множество. Затем последовательно будем добавлять к этому множеству любой нетерминал, который появляется в правой части любого правила из P1, определяющего нетерминал, уже имеющийся в этом множестве.
Процесс завершается, когда никакие новые элементы не могут быть добавлены к упомянутому множеству.
Положим G2 = (VN2,Vt,P2,S), где P2 -множество правил, оставшихся после исключения всех правил из P1, которые используют символы из VNi \ VN2 в левых или
правых частях правил. Известно, что ОД) = ОД).
Грамматики, в которых все нетерминалы продуктивные и достижимые, называются приведёнными. Они хороши тем, что не содержат нетерминалов и правил, не участвующих в порождении данного языка.
III) ЦЕПНЫЕ ПРАВИЛА
Известно, что любой контекстно-сво-бодныш языгк может быгтъ порожден контекстно-свободной грамматикой, не содер-
жащей цепныгх правил, то естъ правил вида A ® B, где A и B - нетерминалыi.
Алгоритм 4. Исключение цепных правил
Вход: G2 = (VN ,VT,P2, S) - приведённая КС-грамматика.
Выход: G3 = (VN,VT,P3,S) - КС-грамматика без цепных правил, эквивалентная исходной.
Метод: Мы построим новое множество правил P3, прежде всего включив в него все нецепные правила из P2. Затем из каждого цепного правила вида A ® B е P2 образуем множество новых нецепных правил вида A ® а, если существует нецепное правило B ® ае P2, и пополним множество P3 новыми нецепными правилами, полученными таким путём.
Вывод одной и той же цепочки в грамматике P3 короче вывода в грамматике P2 на число применённых цепных правил из P2.
IV) СВОЙСТВО САМОВСТАВЛЕННОСТИ
Пусть G = (VN,VT,P,S) - КС-грамматика. Символ А е VN называется самовстав-ленныгм, если существует вывод вида А ajA а2, где aj Ф в и а2 Ф в в предположении, что в грамматике не существует e-правил, и, кроме того, если в е L(G), то S не должен встречаться в правой части никакого правила, и пустое предложение порождается единственным правилом вида S ® в.
Последнее требование можно легко удовлетворить эквивалентным преобразованием грамматики с помощью следующего алгоритма.
Алгоритм 5. Исключение начального нетерминала S из правых частей правил
Вход: G = (VN,VT,P,S) - КС-грамматика.
Выгход: G' = (VN, VT, P', S') - КС-грамматика, эквивалентная исходной.
Метод: Пусть S' - символ, не принадлежащий ни алфавиту нетерминалов, ни алфавиту терминалов.
Положим G' = (VN и {S'}, VT, P', S'), где P '= P/{S ® в} и {S' ® a|S ® а е P}.
3. ПОЛУЧЕНИЕ РЕГУЛЯРНОГО ВЫРАЖЕНИЯ ПО КС-ГРАММАТИКЕ
Известно, что любой контекстно-свободный язык, порождаемый КС-грамматикой без самовставлений, является регулярным множеством.
По следствию из теоремы С.К. Клини [5], если язык регулярный, то он может быть представлен регулярным выражением, то есть конечной формулой с терминальными цепочками в качестве операндов и операциями конкатенации, объединения и замыкания, называемых регулярными. Наша задача - описать способ его получения, исходя из КС-грамматики, полученной с помощью предварительных преобразований (см. раздел 2).
Множество правил КС-грамматики без самовставлений можно рассматривать как систему уравнений с коэффициентами в виде терминальных цепочек, в которой нетерминалы играют роль неизвестных. Решение такой системы заключается в нахождении регулярных выражений, значения которых представляют множества терминальных цепочек, выводимых из соответствующих нетерминалов грамматики. Метод решения такой системы правил грамматики аналогичен методу Гаусса для линейных алгебраических уравнений. Фактически нас интересует значение регулярного выражения для начального нетерминала грамматики.
Алгоритм 6. Исключение несамовстав-ленных нетерминалов
Вход: G = (VN,VT,P,S) - приведённая КС-грамматика без самовставлений.
Выход: регулярное выражение, представляющее язык L(G).
Метод: Введём отношение зависимости нетерминалов, определяемое правилами КС-грамматики следующим образом.
Пусть A ® a1Ba2, где A, B е VN, a1, a2е V*, есть правило грамматики. Будем считать, что нетерминал A зависит от нетерминала B.
Шаг 1. Построим граф зависимостей нетерминалов. В нём будет столько вершин, сколько нетерминалов в грамматике, по одной на каждый нетерминал. Вершины свя-
зываются ориентированными дугами от вершины А к вершине В в соответствии с отношением зависимости, определённым выше.
Шаг 2. С помощью топологической сортировки [6] распределим вершины графа зависимостей по уровням. В результате вершина 5 окажется на самом высоком уровне, а вершины, не зависящие от других, - на самом низком уровне (см. рис. 1).
Шаг 3. Выполним подстановки значений нетерминалов в виде регулярных выражений в правые части правил исходной КС-грамматики в порядке от минимального уровня (0) к максимальному (8) вдоль дуг в противоположном направлении.
При этом прежде чем выполняется упомянутая выше подстановка, леворекурсивное правило, то есть правило вида
А: А а; Ь (1)
где а, Ь - регулярные выражения относительно терминалов, преобразуется к виду
А: Ь, (а)* (2)
а праворекурсивное правило, то есть правило вида
А: аА; Ь (3)
преобразуется к виду
А: (а)*Ь (4)
Шаг 4. Алгоритм завершается, когда образуется регулярное выражение для начального нетерминала грамматики 5. Именно оно и является выходом данного алгоритма.
Замечание 1. Ясно, что шаг 2 алгоритма 6 выполним, только если в исходном графе зависимостей нетерминалов не существует циклов (см. [6]). Однако, петли допустимы.
Замечание 2. На графе зависимостей нетерминалов КС-грамматики (см. рис. 1) лево-сторонняя рекурсия отражается в виде петли.
Замечание 3. На рис. 1 против нетерминалов показаны правые части соответствующих правил исходной грамматики и/или результаты вышеупомянутых подстановок с учётом исключения левосторонней рекурсии, если таковая встречается, и других очевидных регулярных тождеств [4: гл. 5], однако без объяснения, как выбирать и применять регулярные тождества.
Покажем на следующем примере (рис. 1) как преобразовать КС-грамматику чисел языка программирования Алгол 68 [3] к регулярному выражению. Ради краткости,
понятия языка Алгол 68 (они указаны в фигурных скобках) обозначены нетерминальными символами А1-А15. В результате топологической сортировки нетерминалы распо-
Пример 1. Грамматика чисел в языке Алгол 68. Дана КС-грамматика G = (VN,VT,P,S), где V = {A A A A A
N l 15{number}> 1{plusminus}' 2{plusminus option }' 3{power often }' 4{times ten to the power choice }'
AAA A A
5{exponent part}' 6{stagnant part}' 7{floating point numeral}' 8{fractional part}' 9{integral part}'
A A A A
10{integral part option}' 11{variable point numeral}' 12{digit cypher }' 13{digit cypher sequence}' A 14{fixed point numeral }}' S - A15'
VT - { '0 '' '1 '' '2 '' '3 '' '4 '' '5 '' '6 '' '7 '' '8 '' '9 '. '\ ' , 'e '+ ' ; '} - d,
A10' A9 ; e A5: A4 ' A3.
P - {A 15' A14 ; An ; A7.
A14' A13.
A9' A14.
A4: '\ ' ; 'e '.
A ' A • A A A ' ' ' A A ' A A
13 12 13 12 8 14 3 2 14
A 12' 0 • 1 ; 2 ; 3 ; 4 ; 5 ; 6 ; 7 ; 8 ; 9 . A 7 : A 6 ' A 5 . A 2 : A 1 ; £.
A11: A10 ' A8. A6' A14 ; A11. A1' '+ ' ; '- '.}
A15 = d+ ; d*,'.', d+ ; (d+; d*,'.', d+), (('\' ; 'e'), ['+' ; '-'], d+) =
A 7 = (d+; d* , ' = (d+ ; d*,'.', d+), [ ('\' ; 'e'), ['+' ; i \ ' 7 d+) , (('Y ; 'e') 7 [+ ; d+) '-'], d+] Уровень 7
= d+; d* , ' . ' , d+ 7 \ Уровень 6
V "" - 1 \ d*,'.', d+ Уровень 5
A 5 = ('\' ; 'e'), ['+' ; '-' Л 7 Л !10 = A9; e = d+ ; e = d* Уровень 4
A3 = [Y ; '-'] 7 d+ \ 7 / \ ! a8='.', d+ , 1, = d+ Уровень 3
^Ai4 = d+ Уровень 2
h = ['+' ; '-'] \ л + ( A13 = d ; A13, d = d+ Уровень 1
Il = '+' ; '-' A4 = ' T ' ; 'e' A12 = d Уровень 0
Уровень !
A15 ' d+; [A 14] ' . ' A 14 ; A7
'+ '; ' ' ;
)' A14) -
15 14 14 7
- d+; [d+]' '. '' d+; (A 14 ; An)' ( '\ ' ; 'e ')' (( '■ ,
- d+; d*' '. '' d+; (d+; [A14]' '. '' AM) ( '\ '; 'e ')' (( '+ ' ; '- '; )' d+) :
- d+; d*' '. ' ' d+; (d+; [d+] ' '. ' ' d+X ( '\ '; 'e ')' (( '+ ' ; '- '; X d+) =
- A + - A* ' ' A + - (A + - A* ' ' Л+Л i 'Л a '-u' • ' Ч Л+Л —
d+; d*' '. '' d+; (d+; d*' '. \ d+) ( '\ '; 'e ')' ([ '+' ; '- ']' d+) : - (d+; d*' '. '' d+)' [( '\ '; 'e ')' ([ '+ ' ; '- ']' d+)].
Рис. 1. Результат топологической сортировки графа зависимости нетерминалов и итоговое регулярное выражение
лагаются по уровням в соответствии с отношениями зависимости, определяемыми правилами грамматики.
Именно, если А ® ... В ... - правило, где А и В - нетерминалы, то соответствующие вершины графа зависимостей связываются дугой. Причём уровень вершины В должен быть больше уровня вершины А (на рис. 1 на уровне 0 находятся вершины А1, А4, А12; на уровне 1: А2 А13; на уровне 2: А14; на уровне 3: А3, А8, А9; ...; на уровне 8: А15).
Заметим, что на одном уровне располагаются нетерминалы, не зависимые друг от друга. В частности, на самом низком уровне (0) эта независимость абсолютная - все нетерминалы зависят от регулярных выражений в терминалах.
Подстановки значений нетерминалов в виде регулярных выражений в правые части правил исходной КС-грамматики производятся в порядке от минимального уровня (0) к максимальному (в примере, 8). На рис. 1 против нетерминалов показаны правые части соответствующих правил исходной грамматики и/или результаты вышеупомянутых подстановок с учётом исключения левосторонней рекурсии, если таковая встречается.
Например, преобразование правила для А и можно представить как на рис. 1. Предполагается, что регулярные операции объединения (;), конкатенации (,),рефлексивно-транзитивного замыкания (звёздочка Кли-ни) и транзитивного замыкания (плюс Клини) перечислены в порядке возрастания старшинства, причём две последних операции - унарные, имеют одинаковое старшинство. Скобки круглые и квадратные используются, как обычно, для явного указания по-
рядка вычислений, причём подвыражения в квадратных скобках трактуются как необязательные члены регулярного выражения, то есть содержат пустые цепочки.
За счёт средств оптимизации управляющих таблиц анализатора, имеющихся в TK SYNTAX, анализатор, построенный по регулярному выражению в предварительным виде, получается такой же, как тот, который строится по «изящному» виду, и идентичен детерминированному конечному автомату, минимальному по числу состояний (см. табл. 1).
Заметим, что по оптимизированным управляющим таблицам можно восстановить оптимизированную граф-схему, а по ней регулярное выражение в «изящном» виде. Но это уже другая тема.
4. ЗАКЛЮЧЕНИЕ
Как описано в [4], технология SYNTAX основана на аппроксимации входных языков контекстно чувствительными регулярными сплайнами. Это понятие введено по аналогии с понятием полиномиального сплайна в вычислительной математике. Там термин сплайн обозначает аппроксимацию функции на разных участках её области определения различными полиномами. В данной технологии термин регулярный сплайн обозначает кусочно-регулярную аппроксимацию входного языка. Это находит свое отражение и в том, что в качестве средства задания языков используются RBNF-грамматики, правила которых определяют терминальные порождения нетерминалов через регулярные выражения относительно символов всех алфавитов грамматики, и в том, что соответ-
Табл. 1
Состояние Цифра (d) \ или e + или — e
1 2 3
2 2 3 4 конечное
3 5 7
4 6
5 5 4 конечное
6 6 конечное
7 6
ствующий языковой процессор подобен множеству конечных автоматов, каждый из которых распознает свой фрагмент входной цепочки. Контекстная чувствительность обеспечивается тем, что дополнительно к алфавитам терминалов и нетерминалов в грамматику вводятся алфавиты контекстных - семантических и резольверных символов.
В данной статье рассмотрены некоторые виды эквивалентных преобразований трансляционных КС-грамматик, в настоящее время ещё не реализованных в ТК SYNTAX.
Описанный выше алгоритм 6 приведения КС-грамматик к регулярному выражению можно обобщить. Если разделить множество всех символов грамматики на неса-мовставленные нетерминалы и все другие (самовставленные нетерминалы, терминалы, контекстные символы) и обращаться с остальными символами как с терминалами, то после исключения всех несамовставленных нетерминалов мы получим правила для самовставленных нетерминалов, правые части которых будут регулярными выражениями относительно всех других символов грамматики.
Литература
1. Backus J. W., Bayer F.L., Green J., Katz C., Mc-Carthy., Naur P. (editor), Perlis A.J., Rutishshauser H., SamelsonK., VauquoisB., Wegstein J.H., van Wijngaarden A., Woodger M. Report on the Algorithmic Language ALGOL-6O. Numerische Mathematik, 2, № 2 (1960), P. 106-136.
2. Мартыненко Б.К. АЛГОЛ 68. Методы реализации / Под ред. Г. С. Цейтина. Гл. 1. Ленинград: ЛГУ, 1976. С. 14-70.
3. Пересмотренное сообщение об Алголе 68 / Под ред. А. ванВейнгаарден, Б. Майу, Дж. Пек, К. Костер и др. М., 1979.
4. Мартыгненко Б.К. Синтаксически управляемая обработка данных. 2-издание, исправленное и дополненное. СПб.: СПбГУ, 2004.
5. Мартыгненко Б.К. Языки и трансляции. СПб.: СПбГУ, 2004.
6. Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы. Построение и анализ. М.: МЦНМО, 1999.
Abstract
There is a variety of parser generators for formal languages being used during designing compilers for programming languages of different types. All of them are based on the use of context-free grammars with various restrictions, regular grammars being a special case. As a rule, they only check the requirements imposed on grammar, and issue diagnostic messages in case of their violation.
The purpose of this article is to describe the way of eliminating all nonterminals from the non-self-embedding grammar that gives the equivalent regular expression as result.
Keywords: context-free grammar, regular expression, equivalent conversion of grammar.
Мартыненко Борис Константинович, доктор физико-математических наук, профессор кафедрыы информатики математико-механического факультета СПбГУ, [email protected]
@ Наши авторы, 2012. Our authors, 2012