Движок перезаписи — это совершенно гениальное изобретение Джона Бранта и Дона Робертса, которое было представлено вместе с рефакторингом браузеров (см.) «Инструмент рефакторинга для Smalltalk», 1997 г.). Это дает нам удивительные возможности сопоставления и перезаписи на уровне AST.
Но давайте будем честными: сколько людей на самом деле помнят его синтаксис?
Даже самые простые правила переписывания — скажем, замена устаревшего сообщения новым — обычно заставляют меня искать примеры. Во время этого проекта я провел много времени в движке перезаписи, и даже сейчас не могу точно вспомнить точный синтаксис.
Есть ли такое?
``@receiver isNil ifTrue: ``@nilBlock -> ``@receiver ifNil: ``@nilBlock
Или, может быть, с одинарными обратными кавычками?
`@receiver isNil ifTrue: `@nilBlock -> `@receiver ifNil: `@nilBlock
Фактически, Оба Версии работают, но к целевому узлу применяются разные фильтры. Постарайтесь вспомнить, какой именно.
И это только начало.
Знаете ли вы, что вы можете использовать подстановочный знак части селекторов?
`@receiver `anyKeywordPart: `@arg1 staticPart: `@arg2
Вы можете переименовать ключевые слова, используя:
`@receiver newKeywordPart: `@arg1 staticPart: `@arg2
Или даже поменять их местами:
`@receiver staticPart: `@arg2 `anyKeywordPart: `@arg1
Это невероятно мощно. Но как все это запомнить?
Как и в случае с обычным кодом Smalltalk, я исследую систему, используя диспетчеров, разработчиков и инспекторов, постепенно восстанавливая свое понимание. Здесь он ломается. Соответствующий синтаксис находится внутри строк и невидим для стандартных инструментов навигации. Код не завершен. Никакого рефакторинга. Никакой помощи со стороны окружения.
Так как же мы можем получить электричество без синтаксического налога?
это прямо здесь бшаблон Войдите:
[ any isNil ifTrue: anyBlock ] bpattern
бшаблон
бшаблон Предоставьте свободный, собственный для Smalltalk API поверх Rewrite Engine, используя простой блок Smalltalk Как образец.
Вы создаете экземпляр BPattern, отправляя #bpattern Сообщение блоку. Переменные и селекторы внутри блока определяют шаблоны, которые будут сопоставляться с целевыми узлами AST. Все начинается с традиций Любой Слово действует как подстановочный знак. Все остальное должно структурно совпадать.
Под капотом BPattern создает шаблон AST, используя те же классы узлов шаблона, что и механизм перезаписи. Все исходные механизмы сопоставления и перезаписи по-прежнему здесь – просто заключены в более доступный интерфейс со сценариями.
Вы можете подумать о BPatterns Smalltalk DSL Для перезаписи движка.
Pharo уже предоставляет специальные инструменты для переписывания движков, например StRewriterMatchToolPresenter:
При использовании BPattern ничего из этого не требуется. Паттерн — это всего лишь один блок. Добавьте еще одно сообщение, и Simple DoIt выполнит всю работу.
Чтобы найти все соответствующие методы:
[ anyRcv isNil ifTrue: anyBlock ] bpattern browseUsers
Чтобы переписать их:
[[ anyRcv isNil ifTrue: anyBlock ] -> [ anyRcv ifNil: anyBlock ]] brewrite preview
четко уточняя рисунок
Очевидно, вы можете сузить шаблон, используя #with: message: :
[ anyVar isNil ifTrue: anyBlock ] bpattern with: [ anyVar ] -> [:pattern | pattern beVariable ]
бПоскольку это обычный код Smalltalk, все стандартные инструменты разработки работают «из коробки»: подсветка синтаксиса, автодополнение кода, навигация и рефакторинг:
Просмотрите разработчиков #bechangeable Сообщение и другие фильтры вы найдете ниже BPatternVariableNode класс, типа #beInstVar Или #beLocalVar. Если ты что-то помнишь, Просто добавьте метод. Новый синтаксис не требуется.
Вы также можете использовать произвольный блок в качестве фильтра:
[ anyVar isNil ifTrue: anyBlock ] bpattern
with: [ anyVar ] -> [:pattern |
pattern beInstVar where: [:var | var name beginsWith: 'somePrefix' ]]
блок фокусировки [anyVar] Переменные, используемые для ссылки на то, где должен применяться блок конфигурации. Это позволяет избежать необработанных строк для имен переменных и делает эти конфигурации удобными для инструментов разработки:
Еще раз о шаблонах сообщений
Давайте теперь вернемся к примерам подстановочных знаков селектора с нуля, используя BPattern.
Переименование ключевого слова:
[
[ anyRcv anyKeywordPart: anyArg1 staticPart: anyArg2 ]
-> [ anyRcv newKeywordPart: anyArg1 staticPart: anyArg2 ]
] brewrite.
Замена ключевых слов:
[
[ anyRcv anyKeywordPart: anyArg1 staticPart: anyArg2 ]
-> [ anyRcv staticPart: anyArg2 anyKeywordPart: anyArg1 ]
] brewrite.
Шаблоны сообщений также можно уточнить с помощью #с: Сообщение:
[ any anyMessage: any2 ] bpattern
with: #anyMessage: -> [:pattern | pattern beBinary ];
browseUsers.
Это находит все методы с двоичными сообщениями:
Добавьте еще один фильтр, чтобы между литералами оставались только двоичные файлы:
[ any anyMessage: any2 ] bpattern
with: #anyMessage: -> [:pattern | pattern beBinary ];
with: [ any. any2 ] -> [ :pattern | pattern beLiteral ];
browseUsers
Старый синтаксис также поддерживает буквальные шаблоны, но удачи вам в поиске примера.
Шаблоны сообщений также можно настроить с произвольными условиями:
[ any anyMessage ] bpattern
with: #anyMessage -> [:pattern | pattern where: [:node | node selector beginsWith: 'prim' ]];
browseUsers
статус и что дальше
B-образцы не отображаются Каждый Механизм перезаписи еще не представлен, но некоторые из них уже поддерживаются, включая полный шаблон метода. #bmethod.
Полную информацию смотрите в репозитории GitHub:
И ознакомьтесь со следующей публикацией в блоге об упрощенном API устаревания, построенном на основе BPattern: