Spec-Zone.ru › Elisp

Работа с слабым анализатором

Методика разбора, используемая 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API