ISSN 0321-2653 ИЗВЕСТИЯ ВУЗОВ. СЕВЕРО-КАВКАЗСКИЙ РЕГИОН._ТЕХНИЧЕСКИЕ НАУКИ. 2017. № 3
ISSN 0321-2653 IZVESTIYA VUZOV. SEVERO-KAVKAZSKIIREGION. TECHNICAL SCIENCE. 2017. No 3
УДК 519.685.3 DOI: 10.17213/0321-2653-2017-3-43-48
АЛГОРИТМ СИНТАКСИЧЕСКОГО АНАЛИЗА КОНТЕКСТНО-СВОБОДНОЙ АППРОКСИМАЦИИ ДИНАМИЧЕСКИ
ФОРМИРУЕМОГО КОДА
© 2017 г. С.В. Григорьев, Д.А. Ковалев
Санкт-Петербургский государственный университет, г. Санкт-Петербург, Россия
SYNTAX ANALYSIS OF CONTEXT-FREE APPROXIMATION OF DYNAMICALLY GENERATED CODE
S. V. Grigor ev, D.A. Kovalev
Saint Petersburg University, Saint Petersburg, Russia
Григорьев Семён Вячеславович - канд. физ.-мат. наук, Grigor'ev Semen Vyacheslavovich - Candidate of Physical ст. преподаватель, Санкт-Петербургский государственный and Mathematical Sciences, Senior lecturer, Saint Petersburg
университет, г. Санкт-Петербург, Россия. E-mail: University, Saint Petersburg, Russia. E-mail:
semen.grigorev@jetbrains.com semen.grigorev@jetbrains.com
Ковалев Дмитрий Александрович - студент, Санкт- Kovalev Dmitry Aleksandrovich - student, Saint
Петербургский государственный университет, г. Санкт- Petersburg University, Saint Petersburg, Russia. E-mail:
Петербург, Россия. E-mail: dmitry.kovalev-m@ya.ru dmitry.kovalev-m@ya.ru
Многие программы в процессе работы формируют из строк исходный код на некотором языке программирования и передают его для исполнения в соответствующее окружение (пример - dynamic SQL). Для статической проверки корректности динамически формируемого выражения используются различные методы, одним из которых является синтаксический анализ регулярной аппроксимации множества значений такого выражения. Аппроксимация может содержать строки, не принадлежащие исходному множеству значений, в том числе синтаксически некорректные. Анализатор в данном случае сообщит об ошибках, которые на самом деле отсутствуют в выражении, генерируемом программой. В данной статье описан алгоритм синтаксического анализа более точной, чем регулярная, контекстно-свободной аппроксимации динамически формируемого выражения.
Ключевые слова: синтаксический анализ; динамически формируемый код; контекстно-свободные грамматики; GLL; GFG; dynamic SQL; DSQL.
Some programs can produce string representation of clauses in an external programming language and pass them for execution to the appropriate runtime component (e.g. dynamic SQL). There are various methods for static verification of a dynamically generated expression, one of which is to parse regular overapproximation ofpossible values of such an expression. The approximation may contain strings that do not belong to the initial set of values, including syntactically incorrect ones. The analyzer in this case will report errors that are actually missing in the expression generated by the program. This article describes a parsing algorithm that uses a more accurate, than regular, context-free approximation of a dynamically generated expression.
Keywords: parsing; dynamically generated expressions; context-free grammars; GLL; GFG; dynamic SQL; DSQL.
Введение
Современные языки программирования общего назначения поддерживают возможность работы со строковыми литералами, позволяя формировать из них выражения при помощи
строковых операций. Строковые выражения могут создаваться динамически, с использованием таких конструкций языка, как циклы и условные операторы. Данный подход широко используется, например, при формировании SQL-запросов к базам данных из программ,
ISSN 0321-2653 ИЗВЕСТИЯ ВУЗОВ. СЕВЕРО-КАВКАЗСКИМ РЕГИОН._ТЕХНИЧЕСКИЕ НАУКИ. 2017. № 3
ISSN 0321-2653 IZVESTIYA VUZOV. SEVERO-KAVKAZSKIIREGION. TECHNICAL SCIENCE. 2017. No 3
написанных на Java, C# и других высокоуровневых языках:
private void Example (bool cond) {
string columnName = cond ? "name" :
"address";
string queryString ="SELECT id, " + columnName + " FROM users";
Program.Executelmmediate(queryString); }
Недостаток такого метода генерации кода заключается в том, что формируемые выражения, с точки зрения компилятора, являются обычными строками и не проходят статические проверки на корректность и безопасность, что приводит к ошибкам времени исполнения и усложняет разработку и сопровождение системы. Включение обработки динамически формируемых строковых выражений в фазу статического анализа осложняется тем, что такие выражения, в общем случае, невозможно представить в виде линейного потока, который принимают на вход традиционные алгоритмы лексического / синтаксического анализа.
Для решения данной проблемы были разработаны различные методы статического анализа множества значений формируемого выражения. Как правило, язык, на котором написана исходная программа, тьюринг-полон, что делает невозможным проведение точного анализа. Поэтому распространенным подходом является построение некоторой аппроксимации рассматриваемого множества. Ряд предложенных ранее решений использует для анализа регулярную аппроксимацию: множество строк, генерируемых программой, аппроксимируется сверху регулярным языком, и анализатор работает с его компактным представлением, таким как регулярное выражение или конечный автомат.
В магистерской диссертации [1] был описан алгоритм, позволяющий проводить синтаксический анализ регулярной аппроксимации (конечного автомата, представленного в виде графа) множества значений динамически формируемого выражения. Основой для данного алгоритма служит алгоритм обобщенного синтаксического анализа Generalized LL (GLL [2]). Такой подход позволяет получать конечное представление леса разбора [3] корректных строк, содержащихся в аппроксимации множества значений выражения. Это представление может быть использовано для проведения более сложных видов статического анализа и для целей реинжиниринга.
В данной статье представлен алгоритм синтаксического анализа, который основан на
описанной выше модификации GLL, но работает с более точной, чем регулярная, контекстно-свободной аппроксимацией множества значений динамически формируемого выражения. Использование более точной аппроксимации позволяет снизить количество ложных синтаксических ошибок, возникающих в результате того, что аппроксимирующее множество может содержать строки, отсутствующие среди значений искомого выражения.
Обзор существующих работ
Под контекстно-свободной аппроксимацией подразумевается грамматика, описывающая контекстно-свободный язык, который содержит в качестве подмножества возможные значения динамически формируемого выражения. Идея использования такой аппроксимации была заимствована из существующих работ в области анализа динамически формируемого кода, краткий обзор которых будет приведен в данном разделе. Алгоритм синтаксического анализа регулярной аппроксимации на основе GLL был адаптирован нами для работы c графовым представлением КС-грамматик.
В качестве такого представления мы использовали Grammar Flow Graph (GFG, [4]). Описания оригинального алгоритма и GFG также включены в обзор.
Подходы к анализу динамически формируемых выражений. Существует два основных подхода, позволяющих проводить различные виды анализа динамически формируемого кода. Один из них основан на проверке включения языка, аппроксимирующего множество значений динамически формируемого выражения, в эталонный контекстно-свободный язык, заданный пользователем. Данный подход был реализован в инструментах Java String Analyzer (JSA [5]) и PHP String Analyzer (PHPSA [6]). Получение аппроксимации в них реализовано следующим образом: из исходного кода программы извлекается набор dataflow-уравнений, описывающих значения строковых переменных, которые участвуют в генерации выражения. Эти уравнения затем интерпретируются как контекстно-свободная грамматика, описывающая аппроксимирующий язык. PHPSA работает непосредственно с такой грамматикой, используя эвристики (так как в общем случае задача о включении КС-языков неразрешима [7]); в JSA контекстно-свободный язык дополнительно аппроксимируется регулярным.
ISSN 0321-2653 IZVESTIYA VUZOV. SEVERO-KAVKAZSKIIREGION.
TECHNICAL SCIENCE. 2017. No 3
Другой подход заключается в проведении синтаксического анализа аппроксимации множества значений выражения. Такое решение позволяет не просто ответить на вопрос о включении языков, но и реализовать дополнительную функциональность, такую как вычисление семантики или рефакторинг. К методам, основанным на данном подходе, можно отнести алгоритмы синтаксического анализа регулярной аппроксимации на базе семейства GLR [8, 9] и GLL-алгоритмов [1], а также алгоритм абстрактного синтаксического анализа [10], позволяющий совместить синтаксический анализ с решением dataflow-уравнений, получаемых при помощи PHPSA.
Синтаксический анализ регулярной аппроксимации на основе GLL. Generalized LL (GLL [2]) - алгоритм синтаксического анализа, позволяющий, в отличие от классических LL-анализаторов, работать с произвольными контекстно-свободными грамматиками. При этом GLL сохраняет такие важные свойства алгоритмов нисходящего разбора, как интуитивная связь с грамматикой и простота отладки и диагностики ошибок.
Основной идеей GLL является использование дескрипторов, позволяющих полностью описывать состояние анализатора в текущий момент времени.
Определение 1. Дескриптор - это четверка (L, u, i, N), где L - текущая позиция в грамматике вида А ^-a^ß; u - текущая вершина Graph Structured Stack (GSS [11]); i - позиция во входном потоке; N - построенный на данный момент узел дерева вывода.
В процессе работы поддерживается глобальная очередь дескрипторов. В начале каждого шага исполнения алгоритм берет следующий в очереди дескриптор и производит действия в зависимости от позиции в грамматике и текущего входного символа. Для обработки неоднозначностей в грамматике алгоритм добавляет дескрипторы для каждого возможного пути анализа в конец очереди. Результат работы алгоритма - множество деревьев разбора строки (лес разбора) - представляется в виде Shared Packed Parse Forest (SPPF [3]).
В рамках магистерской диссертации [1] на базе GLL был разработан алгоритм для синтаксического анализа регулярной аппроксимации множества значений динамически формируемого выражения. Под регулярной аппроксимацией здесь понимается детерминированный конечный
автомат над алфавитом токенов (рис. 1 с) Оригинальный GLL-алгоритм был модифицирован для работы с нелинейным входом. Дескрипторы нового алгоритма хранят номер вершины входного графа вместо позиции в линейном потоке. Также на шаге исполнения просматривается не единственный входной символ, а все ребра, исходящие из текущей вершины. Псевдокод данного алгоритма можно увидеть в прил. A. Основная логика работы представлена в функциях dispatcher (извлечение дескрипторов из очереди) и processing (анализ исходящих из текущей вершины ребер).
LBR RBR A
expr = "a"
while ( ... ) s' ::= s
expr ="(" + expr + s ::= LBR s EOF
")" RBR
print expr s ::= A
a be
Рис. 1. Исходный код и примеры аппроксимаций a - исходный код; b - КС-аппроксимация; с - регулярная аппроксимация / Fig. 1. Source code and approximations:
a - source code; b - CF-approximation; c - regular approximation
Grammar Flow Graph. Grammar Flow Graph (GFG [4]) - связный помеченный граф, узлы которого соответствуют позициям в грамматике (А ^-a^ß). Различают следующие типы узлов (X - нетерминальный символ, t - терминальный):
А ^aXß A - call;
А ^aX^ßA - return;
А ^•a - entry;
А - exit;
А ^a^tß scan.
Для обозначения подграфа, представляющего продукции, имеющие в левой части нетерминал A, дополнительно используются узлы с метками .A (start-узел) и A. (end-узел) (рис. 2). Подробное описание GFG можно найти в оригинальной статье.
Приведем определение выводимости строки в грамматике в терминах GFG. Для этого нам потребуется также определить понятие сбалансированного пути.
Определение 2. Сбалансированным путем в GFG называется путь, подпоследовательность call- и return-узлов которого сбалансирована.
Определение 3. Строка w выводима в грамматике, если в GFG существует сбалансированный путь из узла .s в узел s. (здесь s - старто-
ISSN 0321-2653 IZVESTIYA VUZOV. SEVERO-KAVKAZSKIIREGION.
TECHNICAL SCIENCE. 2017. No 3
вый нетерминал грамматики), и ^ может быть получена конкатенацией меток на ребрах, содержащихся в данном пути.
s- >.LBR s RBR
6 1 LBR r
s- >LBR .s RBR
8
s- >LBR s . RBR
9 1 RBR г
s- >LBR s RBR.
тов разбора строки). Вместо этого множество CR-стеков, по аналогии с основным стеком GLL-анализатора, представляется в виде GSS. Пример можно увидеть на рис. 3. GSS позволяет хранить только одну копию общих префиксов нескольких стеков, каждый путь в нем соответствует отдельному CR-стеку.
$ 3 7 1 5 / 1 <— 5
$ 3 7 4
N
4
Рис. 2. GFG для грамматики 1b / Fig. 2. GFG for grammar 1b
Синтаксический анализ контекстно-свободной аппроксимации
Наш алгоритм принимает на вход управляющие таблицы LL-анализа, построенные по эталонной грамматике, и GFG, который является представлением КС-грамматики - аппроксимации множества значений выражения. Мы переиспользуем основные структуры данных и функции описанного ранее алгоритма анализа регулярной аппроксимации на основе GLL, расширяя данный подход для корректной обработки GFG.
Алгоритм последовательно обходит узлы GFG, производя синтаксический анализ порождаемых им строк. Для правильного построения таких строк, согласно определению выводимости строки в GFG-грамматике, для каждого просматриваемого пути необходимо поддерживать баланс call- и return-узлов. То есть при прохождении пути алгоритм должен манипулировать дополнительным стеком (назовем его CR-стеком). При достижении call-узла в стек добавляется номер return-узла, соответствующего ему; при достижении end-узла необходимо снять со стека номер return-узла и продолжить обход из него.
Для экономии памяти мы не храним CR-стек для каждой из текущих ветвей работы алгоритма (напомним, что GLL-алгоритм может одновременно рассматривать несколько вариан-
Рис. 3. Структурированный в виде графа стек / Fig. 3. Graph structured stack
Для хранения указателя на текущую вершину стека мы добавили в дескрипторы дополнительное поле. Таким образом, дескриптором в нашем алгоритме называется пятерка вида (L, и, i, N, 5), где i - номер вершины GFG, s - указатель на вершину CR-стека в GSS, остальные поля аналогичны тем, которые представлены в дескрипторах оригинального GLL-алгоритма.
Другой особенностью работы с GFG является то, что он, в отличие от регулярной аппроксимации - детерминированного конечного автомата, допускает возможность неоднозначного выбора пути обхода. Подобная ситуация возникает при наличии в исходной грамматике нескольких продукций, содержащих в левой части одинаковый нетерминал. Например, GFG на рис. 2 содержит start-узел под номером 3, из которого выходит два ребра с пустой меткой (аналог е-переходов в конечном автомате).
Механизм дескрипторов позволяет решать проблему недетерминированного выбора пути: для каждого из возможных вариантов создается отдельный дескриптор, который добавляется в очередь исполнения. Вернемся к примеру с узлом 3 на рис. 2. Пусть в текущий момент времени мы имеем дескриптор (L, и, i, N, s). При рассмотрении ребер, выходящих из узла 3, будут созданы дескрипторы (L, и, 4, N, s) и (L, и, 5, N, s). Если ранее такие дескрипторы не создавались (для контроля за этим в GLL поддерживается глобальное множество создаваемых дескрипторов), они будут добавлены в очередь.
Функции dispatcher и add (проверка и добавление в очередь дескриптора) алгоритма анализа регулярной аппроксимации были незначительно изменены нами для работы с расширенными дескрипторами. Функция processing и методы для работы с основным стеком и построения SPPF переиспользованы без изменений. Обработка start/call/exit-узлов и контроль за
$
7
3
ISSN 0321-2653 IZVESTIYA VUZOV. SEVERO-KAVKAZSKIIREGION.
TECHNICAL SCIENCE. 2017. No 3
состоянием CR-стеков были реализованы во вспомогательной функции closure, псевдокод которой приведен ниже. Она исполняется перед вызовами dispatcher и processing, производя рекурсивный обход GFG до тех пор, пока не встретит start- или scan-узел. При достижении start-узла создаются дескрипторы для каждого из возможных путей, и управление переходит к dispatcher; scan-узел обрабатывается функцией processing так же, как и вершина конечного автомата в оригинальном алгоритме
procedure CLOSURE tuple (CL, Cu, Ci, CN, CS) is the current state of the analysis process v ^ current GFG vertex if (v is not a final vertex) then switch VertexType(v) do case Start
for (e E v. OutputEdges) do
ADD (CL, Cu, e. Target, CN, CS) case Call(r) where r is matching return vertex
add CR-vertex w labeled r and edge from w to CS CS ^ w
CI ^ target vertex of v. OutputEdge CLOSURE case Exit
let w be the target vertex of v. OutputEdge if (w is not a final vertex) then CI ^ CS.Label
CS ^ predecessor of CS in CR-stack CLOSURE else CI ^ w case _ return
Результатом работы алгоритма является SPPF, представляющий набор деревьев разбора строк, порождаемых GFG и одновременно с этим выводимых в эталонной грамматике. Отметим, что SPPF может быть построен не всегда, так как класс КС-языков не замкнут относительно пересечения.
Заключение
Описанный алгоритм был реализован на языке программирования F# в рамках проекта YaccConstructor. Исходный код доступен по ссылке:
https://github.com/YaccConstructor/YaccConstructor.
Результаты проведенных синтетических тестов показали, что использование контекстно-свободной аппроксимации позволяет увеличить точность синтаксического анализа динамически формируемого кода. Так, для примера с рис. 1 a количество ложных синтаксических ошибок снизилось с 7 до 2. В дальнейшем планируется провести апробацию на реальных данных.
Задача синтаксического анализа графов также возникает в области биоинформатики [1]. Предполагается, что наш алгоритм может увели-
чить производительность анализа метагеномных сборок за счет использования более компактного представления входных данных: из конечного автомата, описывающего сборку, может быть извлечена КС-грамматика [12], графовое представление которой содержит меньше состояний.
Приложение А
Псевдокод модифицированного GLL
function DISPATCHER
if R. Count ф 0 then
(CL, Cu, Ci, CN) ^ R.Dequeue() CR ^ $
dispatch ^ false else .stop ^ true
function PROCESSING dispatch ^ true switch CL do
case (X ^ ахв) where x is terminal
for all {e | e E input.OutEdges(Ci), e.Tag = x} do CN' ^ CN, CR' ^ CR if CN' = $ then
CN' ^ getNodeT(e)
else
CR' ^ getNodeT(e) CL ^ (X ^ ах в) if CR' ф $ then
CN' ^ getNodeP(CL, CN', CR') ADD (CL, Cu, e.Target, CN') case (X ^ ахв) where x is nonterminal
Cu <- CREATE ((X^ax-fi), Cu, Ci, CN)
slots <— (J pTable[x] [e.Token]
eeCi.OutEdges
for all L E slots do
ADD (L, Cu, Ci, $) case (X ^ а•)
POP (Cu, Ci, CN)
function CONTROL
while not stop do
if dispatch then DISPATCHER else PROCESSING
function ADD (L, u, i, N)
if (L, u, i, N) £ U then U.Add (L, u, i, N) R.Add (L, u, i, N)
function POP (u, i, z) if u ф u0 then
P.Add (u, z)
for all (a, v) E v. OutEdges do y ^ getNodeP (u.L, a, z) ADD (u.L, v, i, y)
function CREATE (L, u, i, a)
if (L, i) £ GSS.Nodes then
GSS.Nodes.Add (L, i) v ^ GSS.Nodes. Get (L, i) if (v, a, u) £ GSS.Edges then GSS.Edges.Add (v, a, u) for all (v, z) E P do
y ^ getNodeP (L, a, z) (_, _, k) ^ z.Lbl
ISSN 0321-2653 IZVESTIYA VUZOV. SEVERO-KAVKAZSKIIREGION.
ADD (L, u, k, y)
return v
function getNodeT (e)
tag ^ e.Tag, src ^ e.Source, tr ^ e.Target if (tag, src, tr) £ SPPF.Nodes then
SPPF.Nodes.Add (tag, src, tr) return SPPF.Nodes. Get (tag, src, tr)
function getNodeP ((X ^ ml • ml), a, z)
if ml is terminal or non-nullable nonterminal and ml Ф s then return z
else
if ml = s then t ^ X else h ^ (X ^ ml • ml) (q, k, i) ^ z.Lbl if a Ф $ then
(s, j, k) ^ a.Lbl
y ^ findOrCreate SPPF.Nodes (n.Lbl = (t, i, j)) if y does not have a child labeled (X ^ ml ■ ml) then y' ^ newPackedNode(a, z) y.Chld.Add y' return y
else
y ^ findOrCreate SPPF.Nodes (n.Lbl = (t, k, i)) if y does not have a child labeled (X ^ ml ml)then y' ^ newPackedNode (z) y.Chld.Add y' return y return SPPF.Nodes. Get (x, i, h)
Литература
1. Рагозина А.К. Ослабленный синтаксический анализ динамически формируемых выражений на основе алгоритма GLL: магистерская дис. / СПбГУ. СПб., 2016.
TECHNICAL SCIENCE. 2017. No 3
2. Scott E., Johnstone A. Gll parsing. Electronic Notes in Theoretical Computer Science, 253(7), 2009.
3. Rekers J.G. Parser generation for interactive environments. PhD thesis, Citeseer, 1992.
4. Pingali K., Bilardi G. A Graphical Model for Context-Free Grammar Parsing. Springer Berlin Heidelberg, Berlin, Heidelberg, 2015 p. 3-27.
5. Christensen A.S., M0ller A., Schwartzbach M.I. Precise analysis of string expressions. Proc. 10th International Static Analysis Symposium (SAS), Springer-Verlag: Berlin, June 2003 p. 1-18.
6. Minamide Y. Static approximation of dynamically generated web pages. In: Proceedings of the 14th International Conference on World Wide Web, WWW '05, ACM, 2005. p. 432-441.
7. Asveld P.R.J., Nijholt A. The inclusion problem for some subclasses of context-free languages. Theor. Comput. Sci., 230(1-2):247-256, 1999.
8. Annamaa A., Breslav A., Kabanov J., Vene V. An interactive tool for analyzing embedded sql queries. Programming Languages and Systems, Springer: Berlin, 2010. p. 131-138.
9. Verbitskaia E., Grigorev S., Avdyukhin D. Relaxed Parsing of Regular Approximations of String-Embedded Languages, Springer International Publishing, Cham, 2016. p. 291-302.
10. Doh K.-G., Kim H., Schmidt D.A. Abstract LR-Parsing, Springer Berlin Heidelberg, Berlin, Heidelberg, 2011. p. 90- 109.
11. Tomita M. An efficient context-free parsing algorithm for natural languages. In: Proceedings of the 9th International Joint Conference on Artificial Intelligence. Vol. 2, IJCAI'85, San Francisco, CA, USA. Morgan Kaufmann Publishers Inc. p. 756-764. 1985.
12. Nevill-Manning C.G., Witten I.H. Identifying hierarchical structure in sequences: A linear-time algorithm. J. Artif. Int. Res., Sept. 1997. 7(1):67-82.
References
1. Ragozina A. K. Oslablennyi sintaksicheskii analiz dinamicheski formiruemykh vyrazhenii na osnove algoritma GLL. Magisterskaya diss. [Gll-based relaxed parsing of dynamically generated code. Master's thesis]. St. Petersburg, SPbGU Publ., 2016.
2. Scott E. and Johnstone A. Gll parsing. Electronic Notes in Theoretical Computer Science, 2009, 253(7).
3. Rekers J. G. Parser generation for interactive environments. PhD thesis, Citeseer, 1992.
4. Pingali K. and Bilardi G. A Graphical Model for Context-Free Grammar Parsing, Springer Berlin Heidelberg, Berlin, Heidelberg, 2015. Pp. 3-27.
5. Christensen A. S., Maller A., and Schwartzbach M. I. Precise analysis of string expressions. Proc. 10th International Static Analysis Symposium (SAS), Springer-Verlag: Berlin, June 2003. Pp. 1 -18.
6. Minamide Y. Static approximation of dynamically generated web pages. In Proceedings of the 14th International Conference on World Wide Web, WWW '05, ACM, 2005. Pp. 432-441.
7. Asveld P. R. J. and Nijholt A. The inclusion problem for some subclasses of context-free languages. Theor. Comput. Sci., 1999, 230(1-2):247-256.
8. Annamaa A., Breslav A., Kabanov J., and Vene V. An interactive tool for analyzing embedded sql queries. Programming Languages and Systems, Springer: Berlin, 2010. Pp. 131 -138.
9. Verbitskaia E., Grigorev S., and Avdyukhin D. Relaxed Parsing of Regular Approximations of String-Embedded Languages, Springer International Publishing, Cham, 2016. Pp. 291-302.
10. Doh K.-G., Kim H., and Schmidt D. A. Abstract LR-Parsing, Springer Berlin Heidelberg, Berlin, Heidelberg, 2011. Pp. 90- 109.
11. Tomita M. An efficient context-free parsing algorithm for natural languages. In Proceedings of the 9th International Joint Conference on Artificial Intelligence - Volume 2, IJCAI'85, San Francisco, CA, USA, 1985. Morgan Kaufmann Publishers Inc. Pp. 756-764.
12. Nevill-Manning C. G. and Witten I. H. Identifying hierarchical structure in sequences: A linear-time algorithm. J. Artif. Int. Res., 1997. 7(1):67-82, Sept.
Поступила в редакцию /Receive 03 мая 2017 г. /May 03, 2017