Я думаю, что эти действия печати в правилах S будут иметь обратный эффект, поскольку S. может происходить несколько раз.
S может генерировать SaS. Но каждый из этих S также может генерировать SaS.
По сути, если вы создаете печатное представление как семантическое свойство, вы можете выполнить печать вне грамматики только после того, как она будет полностью оценена, гарантируя, что это произойдет только один раз.
Это можно показать, введя псевдостартовый символ X. S уменьшается до X только один раз, и поэтому печать происходит только один раз, вытаскивая окончательный val
с верхнего уровня S
.
X -> S { print S.val } // print the top-level S's val, just once.
Другой подход заключается в том, чтобы иметь по-настоящему синтаксически-ориентированную печать, при которой побочный эффект печати возникает по мере сокращения синтаксического анализа. Например. Yacc-подобное встроенное правило среди правых символов:
S -> S1 a { print a.lexeme } S2 { /* other semantic rules go here */ }
В каждом правиле, которое распознает терминал, распечатайте терминал, как только он будет распознан. Итак, здесь мы знаем, что сокращение S1 приводит к печати всех его терминалов (по аналогичным правилам по всей грамматике). Затем мы распознаем a
и печатаем его, а затем S2 распознается и уменьшается, вызывая печать всех его терминалов. Вы можете признать, что это очень похоже на обход дерева по порядку.