以下是整理后的演讲内容,已去除发言人姓名、时间及口头语,保留核心逻辑与内容结构,使文本结构更为清晰连贯。
玩转 AST:构建自己的代码分析与重写工具
一、AST 的基本概念
抽象语法树(AST, Abstract Syntax Tree)是在编程语言处理中最常见的数据结构,用于抽象表示源代码的语法结构。
通过树形结构来展示代码的组成,每个节点对应代码中的一个语法元素。AST 在编译器、解释器、静态分析工具、代码格式化工具中都有广泛应用,使得代码分析、优化、重构更容易实现。
二、Ruby 中常见的 AST 工具
在 Ruby 中有几种常用方式将源代码转换成 AST:
- Ripper:Ruby 自带的库。
- Parser Gem:如 RuboCop 使用。
- Ruby Parser:Breakman 等安全扫描工具使用。
这些工具能将 Ruby 源码转换成 AST,为代码格式化、静态分析等提供基础。
三、格式化工具的工作原理
代码格式化工具会遍历整个 AST,根据节点类型生成新的代码字符串。
例如遇到 class 节点就输出类定义代码,遇到 def 节点就生成方法定义。
格式规则(如缩进、单双引号样式)由工具预置,而生成的代码并不依赖原始源文件的布局。
以 Prettier 为例,它通过定义“中间表示”,使用 group、line、hardline 等标记控制换行与缩进。当代码超出行宽限制时,可以自动换行或保持一行显示。
四、Linter 工具
Linter 是静态分析工具。其实现模式一般有两种:
- 纯分析型:发现问题仅提示。
- 可修复型:除了提示,还能自动修复。
作者开发过两个工具:
- rails_best_practices:检查是否违反最佳实践,仅提示问题。
- Synvert:能自动重写和修复代码。
五、rails_best_practices 的实现模式
通过监听特定 AST 节点的类型与属性,分析模型与数据库索引、关联定义是否匹配。如检测到缺少索引,则输出警告信息,指出文件与行号。此方法不修改源码,仅报告问题。
六、Synvert 的实现模式
Synvert 以“代码片段(snippet)”为基本单元,提供查找、修改 AST 的规则组合。
它以 AST 为核心,对 Ruby 代码执行“查找 + 重写”操作。
工作流程为:
- 使用
Ripper、Parser或Ruby Parser将代码生成 AST。 - 利用四类信息:
node type、children、location、source来定位节点。 - 增强节点属性,便于后续使用(如
node.receiver、node.message等)。
七、自定义 AST 查询语言(Node Query Language)
为方便查询 AST,作者设计了类 CSS 的查询语法(NQL):
.send[receiver=factory_bot][message=create]
该语法可根据节点类型与属性条件匹配 AST 节点。
其底层实现借鉴编译器中的 词法分析(LEX) 与 语法分析(YACC) 技术,使用 Ruby 中的 Rex 和 Racc 实现。
通过定义 token(如节点类型、属性 key/value、操作符等),构建解析器,将字符串形式的查询语言解析成可执行的节点选择器。
该语言支持:
- 多个属性条件;
- 各种数值、布尔、字符串类型;
- 多种比较操作符(=、!=、>、<、in、not in 等);
- 数组、正则匹配;
- 多重选择器与子节点关系选择。
可匹配复杂模式,例如查找方法调用、查找特定 key 的哈希、检测测试方法中缺少 super 调用等。
八、代码替换与重写策略
代码替换通常有两种方式:
- 基于 AST 修改后重新生成代码:简单但可能破坏代码风格。
- 基于位置信息直接修改源文件:保留原始代码风格。
Synvert 采用第二种方式,通过 AST 节点的 start/end 位置信息,在源代码字符串中精确替换。
支持操作包括:
replace_withinsert_afterinsert_beforedeleteremove
示例:
将代码 FactoryBot.create(:user) 替换为 FactoryBot.create(:user, :activated);
或在测试方法中自动插入 super 调用。
九、查找与替换实例示例
-
删除调试代码
利用查询语法查找puts调用并删除。 -
方法命名调整
在 Rails 2.3 → 3.0 升级场景中,可自动将find_by_email_and_active(true)转换为:where(email: email, active: true)并处理类似的其他动态 finder 方法。
-
语法升级自动化
批量替换旧写法、增加必要语法元素(如super),或更新断言风格。
十、Synvert 的架构组成
- Synvert Core:提供查询与重写 API。
- CLI 工具
synvert:可读取本地或远程 snippet,批量修改文件。 - GUI 与 VSCode 扩展:
- 可视化展示修改 diff;
- 单选或批量应用修改;
- 支持自动生成 snippet;
- 提供输入原始代码与期望输出,即可自动生成对应 snippet。
十一、自动生成 Snippet 逻辑
输入示例代码与目标代码,系统会:
- 将两段代码转换为 AST;
- 对比节点差异;
- 生成相应的“insert / replace / delete”规则;
- 构建出 snippet。
此逻辑可应用在 GUI、VSCode 扩展与 API 后端中,目前尚非 AI,而是规则生成。
十二、跨语言支持与扩展
Synvert 不仅支持 Ruby,还拥有 JavaScript 版本,可实现:
- TypeScript 类型语法转换;
- CSS/LESS/SASS → SCSS 的文件重命名与语法转换等。
十三、开源与协作
Synvert 的核心组件(synvert-core、node_query、node_mutation)均为开源。
社区可贡献 snippet 规则库(synvert-snippets),在 GUI 中可直接启用。
支持即插即用、可版本更新分发的扩展机制。
十四、常见问题答疑摘要
-
AST 是否能分析动态定义的方法(metaprogramming)?
只能处理静态语法,动态定义的方法无法直接出现在 AST 中。 -
代码是否需语法正确?
必须语法正确才能生成 AST;语法错误文件无法解析。 -
是否能自动处理 Rails 版本迁移时的参数传递变化?
可以部分自动化处理,复杂场景需人工复核。 -
是否能实现 RSpec 到 Minitest 的自动转化?
可以,通过预定义输入输出规则与 snippet 即可扩展。
十五、总结
通过 AST 技术,可以实现自定义的代码分析、格式化和重写工具。
Synvert 结合同步的 CLI、GUI 与编辑器扩展,将 AST 操作简化为直观的查找与重写过程,使大规模代码重构自动化成为可能。
(整理完)
581

被折叠的 条评论
为什么被折叠?



