Основываясь на след предоставленной Вами, я предполагаю, что грамматика выглядит следующим образом:
- E → T | E + T
- T → F | Т * Р
- F → Num
без указания в противном случае, yacc
использует LALR (1) алгоритм синтаксического анализа, который использует маркер опережающего просмотра, чтобы помочь разорвать связи при определении, следует ли выполнить сдвиг или уменьшить действие. В тот момент, когда вы указали, у парсера был T
на стеке с таблицей *
. Обратите внимание, что в грамматике нет способа, чтобы символ *
юридически следил за нетерминалом T
при любом деривации (формально *
∉ ПОСЛЕДУЮЩИЕ (T
)). Это означает, что синтаксический анализатор знает, что здесь не следует сокращать, так как LALR (1) никогда не будет уменьшаться на фоне, которое не находится в наборе FOLLOW для данного нетерминала.
Чтобы понять, как это работает, вы можете построить LALR (1) синтаксический анализатор для этой грамматики и посмотреть на определения, связанные с каждым элементом. В этом состоянии, будет два пункта, заполненный пункт формы
Т → Р
и следующий пункт сдвиг:.
Т → Т * Р
Элемент уменьшения не будет иметь *
в своем наборе взглядов (я считаю, что он просто будет $
), поэтому LALR (1) состояние парсер будет выглядеть следующим образом:.
T → F. [$]
T → T * F [$]
В результате, когда синтаксический анализатор видит *
, он не знает, как это сделать, и вместо этого сдвигается, так как в контрольном наборе для элемента уменьшения не содержится *
.
Строго говоря, набор FOLLOW используется для конструкции парсера SLR (1) - LALR (1) и LR (1) для каждого состояния, что для нескольких угловых случаев работает лучше. Но для этого примера SLR (1), LALR (1) и LR (1) будут абсолютно одинаковыми. –
@ChrisDodd Определенно. Анализаторы LALR (1) используют LA-множества, которые являются подмножествами наборов FOLLOW. В результате, если что-то не находится в наборе FOLLOW, оно, конечно же, не входит в набор заметок для парсера LALR (1), хотя обратное неверно. – templatetypedef