В информатике парсер Эрли — это алгоритм для разбора строк , принадлежащих заданному контекстно-свободному языку , хотя (в зависимости от варианта) он может испытывать проблемы с некоторыми грамматиками, допускающими значение null. [1] Алгоритм, названный в честь его изобретателя Джея Эрли , представляет собой анализатор диаграмм , использующий динамическое программирование ; он в основном используется для разбора в компьютерной лингвистике . Впервые он был представлен в его диссертации [2] в 1968 году (и позже появился в сокращенной, более разборчивой форме в журнале [3] ).
Парсеры Early привлекательны тем, что они могут анализировать все контекстно-свободные языки, в отличие от парсеров LR и парсеров LL , которые чаще используются в компиляторах , но могут обрабатывать только ограниченные классы языков. Парсер Earley выполняется за кубическое время в общем случае , где n — длина анализируемой строки, квадратичное время для недвусмысленных грамматик [ 4] и линейное время для всех детерминированных контекстно-свободных грамматик . Он работает особенно хорошо, когда правила написаны леворекурсивно .
Следующий алгоритм описывает распознаватель Эрли. Распознаватель может быть модифицирован для создания дерева разбора по мере распознавания, и таким образом может быть превращен в синтаксический анализатор.
В следующих описаниях α, β и γ представляют любую строку терминалов /нетерминалов (включая пустую строку ), X и Y представляют отдельные нетерминалы, а a представляет терминальный символ.
Алгоритм Эрли — это алгоритм динамического программирования сверху вниз . В дальнейшем мы используем точечную нотацию Эрли: если задана продукция X → αβ, то нотация X → α • β представляет собой условие, в котором α уже проанализировано, а β ожидается.
Входная позиция 0 — это позиция до ввода. Входная позиция n — это позиция после принятия n-го токена. (Неформально входные позиции можно рассматривать как местоположения на границах токенов .) Для каждой входной позиции синтаксический анализатор генерирует набор состояний . Каждое состояние — это кортеж (X → α • β, i ), состоящий из
(Оригинальный алгоритм Эрли включал в себя упреждающий просмотр состояния; более поздние исследования показали, что это не оказывает практического влияния на эффективность анализа, и впоследствии от него отказались в большинстве реализаций.)
Состояние завершается, когда его текущая позиция является последней позицией правой стороны продукции, то есть когда справа от точки • в визуальном представлении состояния нет символа.
Состояние, заданное в позиции ввода k, называется S( k ). Анализатор засеивается S(0), состоящим только из правила верхнего уровня. Затем анализатор многократно выполняет три операции: прогнозирование , сканирование и завершение .
Дублирующиеся состояния не добавляются в набор состояний, только новые. Эти три операции повторяются до тех пор, пока в набор не будет добавлено ни одного нового состояния. Набор обычно реализуется как очередь состояний для обработки, причем операция, которая должна быть выполнена, зависит от того, какое это состояние.
Алгоритм принимает, если (X → γ •, 0) попадает в S( n ), где (X → γ) — правило верхнего уровня, а n — длина входных данных, в противном случае он отклоняется.
Адаптировано из книги «Обработка речи и языка» [5] Дэниела Джурафски и Джеймса Х. Мартина,
ОБЪЯВИТЬ МАССИВ S ; функция INIT ( слова ) S ← CREATE_ARRAY ( ДЛИНА ( слова ) + 1 ) для k ← от 0 до ДЛИНА ( слова ) сделать S [ k ] ← EMPTY_ORDERED_SET function EARLEY_PARSE ( words , grammar ) INIT ( words ) ADD_TO_SET ( ( γ → • S , 0 ) , S [ 0 ]) for k ← от 0 до LENGTH ( words ) do для каждого состояния в S [ k ] do // S[k] может расширяться во время этого цикла if not FINISHED ( state ) then if NEXT_ELEMENT_OF ( state ) is nonterminal then PREDICTOR ( state , k , grammar ) // non_terminal else do SCANNER ( state , k , words ) // terminal else do COMPLETER ( state , k ) end end return chart процедура PREDICTOR (( A → α• B β , j ) , k , грамматика ) для каждого ( B → γ ) в GRAMMAR_RULES_FOR ( B , грамматика ) сделать ADD_TO_SET (( B → •γ , k ) , S [ k ]) конец процедура СКАНЕР (( A → α• a β , j ) , k , слова ) если j < ДЛИНА ( слова ) и a ⊂ ЧАСТИ_РЕЧИ ( слова [ k ]) тогда ДОБАВИТЬ_В_НАБОР (( A → α a •β , j ) , S [ k + 1 ]) конец процедура COMPLETER (( B → γ• , x ) , k ) для каждого ( A → α• B β , j ) в S [ x ] сделать ADD_TO_SET (( A → α B •β , j ) , S [ k ]) конец
Рассмотрим следующую простую грамматику арифметических выражений:
<P> ::= <S> # начальное правило<С> ::= <С> "+" <М> | <М><М> ::= <М> "*" <Т> | <Т><Т> ::= "1" | "2" | "3" | "4"
С вводом:
2 + 3 * 4
Вот последовательность наборов состояний:
Состояние (P → S •, 0) представляет собой завершенный разбор. Это состояние также появляется в S(3) и S(1), которые являются полными предложениями.
Диссертация Эрли [6] кратко описывает алгоритм построения деревьев разбора путем добавления набора указателей из каждого нетерминала в элементе Эрли обратно к элементам, которые привели к его распознаванию. Но Томита заметил [7] , что это не учитывает отношения между символами, поэтому, если мы рассмотрим грамматику S → SS | b и строку bbb, она только отметит, что каждый S может соответствовать одному или двум b, и, таким образом, производит ложные выводы для bb и bbbb, а также два правильных вывода для bbb.
Другой метод [8] заключается в построении леса разбора по мере продвижения, дополняя каждый элемент Earley указателем на узел общего упакованного леса разбора (SPPF), помеченный тройкой (s, i, j), где s — это символ или элемент LR(0) (правило производства с точкой), а i и j задают раздел входной строки, полученный этим узлом. Содержимое узла — это либо пара дочерних указателей, дающих один вывод, либо список «упакованных» узлов, каждый из которых содержит пару указателей и представляет один вывод. Узлы SPPF уникальны (существует только один с заданной меткой), но могут содержать более одного вывода для неоднозначных разборов. Таким образом, даже если операция не добавляет элемент Earley (потому что он уже существует), она все равно может добавить вывод в лес разбора элемента.
Узлы SPPF никогда не помечаются завершенным элементом LR(0): вместо этого они помечаются символом, который создается, так что все производные объединяются под одним узлом независимо от того, из какого альтернативного производства они происходят.
Филипп Маклин и Р. Найджел Хорспул в своей статье «Более быстрый синтаксический анализатор Эрли» объединяют синтаксический анализ Эрли с синтаксическим анализом LR и достигают улучшения на порядок.