Bottom-Up-Parser: когда применять какое правило редукции?

Возьмем следующую контекстно-свободную грамматику:

G = ( {Sum, Product, Number}, {decimal representations of numbers, +, *}, P, Sum)

Быть П:

Sum → Sum + Product
Sum → Product
Product → Product * Number
Product → Number
Number → decimal representation of a number

Я пытаюсь анализировать выражения, созданные этой грамматикой, с помощью восходящего синтаксического анализатора и упреждающего буфера (LAB) длины 1 (который предположительно должен обходиться без догадок и обратного отслеживания).

Теперь, при наличии стека и LAB, часто есть несколько вариантов того, как уменьшить стек, уменьшить ли его вообще или протолкнуть другой токен.

В настоящее время я использую это дерево решений:

Если любые верхние n токенов стека плюс LAB являются началом правой части правила, я помещаю следующий токен в стек.

В противном случае я уменьшаю максимальное количество токенов в верхней части стека. т.е. если можно уменьшить самый верхний элемент и в то же время можно уменьшить три самых верхних элемента, я делаю последнее.

Если такое сокращение невозможно, я помещаю в стек еще один токен.

Промыть и повторить.

Это, кажется (!) работает, но требует ужасного поиска правил, поиска совпадающих префиксов и т. д. Это никак не может работать в O(NM).

Каков стандартный (и, возможно, единственно разумный) подход к принятию решения о сокращении или проталкивании (смещении), и в случае сокращения, какое сокращение применить?

Заранее спасибо за ваши комментарии и ответы.


person Hyperboreus    schedule 14.09.2013    source источник


Ответы (1)


Самый простой восходящий подход к анализу грамматик, подобных вашей (в основном, грамматик выражений), — это анализ приоритета операторов.

Напомним, что синтаксический анализ снизу вверх включает в себя построение дерева синтаксического анализа слева направо снизу. Другими словами, в любой момент во время синтаксического анализа у нас есть частично собранное дерево с только терминальными символами справа от того места, где мы читаем, и комбинацией терминалов и нетерминалов слева («префикс»). . Единственная возможная редукция относится к суффиксу префикса; если редукция не применяется, мы должны иметь возможность сместить терминал с входа на префикс.

Операторная грамматика имеет свойство, заключающееся в том, что ни в одной постановке никогда не бывает двух последовательных нетерминалов. Следовательно, при разборе операторной грамматики снизу вверх либо последний символ в префиксе является терминалом, либо предпоследний символ равен единице. (Они оба могут быть.)

Анализатор приоритета операторов по существу слеп к нетерминалам; он просто не различает их. Таким образом, у вас не может быть двух продукций, правые части которых содержат точно такую ​​же последовательность терминалов, потому что синтаксический анализатор op-prec не будет знать, какую из этих двух продукций применить. (Это традиционная точка зрения. На самом деле ее можно немного расширить, чтобы вы могли иметь две продукции с одними и теми же терминалами, при условии, что нетерминалы находятся в разных местах. Это позволяет использовать грамматики, которые имеют унарные операторы -, например, поскольку правые стороны <non-terminal> - <non-terminal> и - <non-terminal> можно различить, не зная имен нетерминалов, только их присутствие.

Другое требование состоит в том, что вы должны иметь возможность строить отношения приоритета между терминалами. Точнее, мы определяем три отношения предшествования, обычно обозначаемые как , ·> и ·=· (или некоторые типографские вариации на эту тему), и настаиваем на том, что для любых двух терминалов x и y истинно только одно из отношений x ·> y, x ·=· y и x <· y. .

Грубо говоря, < и > в отношениях соответствуют краям производства. Другими словами, если x <· y, это означает, что за x может следовать нетерминал с продукцией, первый терминал которой y. Точно так же x ·> y означает, что y может следовать за нетерминалом с продуктом, последним терминалом которого является x. И x ·=· y означает, что есть некоторая правая часть, где x и y являются последовательными терминалами в этом порядке (возможно, с промежуточным нетерминалом).

Если ограничение на одно отношение истинно, то мы можем разобрать его следующим образом:

Пусть x будет последним терминалом в префиксе (то есть либо последним, либо предпоследним символом), а y будет упреждающим символом, который должен быть терминалом. Если x ·> y то уменьшаем, и повторяем правило. В противном случае мы сдвигаем y на префикс.

Для того, чтобы уменьшить, нам нужно найти начало производства. Мы перемещаемся назад по префиксу, сравнивая последовательные терминалы (все они должны иметь отношения или ·=·), пока не найдем один с отношением . Тогда терминалы между и ·> будут правой стороной искомого произведения, и мы можем вставить нетерминалы в правую сторону, как указано.

Нет гарантии, что будет соответствующая постановка; если нет, синтаксический анализ завершается неудачно. Но если на входе есть правильное предложение и если грамматика является грамматикой с приоритетом операторов, то мы сможем найти правильную продукцию для редукции.

Обратите внимание, что обычно очень легко найти производство, потому что большинство производств имеют только один (<non-terminal> * <non-terminal>) или два (( <non-terminal> )) терминала. Наивная реализация может просто объединить терминалы в строку и использовать ее в качестве ключа в хеш-таблице.

Классической реализацией синтаксического анализа приоритета операторов является так называемый «алгоритм маневровой станции», разработанный Эдсгером Дейкстрой. В этом алгоритме отношения предшествования моделируются с помощью двух функций, левого приоритета и правого приоритета, которые отображают терминалы в целые числа, так что x <· y истинно, только если right-precedence(x) < left-precedence(y) (и аналогично для других операторов). Не всегда возможно найти такие отображения, и отображения являются прикрытием реальных отношений предшествования, потому что часто бывают пары терминалов, для которых не применяется отношение предшествования. Тем не менее, часто бывает так, что эти отображения можно найти, и почти всегда это имеет место для простых грамматик выражений.

Надеюсь, этого достаточно, чтобы вы начали. Я рекомендую вам на самом деле прочитать некоторые тексты о анализе снизу вверх, потому что я думаю, что уже написал слишком много для SO-ответа, и я еще не включил ни одной строки кода. :)

person rici    schedule 14.09.2013
comment
Спасибо Ричи. Позвольте мне переварить ваш ответ. - person Hyperboreus; 14.09.2013
comment
Применим ли этот подход также к грамматике в этом вопросе: .com/questions/18797248/? Боюсь, не из-за TupleItem → TupleItem TupleItem... - person Hyperboreus; 14.09.2013
comment
@Hyperboreus: я ответил на ваш другой вопрос операторной грамматикой. Синтаксический анализ op-prec может фактически иметь дело с RHS регулярных выражений напрямую, и для грамматики кортежа можно построить очень простой анализатор op-prec. - person rici; 14.09.2013
comment
Muchísimas gracias, плотский. - person Hyperboreus; 14.09.2013
comment
В грамматике, которую я дал в своем вопросе, какое отношение (, ·=·, ·>) существует между терминалами decimal и +, а также между + и decimal? Если я правильно прочитал, вообще никак. - person Hyperboreus; 16.09.2013
comment
+ <· decimal и decimal ·> +, потому что decimal может быть первым терминалом и последним терминалом, соответственно, Number. Такие терминалы, как decimal, которые появляются только как единичные продукты, могут быть обработаны ускоренным способом в соответствии с исходным алгоритмом сортировочной станции, но они прекрасно работают и с полным алгоритмом. - person rici; 16.09.2013
comment
@Hyperboreus: есть формальное определение и рабочий пример, очень похожий на вашу грамматику внизу страницы 2 и вверху страницы 3 этой статьи: home.deib.polimi.it/pradella/papers/floydpar.pdf. Обратите внимание, что определение Lg и Rg включает в себя =›*, а не только =›. - person rici; 16.09.2013