重寫引擎是 John Brant 和 Don Roberts 的一項絕對傑出的發明,與 Refactoring Browser 一同推出(參見「A Refactoring Tool for Smalltalk」,1997 年)。它提供了 AST 層級的匹配和重寫功能,威力驚人。
但說實話:有多少人真正記得它的語法?
即使是最簡單的重寫規則——例如,將一個已棄用的訊息替換為一個新的——通常也會讓我去尋找範例。在這個專案中,我花了很多時間深入研究重寫引擎,即使現在我仍然無法可靠地記住確切的語法。
事實上,這兩種方法都有效——但它們對目標節點應用了不同的過濾器。試著記住是哪一種。
而這僅僅是開始。
你知道你可以對選擇器的部分進行萬用字元匹配嗎?
這功能強大無比。但你該如何記住這一切?
對於一般的 Smalltalk 程式碼,我會使用 senders、implementors、inspectors 來探索系統——逐步重建我的理解。在這裡,這方法失效了。匹配語法存在於字串中,標準導航工具無法看到。沒有程式碼補全。沒有重構。沒有環境的幫助。
那麼,我們如何在不支付語法稅的情況下保留其威力呢?
BPatterns 在重寫引擎之上提供了一個流暢、原生 Smalltalk 的 API,使用普通的 Smalltalk 程式碼區塊作為模式。
在底層,BPattern 使用與重寫引擎相同的模式節點類別來建構模式 AST。所有原始的匹配和重寫機制仍然存在——只是被包裝在一個更易於接近、可腳本化的介面中。
你可以將 BPatterns 視為重寫引擎的 Smalltalk DSL。
Pharo 已經為重寫引擎提供了專用的工具,例如 StRewriterMatchToolPresenter:
有了 BPatterns,這些都不再需要。一個模式只是一個程式碼區塊。再加一個訊息,簡單的 DoIt 就能完成工作。
你可以使用 #with: 訊息來明確地縮小模式:
[ anyVar isNil ifTrue: anyBlock ] bpattern with: [ anyVar ] -> [:pattern | pattern beVariable ]
因為這是常規的 Smalltalk 程式碼,所有標準的開發工具都能開箱即用:語法高亮、程式碼補全、導航和重構:
瀏覽 #beVariable 訊息的 implementors,你將在 BPatternVariableNode 類別下找到其他過濾器,例如 #beInstVar 或 #beLocalVar 。如果你錯過了什麼,只需添加一個方法。無需新的語法。
你也可以使用任意的程式碼區塊作為過濾器:
[ anyVar isNil ifTrue: anyBlock ] bpattern
pattern beInstVar where: [:var | var name beginsWith: 'somePrefix' ]]
請注意,程式碼區塊 [anyVar] 用於引用應應用配置區塊的變數。這避免了使用原始字串作為變數名稱,並使這些配置對開發工具友好:
現在,讓我們使用 BPatterns 回顧開頭的選擇器萬用字元匹配範例。
[ anyRcv anyKeywordPart: anyArg1 staticPart: anyArg2 ]
-> [ anyRcv newKeywordPart: anyArg1 staticPart: anyArg2 ]
[ anyRcv anyKeywordPart: anyArg1 staticPart: anyArg2 ]
-> [ anyRcv staticPart: anyArg2 anyKeywordPart: anyArg1 ]
訊息模式也可以使用 #with: 訊息進行細化:
with: #anyMessage: -> [:pattern | pattern beBinary ];
這會找到所有包含二進位訊息的方法:
添加另一個過濾器,只保留字面量之間的二進位訊息:
with: #anyMessage: -> [:pattern | pattern beBinary ];
with: [ any. any2 ] -> [ :pattern | pattern beLiteral ];
訊息模式也可以使用任意條件進行配置:
with: #anyMessage -> [:pattern | pattern where: [:node | node selector beginsWith: 'prim' ]];
BPatterns 尚未暴露重寫引擎的每一項功能,但許多功能已經得到支持,包括透過 #bmethod 的完整方法模式。
更多詳情,請參閱 GitHub 儲存庫:
並查看下一篇部落格文章,介紹一個基於 BPatterns 的簡化棄用 API: