Работа с слабым анализатором
Методика разбора, используемая SMIE, не позволяет маркерам вести себя по-разному в разных контекстах. В большинстве языков программирования это проявляется в конфликтах приоритетов при преобразовании грамматики БНФ.
Иногда эти конфликты можно обойти, выразив грамматику немного по-другому. Например, для Modula-2 может показаться естественным иметь грамматику БНФ, которая выглядит так:
...
(inst ("IF" exp "THEN" insts "ELSE" insts "END")
("CASE" exp "OF" cases "END")
...)
(cases (cases "|" cases)
(caselabel ":" insts)
("ELSE" insts))
...
Но это создаст конфликты для "ELSE": с одной стороны, правило IF подразумевает (среди прочего), что "ELSE" = "END"; но с другой стороны, поскольку "ELSE" появляется внутри cases, которое появляется слева от "END", у нас также есть "ELSE" > "END". Мы можем решить конфликт, либо используя:
...
(inst ("IF" exp "THEN" insts "ELSE" insts "END")
("CASE" exp "OF" cases "END")
("CASE" exp "OF" cases "ELSE" insts "END")
...)
(cases (cases "|" cases) (caselabel ":" insts))
...
или
...
(inst ("IF" exp "THEN" else "END")
("CASE" exp "OF" cases "END")
...)
(else (insts "ELSE" insts))
(cases (cases "|" cases) (caselabel ":" insts) (else))
...
Переработка грамматики для решения конфликтов имеет свои недостатки, так как SMIE предполагает, что грамматика отражает логическую структуру кода, поэтому желательно держать БНФ ближе к задуманному дереву абстрактного синтаксиса.
В других случаях, после тщательного рассмотрения, вы можете прийти к выводу, что эти конфликты несерьезны и просто разрешить их с помощью аргумента resolvers для smie-bnf->prec2. Обычно это происходит потому, что грамматика просто неоднозначна: конфликт не влияет на набор программ, описываемых грамматикой, а только на способ разбора этих программ. Это обычно имеет место для разделителей и ассоциативных инфиксных операторов, где вы хотите добавить решатель, такой как '((assoc "|")). Другой случай, когда это может произойти, — это классическая проблема «висящего else», где вы будете использовать '((assoc
"else" "then")). Это также может произойти в случаях, когда конфликт реальный и его нельзя действительно разрешить, но вряд ли это создаст проблемы на практике.
Наконец, во многих случаях некоторые конфликты останутся, несмотря на все усилия по перестройке грамматики. Не отчаивайтесь: хотя анализатор не может стать более умным, вы можете сделать лексический анализатор настолько умным, насколько захотите. Таким образом, решение заключается в изучении маркеров, участвующих в конфликте, и разделении одного из этих маркеров на 2 (или более) разных маркера. Например, если грамматика должна различать два несовместимых использования маркера "begin", заставьте лексический анализатор возвращать разные маркеры (скажем, "begin-fun" и "begin-plain") в зависимости от того, какой тип "begin" он обнаружит. Это переносит работу по различению разных случаев на лексический анализатор, который поэтому должен рассматривать окружающий текст, чтобы найти специальные подсказки.
Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/SMIE-Tricks.html