如何用面向对象方式解析并修改Dart源代码?
解决方案
方法1:直接操作Analyzer AST生成修改后的代码
Analyzer的AST虽为不可变设计,但可通过创建新节点替换原有节点实现修改,无需额外中间层:
- 用
parseFile解析模板文件得到CompilationUnit语法树 - 自定义
AstVisitor遍历树,定位到目标类节点 - 复制原有类的成员列表,添加生成的函数节点,创建新的
ClassDeclaration - 替换
CompilationUnit中的原类节点,最后用toSource()生成代码并格式化
示例代码:
import 'package:analyzer/dart/analysis/features.dart'; import 'package:analyzer/dart/analysis/utilities.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/visitor.dart'; import 'package:dart_style/dart_style.dart'; class ClassUpdater extends RecursiveAstVisitor { final String targetClass; final FunctionDeclaration newFunc; ClassDeclaration? updatedClass; ClassUpdater(this.targetClass, this.newFunc); @override void visitClassDeclaration(ClassDeclaration node) { if (node.name.name == targetClass) { // 复制原有成员并添加新函数 final newMembers = [...node.members, newFunc]; // 创建新的类节点 updatedClass = node.copyWith(members: newMembers); } super.visitClassDeclaration(node); } } String updateClassInFile(String templatePath, FunctionDeclaration newFunc, String className) { // 解析模板文件 final parseResult = parseFile( path: templatePath, featureSet: FeatureSet.latestLanguageVersion(), ); final unit = parseResult.unit; // 遍历并更新类 final updater = ClassUpdater(className, newFunc); unit.accept(updater); if (updater.updatedClass == null) throw Exception('Class $className not found'); // 替换原类节点,生成新的CompilationUnit final newDeclarations = unit.declarations.map((decl) { if (decl is ClassDeclaration && decl.name.name == className) { return updater.updatedClass!; } return decl; }).toList(); final newUnit = unit.copyWith(declarations: newDeclarations); // 生成并格式化代码 return DartFormatter().format(newUnit.toSource()); }
方法2:用Analyzer提取元数据,code_builder重建类
若觉得直接操作AST繁琐,可拆分步骤:用Analyzer读取原有类的结构信息,再用code_builder重新构建类并添加新函数:
- 解析模板文件,提取目标类的名称、现有方法/字段
- 用code_builder的
ClassBuilder复刻原有类,添加新方法 - 生成代码并格式化
示例代码:
import 'package:analyzer/dart/analysis/utilities.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:code_builder/code_builder.dart'; import 'package:dart_style/dart_style.dart'; ClassBuilder buildClassFromAst(ClassDeclaration classAst) { final classBuilder = ClassBuilder()..name = classAst.name.name; // 复刻原有方法 for (final member in classAst.members) { if (member is FunctionDeclaration) { final method = MethodBuilder() ..name = member.name.name ..returns = refer(member.returnType?.toSource() ?? 'void') ..body = Code(member.body.toSource()); // 处理参数(简化示例,可根据需求完善) for (final param in member.functionExpression.parameters?.parameters ?? []) { method.requiredParameters.add(Parameter((p) => p.name = param.identifier.name)); } classBuilder.methods.add(method); } } return classBuilder; } String regenerateClass(String templatePath, MethodBuilder newMethod, String className) { final unit = parseFile( path: templatePath, featureSet: FeatureSet.latestLanguageVersion(), ).unit; // 找到目标类 final classAst = unit.declarations.firstWhere( (d) => d is ClassDeclaration && (d as ClassDeclaration).name.name == className, ) as ClassDeclaration; // 构建类并添加新方法 final classBuilder = buildClassFromAst(classAst); classBuilder.methods.add(newMethod); // 生成代码 final emitter = DartEmitter(); final library = Library((l) => l.body.add(classBuilder.build())); return DartFormatter().format(library.accept(emitter).toString()); }
极简方案(适合简单场景)
如果只是在类末尾添加函数,且模板文件格式稳定,可直接用字符串操作:读取文件内容,定位到类的}前,插入新函数代码,最后格式化。这种方法开销最小,但不适合复杂AST修改场景。
内容的提问来源于stack exchange,提问作者Ty Landercasper
相关产品推荐
相关产品推荐

