抽象语法树 (AST)
🌐 Abstract Syntax Tree (AST)
Oxc AST 是所有 Oxc 工具的基础。了解其结构以及如何使用它,对于参与解析器、代码检查器、转换器及其他组件的开发至关重要。
🌐 The Oxc AST is the foundation of all Oxc tools. Understanding its structure and how to work with it is essential for contributing to parser, linter, transformer, and other components.
AST 架构
🌐 AST Architecture
设计原则
🌐 Design Principles
Oxc AST 是基于以下原则设计的:
🌐 The Oxc AST is designed with the following principles:
- 性能优先:针对速度和内存效率进行了优化
- 类型安全:利用 Rust 的类型系统防止常见错误
- 规范合规:严格遵循 ECMAScript 规范
- 清晰语义:消除其他 AST 格式中存在的歧义
使用 AST
🌐 Working with the AST
生成 AST 相关代码
🌐 Generate AST Related Code
当你修改 AST 定义时,运行代码生成工具:
🌐 When you modify AST definitions, run the code generation tool:
just ast这会生成:
🌐 This generates:
- 访问者模式:用于遍历 AST
- 构建方法:用于构建 AST 节点
- 特性实现:用于常见操作
- TypeScript 类型:用于 Node.js 绑定
AST 节点结构
🌐 AST Node Structure
每个 AST 节点都遵循一致的模式:
🌐 Every AST node follows a consistent pattern:
#[ast(visit)]
pub struct FunctionDeclaration<'a> {
pub span: Span,
pub id: Option<BindingIdentifier<'a>>,
pub generator: bool,
pub r#async: bool,
pub params: FormalParameters<'a>,
pub body: Option<FunctionBody<'a>>,
pub type_parameters: Option<TSTypeParameterDeclaration<'a>>,
pub return_type: Option<TSTypeAnnotation<'a>>,
}关键组成部分:
🌐 Key components:
span:源位置资讯#[ast(visit)]:生成访问者方法- 生命周期
'a:对竞技场分配内存的引用
内存管理
🌐 Memory Management
AST 使用内存区域来进行高效分配:
🌐 The AST uses a memory arena for efficient allocation:
use oxc_allocator::Allocator;
let allocator = Allocator::default();
let ast = parser.parse(&allocator, source_text, source_type)?;好处:
🌐 Benefits:
- 快速分配:无需单独的 malloc 调用
- 快速释放:一次性释放整个区域
- 缓存友好:线性内存布局
- 无引用计数:简单的生命周期管理
AST 遍历
🌐 AST Traversal
访问者模式
🌐 Visitor Pattern
使用生成的访问者进行 AST 遍历:
🌐 Use the generated visitor for AST traversal:
use oxc_ast::visit::{Visit, walk_mut};
struct MyVisitor;
impl<'a> Visit<'a> for MyVisitor {
fn visit_function_declaration(&mut self, func: &FunctionDeclaration<'a>) {
println!("Found function: {:?}", func.id);
walk_mut::walk_function_declaration(self, func);
}
}
// Usage
let mut visitor = MyVisitor;
visitor.visit_program(&program);可变访问者
🌐 Mutable Visitor
对于转换,使用可变访问器:
🌐 For transformations, use the mutable visitor:
use oxc_ast::visit::{VisitMut, walk_mut};
struct MyTransformer;
impl<'a> VisitMut<'a> for MyTransformer {
fn visit_binary_expression(&mut self, expr: &mut BinaryExpression<'a>) {
// Transform the expression
if expr.operator == BinaryOperator::Addition {
// Modify the AST node
}
walk_mut::walk_binary_expression_mut(self, expr);
}
}抽象语法树构建
🌐 AST Construction
构建者模式
🌐 Builder Pattern
使用 AST 构建器创建节点:
🌐 Use the AST builder for creating nodes:
use oxc_ast::AstBuilder;
let ast = AstBuilder::new(&allocator);
// Create a binary expression: a + b
let left = ast.expression_identifier_reference(SPAN, "a");
let right = ast.expression_identifier_reference(SPAN, "b");
let expr = ast.expression_binary_expression(
SPAN,
left,
BinaryOperator::Addition,
right,
);辅助函数
🌐 Helper Functions
常用模式作为辅助提供:
🌐 Common patterns are provided as helpers:
impl<'a> AstBuilder<'a> {
pub fn expression_number_literal(&self, span: Span, value: f64) -> Expression<'a> {
self.alloc(Expression::NumericLiteral(
self.alloc(NumericLiteral { span, value, raw: None })
))
}
}开发工作流程
🌐 Development Workflow
添加新的 AST 节点
🌐 Adding New AST Nodes
定义结构体:
rust#[ast(visit)] pub struct MyNewNode<'a> { pub span: Span, pub name: Atom<'a>, pub value: Expression<'a>, }添加到枚举:
rustpub enum Statement<'a> { // ... existing variants MyNewStatement(Box<'a, MyNewNode<'a>>), }运行代码生成:
bashjust ast实现解析逻辑:
rustimpl<'a> Parser<'a> { fn parse_my_new_node(&mut self) -> Result<MyNewNode<'a>> { // Parsing implementation } }
比较 AST 格式
🌐 Comparing AST Formats
使用 AST 探索器
🌐 Use AST Explorer
要与其他解析器进行比较,请使用 ast-explorer.dev:
🌐 For comparing with other parsers, use ast-explorer.dev:
- 更好的用户界面:带语法高亮的现代界面
- 最新:最新的解析器版本
- 多个解析器:比较 Oxc、Babel、TypeScript 等。
- 导出格式:JSON,代码生成
性能考虑
🌐 Performance Considerations
内存布局
🌐 Memory Layout
AST 的设计考虑了缓存效率:
🌐 The AST is designed for cache efficiency:
// Good: Compact representation
struct CompactNode<'a> {
span: Span, // 8 bytes
flags: u8, // 1 byte
name: Atom<'a>, // 8 bytes
}
// Avoid: Large enums without boxing
enum LargeEnum {
Small,
Large { /* 200 bytes of data */ },
}竞技场分配
🌐 Arena Allocation
所有 AST 节点都分配在 arena 中:
🌐 All AST nodes are allocated in the arena:
// Automatically handled by #[ast] macro
let node = self.ast.alloc(MyNode {
span: SPAN,
value: 42,
});枚举大小测试
🌐 Enum Size Testing
我们强制使用小的枚举大小:
🌐 We enforce small enum sizes:
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
#[test]
fn no_bloat_enum_sizes() {
use std::mem::size_of;
assert_eq!(size_of::<Statement>(), 16);
assert_eq!(size_of::<Expression>(), 16);
assert_eq!(size_of::<Declaration>(), 16);
}高级主题
🌐 Advanced Topics
自定义AST属性
🌐 Custom AST Attributes
为特定工具添加自定义属性:
🌐 Add custom attributes for specific tools:
#[ast(visit)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub struct MyNode<'a> {
#[cfg_attr(feature = "serialize", serde(skip))]
pub internal_data: u32,
pub public_field: Atom<'a>,
}与语义分析的集成
🌐 Integration with Semantic Analysis
将 AST 节点与语义信息关联:
🌐 Link AST nodes with semantic information:
#[ast(visit)]
pub struct IdentifierReference<'a> {
pub span: Span,
pub name: Atom<'a>,
#[ast(ignore)]
pub reference_id: Cell<Option<ReferenceId>>,
}这允许工具在遍历抽象语法树(AST)时访问绑定信息、作用域上下文和类型信息。
🌐 This allows tools to access binding information, scope context, and type information during AST traversal.
调试技巧
🌐 Debugging Tips
美化打印
🌐 Pretty Printing
使用调试格式化器检查 AST:
🌐 Use the debug formatter to inspect AST:
println!("{:#?}", ast_node);跨度信息
🌐 Span Information
跟踪错误报告的源位置:
🌐 Track source locations for error reporting:
let span = node.span();
println!("Error at {}:{}", span.start, span.end);