大家好,今天我想给大家介绍一个很有用的代码搜索工具,叫做 ast-grep。官网在 https://ast-grep.github.io/ ,https://github.com/ast-grep/ast-grep 在这里
它可以让你用抽象语法树(AST)的模式来搜索代码,而不是用正则表达式或者字符串匹配。这样可以让你更精确地找到你想要的代码片段,而不会受到变量名、空格、注释等无关因素的干扰。
ast-grep 支持多种编程语言,包括 Ruby。
因为是在 Ruby China 发帖,所以这次主要想给大家展示一下 ast-grep 的 Ruby 例子,以及它的 Web Playground 中可以调试 Ruby 代码。
同时,我也想和大家比较一下 ast-grep 和论坛坛友的Synvert的区别和优劣。Synvert 是一个可以自动修改 Ruby 代码的工具,它也是基于 AST 的,但是它的目标和方法和 ast-grep 不同。
假设我们有一段 Idol
的 class,要去替换其初始化的语句。(改编自 Ruby 官网 hello world)
# The Idol class
class Idol
def initialize(name)
@name = name.capitalize
end
def debut
puts "苺プロ所属 #{@name}です!"
end
end
# Create a new Idol!
oshi = Idol.new("瑠美衣")
# Output "苺プロ所属 瑠美衣です!"
oshi.debut
在 ast-grep 里只要写一句就可以找到 Idol.new
Idol.new($OSHI_NO_KO)
可以在线看 Playground 执行结果。
ast-grep 的官网有详细的文档和例子。对于 Ruby 而言,Ruby Example Catalog 收集了一些例子。也包括了 migrate Rails API 的实用例子。
除了查看文档和例子,你还可以在 ast-grep 的Web Playground中在线调试 Ruby 代码。 Web Playground 是一个网页版的交互式环境,你可以在左边输入 Ruby 代码,然后在右边输入 AST 模式,就可以看到匹配结果。你匹配结果还会在左边的代码中高亮显示对应的位置。这样,你就可以很方便地测试和调整你的 AST 模式,直到达到你想要的效果。
除了模式以外,ast-grep 也有更高级的YAML功能,可以更准确匹配代码。也可以当 linter 来使用。
Ruby 论坛里已经有 Synvert 的介绍啦。相比 Synvert,ast-grep 是基于 tree-sitter 实现的,支持的语言也更多一点。此外,默认的接口形式上,ast-grep 的模式匹配也简单一点。
比如 把 map + flatten 转成 flat_map 的转换,
[1, 2, 3, 4].map { |e| [e, e] }.flatten
# =>
[1, 2, 3, 4].flat_map { |e| [e, e] }
Synvert 的核心逻辑是这样的:
find_node '.send
[receiver=.block
[caller=.send[message=map]]]
[message=flatten]
[arguments.size=0]' do
delete :message, :dot
replace 'receiver.caller.message', with: 'flat_map'
end
这段代码还不能直接跑,需要加入 Synvert 的一些 boilerplate 代码
而 ast-grep 的规则可以这么写,在命令行可以做完代码转换
sg --pattern '$LIST.map {|$V| $E }.flatten ' --rewrite '$LIST.flat_map { |$V| $E }'
以上 Ruby 例子可以看到,ast-grep 的 pattern 更简洁而且直观。你只需要用$符号来表示变量,就可以构造出各种各样的 AST 模式。
以上就是 ast-grep 的介绍,希望能帮助大家在工作或项目中用到。谢谢支持!