From 52e62d58393183f988d5d18f6d793e6d193e320b Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Sun, 15 Nov 2026 19:06:33 +0800 Subject: [PATCH] docs(frontend): persist generated AST and visitor annotations --- crates/frontend/editor/ast.ts | 301 +++++++++++++++++++++++++++ crates/frontend/src/ast/generated.rs | 136 ++++++++++++ crates/frontend/src/ast/visitor.rs | 186 +++++++++++++++++ scripts/generate_ast.py | 181 ++++++++++++++-- 4 files changed, 788 insertions(+), 16 deletions(-) diff --git a/crates/frontend/editor/ast.ts b/crates/frontend/editor/ast.ts index 8f83e3e..9b2982a 100644 --- a/crates/frontend/editor/ast.ts +++ b/crates/frontend/editor/ast.ts @@ -1,245 +1,546 @@ // Generated by scripts/generate_ast.py from Endor.asdl. Do not edit. +// 与 Rust wire 共同核对的 ASDL 原文协议身份;生成源码注释不改变此值。 export const AST_SCHEMA_ID = "urn:endor:ast:sha256:21b0349ac295d9bc8cedcad8f07d8e2d45cf220e19e7c2fad10269555fcfd71a" as const; +// 原 UTF-8 半开字节范围;number 类型本身不验证安全整数、顺序或原文边界。 export interface Span { start: number; end: number; } +// 宿主/编辑器传输的版本化语法文档,wire 校验后供 compiler 使用。 +// schema: 协议身份,wire 按 AST_SCHEMA_ID 核对;不是源码内容哈希。 +// root: 拥有的语法根,文档校验及编译从这里递归。 export type AstDocument = { schema: typeof AST_SCHEMA_ID; root: AstRoot; }; +// parse_ast 按模式构造的模块、表达式或交互根,compiler 据此选择生成入口。 +// Module: 模块根,包含函数/类声明。 +// Module.body: body 保存 Program 类型的值。 +// Expression: 表达式根,编译后直接返回求值结果。 +// Expression.body: body 保存 Expr 类型的值。 +// Interactive: 交互根,包含一次完整提交。 +// Interactive.body: body 保存 InteractiveInput 类型的值。 export type AstRoot = { kind: "Module"; data: Program; } | { kind: "Expression"; data: Expr; } | { kind: "Interactive"; data: InteractiveInput; }; +// parser 复制的名称文本与位置,用于声明、类型注解或属性名。 +// text: 拥有名称文本,不依赖输入缓冲存活。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type Name = { text: string; span: Span; }; +// 模块声明序列,compiler 先注册全部顶层名称再分析各函数体。 +// declarations: 按源码顺序的声明;源码模块解析要求非空,文档结构本身不限制数量。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type Program = { declarations: Array; span: Span; }; +// 顶层/交互声明的种类及完整范围,compiler 按函数或类分派。 +// kind: 函数/类声明载荷,JSON 和类型采用 kind/data 标签。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type Decl = { kind: DeclKind; span: Span; }; +// 函数与类声明的判别载荷,外层 Decl 另外保存完整跨度。 +// Function: 显式参数/返回注解与函数体,compiler 生成独立模板及源码签名;解析本身不调用函数。 +// Function.definition: definition 保存 Function 类型的值。 +// Class: 类名、可选基类、字段和方法,compiler 检查成员后生成构类指令。 +// Class.definition: definition 保存 Class 类型的值。 export type DeclKind = { kind: "Function"; data: Function; } | { kind: "Class"; data: Class; }; +// 类名、可选基类、字段和方法,compiler 检查成员后生成构类指令。 +// name: 类声明名与精确位置。 +// base: 显式基类名,可省略;当前 compiler 对省略项使用 Object。 +// fields: 按出现顺序的字段名称/类型注解,不含方法。 +// methods: 按出现顺序的方法;显式 self 约束由 compiler 检查。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type Class = { name: Name; base?: Name | null; fields: Array; methods: Array; span: Span; }; +// 显式参数/返回注解与函数体,compiler 生成独立模板及源码签名;解析本身不调用函数。 +// name: 函数/方法名,代码模板沿用为诊断名称。 +// parameters: 位置参数列表,compiler 依次分配前部局部槽。 +// return_type: 返回类型名,生成源码签名后由运行时解析。 +// body: 独立函数作用域的语句体,当前源码不支持嵌套声明闭包。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type Function = { name: Name; parameters: Array; return_type: Name; body: Block; span: Span; }; +// 函数参数或类字段共用的名称/类型注解,实际类型身份由运行时环境解析。 +// name: 参数/字段名,用于布局和重复检查。 +// type_name: 声明类型名,前端只保留文本与位置。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type Parameter = { name: Name; type_name: Name; span: Span; }; +// 顺序语句块,compiler 检查确定初始化并按语句生成指令。 +// statements: 有序语句列表,不引入独立块级局部作用域。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type Block = { statements: Array; span: Span; }; +// 语句种类及原文范围,compiler 处理副作用、控制流或返回。 +// kind: 具体语句载荷,外层另存整句范围。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type Stmt = { kind: StmtKind; span: Span; }; +// 语句判别载荷,parser 选择分支,compiler 按绑定、赋值、控制流等规则处理。 +// Binding: let 新绑定,compiler 检查重名并分析右值后才标记目标已初始化。 +// Binding.binding: binding 保存 Binding 类型的值。 +// Assignment: 普通名称赋值,compiler 按作用域写局部槽或全局环境。 +// Assignment.assignment: assignment 保存 Assignment 类型的值。 +// MemberAssignment: 成员写入的对象、属性名与右值,compiler 生成 StoreAttr。 +// MemberAssignment.assignment: assignment 保存 MemberAssignment 类型的值。 +// If: 条件、then 和可选 else,compiler 合并继续路径的初始化集合并生成跳转。 +// If.statement: statement 保存 IfStatement 类型的值。 +// While: 循环条件与体,compiler 生成回边并处理循环可能零次执行。 +// While.statement: statement 保存 WhileStatement 类型的值。 +// Return: 函数返回,compiler 对缺省值补 Nil;当前源码语法要求 return 后有表达式。 +// Return.statement: statement 保存 ReturnStatement 类型的值。 +// Expression: 表达式载荷及完整范围,compiler 生成留下一个结果的求值序列。 +// Expression.expression: expression 保存 Expr 类型的值。 +// Declaration: 顶层/交互声明的种类及完整范围,compiler 按函数或类分派。 +// Declaration.declaration: declaration 保存 Decl 类型的值。 export type StmtKind = { kind: "Binding"; data: Binding; } | { kind: "Assignment"; data: Assignment; } | { kind: "MemberAssignment"; data: MemberAssignment; } | { kind: "If"; data: IfStatement; } | { kind: "While"; data: WhileStatement; } | { kind: "Return"; data: ReturnStatement; } | { kind: "Expression"; data: Expr; } | { kind: "Declaration"; data: Decl; }; +// let 新绑定,compiler 检查重名并分析右值后才标记目标已初始化。 +// name: let 目标名,重复声明由语义分析拒绝。 +// value: 绑定右值,本局部不能在初始化前读取自身。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type Binding = { name: Name; value: Expr; span: Span; }; +// 普通名称赋值,compiler 按作用域写局部槽或全局环境。 +// name: 普通赋值目标,函数内首次赋值可预分配局部槽。 +// value: 先求右值再更新目标。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type Assignment = { name: Name; value: Expr; span: Span; }; +// 成员写入的对象、属性名与右值,compiler 生成 StoreAttr。 +// object: 属性写入接收对象表达式,前端不求值。 +// name: 待写属性名,compiler 加入名字表。 +// value: 待写右值,当前 compiler 先于接收对象生成求值以匹配 VM 栈布局。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type MemberAssignment = { object: Expr; name: Name; value: Expr; span: Span; }; +// 条件、then 和可选 else,compiler 合并继续路径的初始化集合并生成跳转。 +// condition: 条件表达式,运行时按真值协议选择分支。 +// body: 条件为真的 then 块。 +// otherwise: 可选 else 块,缺省时存在直接继续路径。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type IfStatement = { condition: Expr; body: Block; otherwise?: Block | null; span: Span; }; +// 循环条件与体,compiler 生成回边并处理循环可能零次执行。 +// condition: 每轮重新求值的条件,假值退出。 +// body: 循环体,可能一次也不执行。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type WhileStatement = { condition: Expr; body: Block; span: Span; }; +// 函数返回,compiler 对缺省值补 Nil;当前源码语法要求 return 后有表达式。 +// value: 可选返回表达式,手工 AST 缺省时由 compiler 补 Nil。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type ReturnStatement = { value?: Expr | null; span: Span; }; +// 单次交互提交容器,parser 产生一条语句,compiler 也检查数量,不保存宿主会话环境。 +// statements: 当前 parse/compile 约定恰好一个完整提交的语句。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type InteractiveInput = { statements: Array; span: Span; }; +// 表达式载荷及完整范围,compiler 生成留下一个结果的求值序列。 +// kind: 求值形状,不缓存名称解析或运行时类型。 +// span: 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 export type Expr = { kind: ExprKind; span: Span; }; +// 名称、字面量、等待、运算和调用等表达式载荷,范围由外层 Expr 提供。 +// Name: 变量引用文本,compiler 决定局部槽或全局索引。 +// Name.text: text 保存 string 类型的值。 +// Number: 原始十进制拼写,保留全部位数、前导零及尾零,不转浮点。 +// Number.spelling: spelling 保存 string 类型的值。 +// String: 已解码受支持转义的拥有文本,原引号范围在外层 Expr。 +// String.value: value 保存 string 类型的值。 +// Symbol: 点开头的符号名,前端可表示,当前 compiler 拒绝生成运行时值。 +// Symbol.name: 成员或符号名,不是已解析的运行时属性句柄。 +// Await: 等待子表达式,VM 执行上下文决定是否允许停放。 +// Await.operand: 一元子表达式,Rust 的递归回边间接存储。 +// Unary: 一元取负,可在纯数值链上合并符号。 +// Unary.operation: 运算种类,决定算术、比较或取负指令。 +// Unary.operand: 一元子表达式,Rust 的递归回边间接存储。 +// Binary: 左右操作数及算术/比较种类,保留解析优先级树。 +// Binary.operation: 运算种类,决定算术、比较或取负指令。 +// Binary.left: 二元左操作数,当前 compiler 先于右侧求值。 +// Binary.right: 二元右操作数,与左侧一起被运行时运算消费。 +// Member: 接收对象及属性名的读取。 +// Member.object: 成员读取接收对象。 +// Member.name: 成员或符号名,不是已解析的运行时属性句柄。 +// Call: 被调用表达式与位置实参,由 VM 实际求值。 +// Call.callee: 调用目标,先于实参求值。 +// Call.arguments: 有序位置实参,AST 不隐式添加接收者。 export type ExprKind = { kind: "Name"; data: string; } | { kind: "Number"; data: string; } | { kind: "String"; data: string; } | { kind: "Symbol"; data: Name; } | { kind: "Await"; data: Expr; } | { kind: "Unary"; data: { operation: UnaryOp; operand: Expr; }; } | { kind: "Binary"; data: { operation: BinaryOp; left: Expr; right: Expr; }; } | { kind: "Member"; data: { object: Expr; name: Name; }; } | { kind: "Call"; data: { callee: Expr; arguments: Array; }; }; +// 一元运算种类,compiler 合并纯数值负号或生成运行时取负。 +// Negate: 一元取负。 export type UnaryOp = "Negate"; +// 二元算术/比较种类,parser 保留优先级树,compiler 映射为对应 VM 操作。 +// Add: 加法。 +// Subtract: 减法。 +// Multiply: 乘法。 +// Divide: 除法。 +// Equal: 相等比较。 +// NotEqual: 不等比较。 +// Less: 小于比较。 +// LessEqual: 小于等于比较。 +// Greater: 大于比较。 +// GreaterEqual: 大于等于比较。 export type BinaryOp = "Add" | "Subtract" | "Multiply" | "Divide" | "Equal" | "NotEqual" | "Less" | "LessEqual" | "Greater" | "GreaterEqual"; +// 编辑器可选节点回调;walk 先回调,返回 false 只剪掉当前子树,其他值继续递归。 export interface Visitor { +// 跨度叶子回调没有子结构,其返回值不参与后续剪枝。 visitSpan?(node: Span): boolean | void; +// walkAstDocument 到达 AstDocument 时调用,false 跳过该节点的后续子字段。 visitAstDocument?(node: AstDocument): boolean | void; +// walkAstRoot 到达 AstRoot 时调用,false 跳过该节点的后续子字段。 visitAstRoot?(node: AstRoot): boolean | void; +// walkName 到达 Name 时调用,false 跳过该节点的后续子字段。 visitName?(node: Name): boolean | void; +// walkProgram 到达 Program 时调用,false 跳过该节点的后续子字段。 visitProgram?(node: Program): boolean | void; +// walkDecl 到达 Decl 时调用,false 跳过该节点的后续子字段。 visitDecl?(node: Decl): boolean | void; +// walkDeclKind 到达 DeclKind 时调用,false 跳过该节点的后续子字段。 visitDeclKind?(node: DeclKind): boolean | void; +// walkClass 到达 Class 时调用,false 跳过该节点的后续子字段。 visitClass?(node: Class): boolean | void; +// walkFunction 到达 Function 时调用,false 跳过该节点的后续子字段。 visitFunction?(node: Function): boolean | void; +// walkParameter 到达 Parameter 时调用,false 跳过该节点的后续子字段。 visitParameter?(node: Parameter): boolean | void; +// walkBlock 到达 Block 时调用,false 跳过该节点的后续子字段。 visitBlock?(node: Block): boolean | void; +// walkStmt 到达 Stmt 时调用,false 跳过该节点的后续子字段。 visitStmt?(node: Stmt): boolean | void; +// walkStmtKind 到达 StmtKind 时调用,false 跳过该节点的后续子字段。 visitStmtKind?(node: StmtKind): boolean | void; +// walkBinding 到达 Binding 时调用,false 跳过该节点的后续子字段。 visitBinding?(node: Binding): boolean | void; +// walkAssignment 到达 Assignment 时调用,false 跳过该节点的后续子字段。 visitAssignment?(node: Assignment): boolean | void; +// walkMemberAssignment 到达 MemberAssignment 时调用,false 跳过该节点的后续子字段。 visitMemberAssignment?(node: MemberAssignment): boolean | void; +// walkIfStatement 到达 IfStatement 时调用,false 跳过该节点的后续子字段。 visitIfStatement?(node: IfStatement): boolean | void; +// walkWhileStatement 到达 WhileStatement 时调用,false 跳过该节点的后续子字段。 visitWhileStatement?(node: WhileStatement): boolean | void; +// walkReturnStatement 到达 ReturnStatement 时调用,false 跳过该节点的后续子字段。 visitReturnStatement?(node: ReturnStatement): boolean | void; +// walkInteractiveInput 到达 InteractiveInput 时调用,false 跳过该节点的后续子字段。 visitInteractiveInput?(node: InteractiveInput): boolean | void; +// walkExpr 到达 Expr 时调用,false 跳过该节点的后续子字段。 visitExpr?(node: Expr): boolean | void; +// walkExprKind 到达 ExprKind 时调用,false 跳过该节点的后续子字段。 visitExprKind?(node: ExprKind): boolean | void; +// walkUnaryOp 到达 UnaryOp 时调用,false 跳过该节点的后续子字段。 visitUnaryOp?(node: UnaryOp): boolean | void; +// walkBinaryOp 到达 BinaryOp 时调用,false 跳过该节点的后续子字段。 visitBinaryOp?(node: BinaryOp): boolean | void; } +// 上层 walker/宿主调用的跨度叶子入口,仅通知 visitSpan,不验证输入或修改节点。 export function walkSpan(node: Span, visitor: Visitor): void { visitor.visitSpan?.(node); } +// 上层 walker 或宿主调用,先通知 AstDocument 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkAstDocument(node: AstDocument, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitAstDocument?.(node) === false) return; +// 递归访问 node.root 的 AstRoot 子结构或跨度。 walkAstRoot(node.root, visitor); } +// 上层 walker 或宿主调用,先通知 AstRoot 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkAstRoot(node: AstRoot, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitAstRoot?.(node) === false) return; +// 判别 kind 只选择当前载荷,普通文本/数字拼写没有进一步语法子结构。 switch (node.kind) { +// 模块根,包含函数/类声明。 case "Module": +// 递归访问 node.data 的 Program 子结构或跨度。 walkProgram(node.data, visitor); break; +// 表达式根,编译后直接返回求值结果。 case "Expression": +// 递归访问 node.data 的 Expr 子结构或跨度。 walkExpr(node.data, visitor); break; +// 交互根,包含一次完整提交。 case "Interactive": +// 递归访问 node.data 的 InteractiveInput 子结构或跨度。 walkInteractiveInput(node.data, visitor); break; } } +// 上层 walker 或宿主调用,先通知 Name 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkName(node: Name, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitName?.(node) === false) return; +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 Program 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkProgram(node: Program, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitProgram?.(node) === false) return; +// 按声明顺序访问 node.declarations 列表中的每个 Decl 子节点。 for (const value of node.declarations) walkDecl(value, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 Decl 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkDecl(node: Decl, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitDecl?.(node) === false) return; +// 递归访问 node.kind 的 DeclKind 子结构或跨度。 walkDeclKind(node.kind, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 DeclKind 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkDeclKind(node: DeclKind, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitDeclKind?.(node) === false) return; +// 判别 kind 只选择当前载荷,普通文本/数字拼写没有进一步语法子结构。 switch (node.kind) { +// 显式参数/返回注解与函数体,compiler 生成独立模板及源码签名;解析本身不调用函数。 case "Function": +// 递归访问 node.data 的 Function 子结构或跨度。 walkFunction(node.data, visitor); break; +// 类名、可选基类、字段和方法,compiler 检查成员后生成构类指令。 case "Class": +// 递归访问 node.data 的 Class 子结构或跨度。 walkClass(node.data, visitor); break; } } +// 上层 walker 或宿主调用,先通知 Class 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkClass(node: Class, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitClass?.(node) === false) return; +// 递归访问 node.name 的 Name 子结构或跨度。 walkName(node.name, visitor); +// node.base 可缺省或为 null,只有存在时才访问该子节点。 if (node.base != null) walkName(node.base, visitor); +// 按声明顺序访问 node.fields 列表中的每个 Parameter 子节点。 for (const value of node.fields) walkParameter(value, visitor); +// 按声明顺序访问 node.methods 列表中的每个 Function 子节点。 for (const value of node.methods) walkFunction(value, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 Function 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkFunction(node: Function, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitFunction?.(node) === false) return; +// 递归访问 node.name 的 Name 子结构或跨度。 walkName(node.name, visitor); +// 按声明顺序访问 node.parameters 列表中的每个 Parameter 子节点。 for (const value of node.parameters) walkParameter(value, visitor); +// 递归访问 node.return_type 的 Name 子结构或跨度。 walkName(node.return_type, visitor); +// 递归访问 node.body 的 Block 子结构或跨度。 walkBlock(node.body, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 Parameter 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkParameter(node: Parameter, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitParameter?.(node) === false) return; +// 递归访问 node.name 的 Name 子结构或跨度。 walkName(node.name, visitor); +// 递归访问 node.type_name 的 Name 子结构或跨度。 walkName(node.type_name, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 Block 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkBlock(node: Block, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitBlock?.(node) === false) return; +// 按声明顺序访问 node.statements 列表中的每个 Stmt 子节点。 for (const value of node.statements) walkStmt(value, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 Stmt 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkStmt(node: Stmt, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitStmt?.(node) === false) return; +// 递归访问 node.kind 的 StmtKind 子结构或跨度。 walkStmtKind(node.kind, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 StmtKind 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkStmtKind(node: StmtKind, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitStmtKind?.(node) === false) return; +// 判别 kind 只选择当前载荷,普通文本/数字拼写没有进一步语法子结构。 switch (node.kind) { +// let 新绑定,compiler 检查重名并分析右值后才标记目标已初始化。 case "Binding": +// 递归访问 node.data 的 Binding 子结构或跨度。 walkBinding(node.data, visitor); break; +// 普通名称赋值,compiler 按作用域写局部槽或全局环境。 case "Assignment": +// 递归访问 node.data 的 Assignment 子结构或跨度。 walkAssignment(node.data, visitor); break; +// 成员写入的对象、属性名与右值,compiler 生成 StoreAttr。 case "MemberAssignment": +// 递归访问 node.data 的 MemberAssignment 子结构或跨度。 walkMemberAssignment(node.data, visitor); break; +// 条件、then 和可选 else,compiler 合并继续路径的初始化集合并生成跳转。 case "If": +// 递归访问 node.data 的 IfStatement 子结构或跨度。 walkIfStatement(node.data, visitor); break; +// 循环条件与体,compiler 生成回边并处理循环可能零次执行。 case "While": +// 递归访问 node.data 的 WhileStatement 子结构或跨度。 walkWhileStatement(node.data, visitor); break; +// 函数返回,compiler 对缺省值补 Nil;当前源码语法要求 return 后有表达式。 case "Return": +// 递归访问 node.data 的 ReturnStatement 子结构或跨度。 walkReturnStatement(node.data, visitor); break; +// 表达式载荷及完整范围,compiler 生成留下一个结果的求值序列。 case "Expression": +// 递归访问 node.data 的 Expr 子结构或跨度。 walkExpr(node.data, visitor); break; +// 顶层/交互声明的种类及完整范围,compiler 按函数或类分派。 case "Declaration": +// 递归访问 node.data 的 Decl 子结构或跨度。 walkDecl(node.data, visitor); break; } } +// 上层 walker 或宿主调用,先通知 Binding 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkBinding(node: Binding, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitBinding?.(node) === false) return; +// 递归访问 node.name 的 Name 子结构或跨度。 walkName(node.name, visitor); +// 递归访问 node.value 的 Expr 子结构或跨度。 walkExpr(node.value, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 Assignment 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkAssignment(node: Assignment, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitAssignment?.(node) === false) return; +// 递归访问 node.name 的 Name 子结构或跨度。 walkName(node.name, visitor); +// 递归访问 node.value 的 Expr 子结构或跨度。 walkExpr(node.value, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 MemberAssignment 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkMemberAssignment(node: MemberAssignment, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitMemberAssignment?.(node) === false) return; +// 递归访问 node.object 的 Expr 子结构或跨度。 walkExpr(node.object, visitor); +// 递归访问 node.name 的 Name 子结构或跨度。 walkName(node.name, visitor); +// 递归访问 node.value 的 Expr 子结构或跨度。 walkExpr(node.value, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 IfStatement 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkIfStatement(node: IfStatement, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitIfStatement?.(node) === false) return; +// 递归访问 node.condition 的 Expr 子结构或跨度。 walkExpr(node.condition, visitor); +// 递归访问 node.body 的 Block 子结构或跨度。 walkBlock(node.body, visitor); +// node.otherwise 可缺省或为 null,只有存在时才访问该子节点。 if (node.otherwise != null) walkBlock(node.otherwise, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 WhileStatement 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkWhileStatement(node: WhileStatement, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitWhileStatement?.(node) === false) return; +// 递归访问 node.condition 的 Expr 子结构或跨度。 walkExpr(node.condition, visitor); +// 递归访问 node.body 的 Block 子结构或跨度。 walkBlock(node.body, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 ReturnStatement 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkReturnStatement(node: ReturnStatement, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitReturnStatement?.(node) === false) return; +// node.value 可缺省或为 null,只有存在时才访问该子节点。 if (node.value != null) walkExpr(node.value, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 InteractiveInput 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkInteractiveInput(node: InteractiveInput, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitInteractiveInput?.(node) === false) return; +// 按声明顺序访问 node.statements 列表中的每个 Stmt 子节点。 for (const value of node.statements) walkStmt(value, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 Expr 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkExpr(node: Expr, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitExpr?.(node) === false) return; +// 递归访问 node.kind 的 ExprKind 子结构或跨度。 walkExprKind(node.kind, visitor); +// 递归访问 node.span 的 span 子结构或跨度。 walkSpan(node.span, visitor); } +// 上层 walker 或宿主调用,先通知 ExprKind 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkExprKind(node: ExprKind, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitExprKind?.(node) === false) return; +// 判别 kind 只选择当前载荷,普通文本/数字拼写没有进一步语法子结构。 switch (node.kind) { +// 变量引用文本,compiler 决定局部槽或全局索引。 case "Name": break; +// 原始十进制拼写,保留全部位数、前导零及尾零,不转浮点。 case "Number": break; +// 已解码受支持转义的拥有文本,原引号范围在外层 Expr。 case "String": break; +// 点开头的符号名,前端可表示,当前 compiler 拒绝生成运行时值。 case "Symbol": +// 递归访问 node.data 的 Name 子结构或跨度。 walkName(node.data, visitor); break; +// 等待子表达式,VM 执行上下文决定是否允许停放。 case "Await": +// 递归访问 node.data 的 Expr 子结构或跨度。 walkExpr(node.data, visitor); break; +// 一元取负,可在纯数值链上合并符号。 case "Unary": +// 递归访问 node.data.operation 的 UnaryOp 子结构或跨度。 walkUnaryOp(node.data.operation, visitor); +// 递归访问 node.data.operand 的 Expr 子结构或跨度。 walkExpr(node.data.operand, visitor); break; +// 左右操作数及算术/比较种类,保留解析优先级树。 case "Binary": +// 递归访问 node.data.operation 的 BinaryOp 子结构或跨度。 walkBinaryOp(node.data.operation, visitor); +// 递归访问 node.data.left 的 Expr 子结构或跨度。 walkExpr(node.data.left, visitor); +// 递归访问 node.data.right 的 Expr 子结构或跨度。 walkExpr(node.data.right, visitor); break; +// 接收对象及属性名的读取。 case "Member": +// 递归访问 node.data.object 的 Expr 子结构或跨度。 walkExpr(node.data.object, visitor); +// 递归访问 node.data.name 的 Name 子结构或跨度。 walkName(node.data.name, visitor); break; +// 被调用表达式与位置实参,由 VM 实际求值。 case "Call": +// 递归访问 node.data.callee 的 Expr 子结构或跨度。 walkExpr(node.data.callee, visitor); +// 按声明顺序访问 node.data.arguments 列表中的每个 Expr 子节点。 for (const value of node.data.arguments) walkExpr(value, visitor); break; } } +// 上层 walker 或宿主调用,先通知 UnaryOp 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkUnaryOp(node: UnaryOp, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitUnaryOp?.(node) === false) return; } +// 上层 walker 或宿主调用,先通知 BinaryOp 回调,再按 ASDL 顺序访问子结构;不修改树。 export function walkBinaryOp(node: BinaryOp, visitor: Visitor): void { +// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。 if (visitor.visitBinaryOp?.(node) === false) return; } diff --git a/crates/frontend/src/ast/generated.rs b/crates/frontend/src/ast/generated.rs index c6c4a38..6ad186e 100644 --- a/crates/frontend/src/ast/generated.rs +++ b/crates/frontend/src/ast/generated.rs @@ -2,23 +2,33 @@ use super::Spanned; use crate::Span; +/// wire/编辑器核对的协议身份,来自 ASDL 原文哈希;生成节点注释不参与此值。 pub const AST_SCHEMA_ID: &str = "urn:endor:ast:sha256:21b0349ac295d9bc8cedcad8f07d8e2d45cf220e19e7c2fad10269555fcfd71a"; +/// 宿主/编辑器传输的版本化语法文档,wire 校验后供 compiler 使用。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct AstDocument { + /// 协议身份,wire 按 AST_SCHEMA_ID 核对;不是源码内容哈希。 pub schema: String, + /// 拥有的语法根,文档校验及编译从这里递归。 pub root: AstRoot, } +/// parse_ast 按模式构造的模块、表达式或交互根,compiler 据此选择生成入口。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(tag = "kind", content = "data", deny_unknown_fields)] pub enum AstRoot { + /// 模块根,包含函数/类声明。 Module(Program), + /// 表达式根,编译后直接返回求值结果。 Expression(Expr), + /// 交互根,包含一次完整提交。 Interactive(InteractiveInput), } impl Spanned for AstRoot { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { + // 按实际构造器取得唯一可定位子节点的范围。 match self { Self::Module(node) => node.span(), Self::Expression(node) => node.span(), @@ -26,129 +36,184 @@ impl Spanned for AstRoot { } } } +/// parser 复制的名称文本与位置,用于声明、类型注解或属性名。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct Name { + /// 拥有名称文本,不依赖输入缓冲存活。 pub text: String, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for Name { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 模块声明序列,compiler 先注册全部顶层名称再分析各函数体。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct Program { + /// 按源码顺序的声明;源码模块解析要求非空,文档结构本身不限制数量。 pub declarations: Vec, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for Program { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 顶层/交互声明的种类及完整范围,compiler 按函数或类分派。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct Decl { + /// 函数/类声明载荷,JSON 和类型采用 kind/data 标签。 pub kind: DeclKind, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for Decl { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 函数与类声明的判别载荷,外层 Decl 另外保存完整跨度。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(tag = "kind", content = "data", deny_unknown_fields)] pub enum DeclKind { + /// 显式参数/返回注解与函数体,compiler 生成独立模板及源码签名;解析本身不调用函数。 Function(Function), + /// 类名、可选基类、字段和方法,compiler 检查成员后生成构类指令。 Class(Class), } impl Spanned for DeclKind { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { + // 按实际构造器取得唯一可定位子节点的范围。 match self { Self::Function(node) => node.span(), Self::Class(node) => node.span(), } } } +/// 类名、可选基类、字段和方法,compiler 检查成员后生成构类指令。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct Class { + /// 类声明名与精确位置。 pub name: Name, + /// 显式基类名,可省略;当前 compiler 对省略项使用 Object。 pub base: Option, + /// 按出现顺序的字段名称/类型注解,不含方法。 pub fields: Vec, + /// 按出现顺序的方法;显式 self 约束由 compiler 检查。 pub methods: Vec, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for Class { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 显式参数/返回注解与函数体,compiler 生成独立模板及源码签名;解析本身不调用函数。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct Function { + /// 函数/方法名,代码模板沿用为诊断名称。 pub name: Name, + /// 位置参数列表,compiler 依次分配前部局部槽。 pub parameters: Vec, + /// 返回类型名,生成源码签名后由运行时解析。 pub return_type: Name, + /// 独立函数作用域的语句体,当前源码不支持嵌套声明闭包。 pub body: Block, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for Function { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 函数参数或类字段共用的名称/类型注解,实际类型身份由运行时环境解析。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct Parameter { + /// 参数/字段名,用于布局和重复检查。 pub name: Name, + /// 声明类型名,前端只保留文本与位置。 pub type_name: Name, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for Parameter { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 顺序语句块,compiler 检查确定初始化并按语句生成指令。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct Block { + /// 有序语句列表,不引入独立块级局部作用域。 pub statements: Vec, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for Block { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 语句种类及原文范围,compiler 处理副作用、控制流或返回。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct Stmt { + /// 具体语句载荷,外层另存整句范围。 pub kind: StmtKind, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for Stmt { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 语句判别载荷,parser 选择分支,compiler 按绑定、赋值、控制流等规则处理。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(tag = "kind", content = "data", deny_unknown_fields)] pub enum StmtKind { + /// let 新绑定,compiler 检查重名并分析右值后才标记目标已初始化。 Binding(Binding), + /// 普通名称赋值,compiler 按作用域写局部槽或全局环境。 Assignment(Assignment), + /// 成员写入的对象、属性名与右值,compiler 生成 StoreAttr。 MemberAssignment(MemberAssignment), + /// 条件、then 和可选 else,compiler 合并继续路径的初始化集合并生成跳转。 If(IfStatement), + /// 循环条件与体,compiler 生成回边并处理循环可能零次执行。 While(WhileStatement), + /// 函数返回,compiler 对缺省值补 Nil;当前源码语法要求 return 后有表达式。 Return(ReturnStatement), + /// 表达式载荷及完整范围,compiler 生成留下一个结果的求值序列。 Expression(Expr), + /// 顶层/交互声明的种类及完整范围,compiler 按函数或类分派。 Declaration(Decl), } impl Spanned for StmtKind { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { + // 按实际构造器取得唯一可定位子节点的范围。 match self { Self::Binding(node) => node.span(), Self::Assignment(node) => node.span(), @@ -161,141 +226,212 @@ impl Spanned for StmtKind { } } } +/// let 新绑定,compiler 检查重名并分析右值后才标记目标已初始化。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct Binding { + /// let 目标名,重复声明由语义分析拒绝。 pub name: Name, + /// 绑定右值,本局部不能在初始化前读取自身。 pub value: Expr, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for Binding { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 普通名称赋值,compiler 按作用域写局部槽或全局环境。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct Assignment { + /// 普通赋值目标,函数内首次赋值可预分配局部槽。 pub name: Name, + /// 先求右值再更新目标。 pub value: Expr, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for Assignment { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 成员写入的对象、属性名与右值,compiler 生成 StoreAttr。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct MemberAssignment { + /// 属性写入接收对象表达式,前端不求值。 pub object: Expr, + /// 待写属性名,compiler 加入名字表。 pub name: Name, + /// 待写右值,当前 compiler 先于接收对象生成求值以匹配 VM 栈布局。 pub value: Expr, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for MemberAssignment { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 条件、then 和可选 else,compiler 合并继续路径的初始化集合并生成跳转。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct IfStatement { + /// 条件表达式,运行时按真值协议选择分支。 pub condition: Expr, + /// 条件为真的 then 块。 pub body: Block, + /// 可选 else 块,缺省时存在直接继续路径。 pub otherwise: Option, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for IfStatement { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 循环条件与体,compiler 生成回边并处理循环可能零次执行。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct WhileStatement { + /// 每轮重新求值的条件,假值退出。 pub condition: Expr, + /// 循环体,可能一次也不执行。 pub body: Block, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for WhileStatement { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 函数返回,compiler 对缺省值补 Nil;当前源码语法要求 return 后有表达式。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct ReturnStatement { + /// 可选返回表达式,手工 AST 缺省时由 compiler 补 Nil。 pub value: Option, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for ReturnStatement { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 单次交互提交容器,parser 产生一条语句,compiler 也检查数量,不保存宿主会话环境。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct InteractiveInput { + /// 当前 parse/compile 约定恰好一个完整提交的语句。 pub statements: Vec, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for InteractiveInput { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 表达式载荷及完整范围,compiler 生成留下一个结果的求值序列。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct Expr { + /// 求值形状,不缓存名称解析或运行时类型。 pub kind: ExprKind, + /// 原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。 pub span: Span, } impl Spanned for Expr { + /// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。 fn span(&self) -> Span { self.span } } +/// 名称、字面量、等待、运算和调用等表达式载荷,范围由外层 Expr 提供。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(tag = "kind", content = "data", deny_unknown_fields)] pub enum ExprKind { + /// 变量引用文本,compiler 决定局部槽或全局索引。 Name(String), + /// 原始十进制拼写,保留全部位数、前导零及尾零,不转浮点。 Number(String), + /// 已解码受支持转义的拥有文本,原引号范围在外层 Expr。 String(String), + /// 点开头的符号名,前端可表示,当前 compiler 拒绝生成运行时值。 Symbol(Name), + /// 等待子表达式,VM 执行上下文决定是否允许停放。 Await(Box), + /// 一元取负,可在纯数值链上合并符号。 Unary { + /// 运算种类,决定算术、比较或取负指令。 operation: UnaryOp, + /// 一元子表达式,Rust 的递归回边间接存储。 operand: Box, }, + /// 左右操作数及算术/比较种类,保留解析优先级树。 Binary { + /// 运算种类,决定算术、比较或取负指令。 operation: BinaryOp, + /// 二元左操作数,当前 compiler 先于右侧求值。 left: Box, + /// 二元右操作数,与左侧一起被运行时运算消费。 right: Box, }, + /// 接收对象及属性名的读取。 Member { + /// 成员读取接收对象。 object: Box, + /// 成员或符号名,不是已解析的运行时属性句柄。 name: Name, }, + /// 被调用表达式与位置实参,由 VM 实际求值。 Call { + /// 调用目标,先于实参求值。 callee: Box, + /// 有序位置实参,AST 不隐式添加接收者。 arguments: Vec, }, } +/// 一元运算种类,compiler 合并纯数值负号或生成运行时取负。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Copy)] pub enum UnaryOp { + /// 一元取负。 Negate, } +/// 二元算术/比较种类,parser 保留优先级树,compiler 映射为对应 VM 操作。 #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Copy)] pub enum BinaryOp { + /// 加法。 Add, + /// 减法。 Subtract, + /// 乘法。 Multiply, + /// 除法。 Divide, + /// 相等比较。 Equal, + /// 不等比较。 NotEqual, + /// 小于比较。 Less, + /// 小于等于比较。 LessEqual, + /// 大于比较。 Greater, + /// 大于等于比较。 GreaterEqual, } diff --git a/crates/frontend/src/ast/visitor.rs b/crates/frontend/src/ast/visitor.rs index c8bd1ea..3ba8f16 100644 --- a/crates/frontend/src/ast/visitor.rs +++ b/crates/frontend/src/ast/visitor.rs @@ -6,382 +6,566 @@ use super::{ ReturnStatement, Spanned, Stmt, StmtKind, UnaryOp, WhileStatement, }; use crate::Span; +/// wire 校验与宿主分析实现的借用节点访问协议;默认按 ASDL 字段顺序递归,不修改树。 +/// 覆盖 visit 方法后可显式调用对应 walk 继续子遍历;walk 本身不执行当前节点的进入/退出钩子。 pub trait Visitor { + /// 实现者选择的错误类型,任一子访问失败即向上返回。 type Error; + /// 默认可定位 visit 在遍历前调用;失败时本节点尚未成功进入,不再调用它的 leave_node。 fn enter_node(&mut self, _span: Span) -> Result<(), Self::Error> { Ok(()) } + /// 已成功进入节点的遍历结束后调用,子节点错误也需退出;默认无操作。 fn leave_node(&mut self) {} + /// walk 遇到显式 span 字段时调用,默认不处理;区别于围绕整个节点的 enter_node。 fn visit_span(&mut self, _span: &Span) -> Result<(), Self::Error> { Ok(()) } + /// 上层 walk 或宿主调用的 AstDocument 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_ast_document(&mut self, node: &AstDocument) -> Result<(), Self::Error> { walk_ast_document(self, node) } + /// 上层 walk 或宿主调用的 AstRoot 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_ast_root(&mut self, node: &AstRoot) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_ast_root(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 Name 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_name(&mut self, node: &Name) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_name(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 Program 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_program(&mut self, node: &Program) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_program(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 Decl 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_decl(&mut self, node: &Decl) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_decl(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 DeclKind 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_decl_kind(&mut self, node: &DeclKind) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_decl_kind(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 Class 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_class(&mut self, node: &Class) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_class(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 Function 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_function(&mut self, node: &Function) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_function(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 Parameter 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_parameter(&mut self, node: &Parameter) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_parameter(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 Block 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_block(&mut self, node: &Block) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_block(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 Stmt 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_stmt(&mut self, node: &Stmt) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_stmt(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 StmtKind 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_stmt_kind(&mut self, node: &StmtKind) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_stmt_kind(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 Binding 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_binding(&mut self, node: &Binding) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_binding(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 Assignment 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_assignment(&mut self, node: &Assignment) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_assignment(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 MemberAssignment 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_member_assignment(&mut self, node: &MemberAssignment) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_member_assignment(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 IfStatement 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_if_statement(&mut self, node: &IfStatement) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_if_statement(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 WhileStatement 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_while_statement(&mut self, node: &WhileStatement) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_while_statement(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 ReturnStatement 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_return_statement(&mut self, node: &ReturnStatement) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_return_statement(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 InteractiveInput 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_interactive_input(&mut self, node: &InteractiveInput) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_interactive_input(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 Expr 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_expr(&mut self, node: &Expr) -> Result<(), Self::Error> { + // 先进入;进入失败则不启动子遍历。 self.enter_node(node.span())?; + // 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。 let result = walk_expr(self, node); self.leave_node(); result } + /// 上层 walk 或宿主调用的 ExprKind 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_expr_kind(&mut self, node: &ExprKind) -> Result<(), Self::Error> { walk_expr_kind(self, node) } + /// 上层 walk 或宿主调用的 UnaryOp 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_unary_op(&mut self, node: &UnaryOp) -> Result<(), Self::Error> { walk_unary_op(self, node) } + /// 上层 walk 或宿主调用的 BinaryOp 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。 fn visit_binary_op(&mut self, node: &BinaryOp) -> Result<(), Self::Error> { walk_binary_op(self, node) } } +/// 默认 visit_ast_document 或自定义访问器调用,按字段顺序借用 AstDocument 的子结构;本函数不进入/退出当前节点。 pub fn walk_ast_document( visitor: &mut V, node: &AstDocument, ) -> Result<(), V::Error> { + // 访问 &node.root 的 AstRoot 子结构或跨度,借用不转移节点所有权。 visitor.visit_ast_root(&node.root)?; Ok(()) } +/// 默认 visit_ast_root 或自定义访问器调用,按字段顺序借用 AstRoot 的子结构;本函数不进入/退出当前节点。 pub fn walk_ast_root(visitor: &mut V, node: &AstRoot) -> Result<(), V::Error> { + // 按构造器只递归当前实际载荷,字符串等标量没有语法子节点。 match node { + // 模块根,包含函数/类声明。 AstRoot::Module(value) => { + // 访问 value 的 Program 子结构或跨度,借用不转移节点所有权。 visitor.visit_program(value)?; } + // 表达式根,编译后直接返回求值结果。 AstRoot::Expression(value) => { + // 访问 value 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(value)?; } + // 交互根,包含一次完整提交。 AstRoot::Interactive(value) => { + // 访问 value 的 InteractiveInput 子结构或跨度,借用不转移节点所有权。 visitor.visit_interactive_input(value)?; } } Ok(()) } +/// 默认 visit_name 或自定义访问器调用,按字段顺序借用 Name 的子结构;本函数不进入/退出当前节点。 pub fn walk_name(visitor: &mut V, node: &Name) -> Result<(), V::Error> { + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_program 或自定义访问器调用,按字段顺序借用 Program 的子结构;本函数不进入/退出当前节点。 pub fn walk_program(visitor: &mut V, node: &Program) -> Result<(), V::Error> { + // 按顺序借用 &node.declarations 中每个 Decl,任一访问失败立即向上传播。 for value in &node.declarations { visitor.visit_decl(value)?; } + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_decl 或自定义访问器调用,按字段顺序借用 Decl 的子结构;本函数不进入/退出当前节点。 pub fn walk_decl(visitor: &mut V, node: &Decl) -> Result<(), V::Error> { + // 访问 &node.kind 的 DeclKind 子结构或跨度,借用不转移节点所有权。 visitor.visit_decl_kind(&node.kind)?; + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_decl_kind 或自定义访问器调用,按字段顺序借用 DeclKind 的子结构;本函数不进入/退出当前节点。 pub fn walk_decl_kind( visitor: &mut V, node: &DeclKind, ) -> Result<(), V::Error> { + // 按构造器只递归当前实际载荷,字符串等标量没有语法子节点。 match node { + // 显式参数/返回注解与函数体,compiler 生成独立模板及源码签名;解析本身不调用函数。 DeclKind::Function(value) => { + // 访问 value 的 Function 子结构或跨度,借用不转移节点所有权。 visitor.visit_function(value)?; } + // 类名、可选基类、字段和方法,compiler 检查成员后生成构类指令。 DeclKind::Class(value) => { + // 访问 value 的 Class 子结构或跨度,借用不转移节点所有权。 visitor.visit_class(value)?; } } Ok(()) } +/// 默认 visit_class 或自定义访问器调用,按字段顺序借用 Class 的子结构;本函数不进入/退出当前节点。 pub fn walk_class(visitor: &mut V, node: &Class) -> Result<(), V::Error> { + // 访问 &node.name 的 Name 子结构或跨度,借用不转移节点所有权。 visitor.visit_name(&node.name)?; + // &node.base 是可选字段,仅 Some 时访问其 Name 子节点。 if let Some(value) = &node.base { visitor.visit_name(value)?; } + // 按顺序借用 &node.fields 中每个 Parameter,任一访问失败立即向上传播。 for value in &node.fields { visitor.visit_parameter(value)?; } + // 按顺序借用 &node.methods 中每个 Function,任一访问失败立即向上传播。 for value in &node.methods { visitor.visit_function(value)?; } + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_function 或自定义访问器调用,按字段顺序借用 Function 的子结构;本函数不进入/退出当前节点。 pub fn walk_function( visitor: &mut V, node: &Function, ) -> Result<(), V::Error> { + // 访问 &node.name 的 Name 子结构或跨度,借用不转移节点所有权。 visitor.visit_name(&node.name)?; + // 按顺序借用 &node.parameters 中每个 Parameter,任一访问失败立即向上传播。 for value in &node.parameters { visitor.visit_parameter(value)?; } + // 访问 &node.return_type 的 Name 子结构或跨度,借用不转移节点所有权。 visitor.visit_name(&node.return_type)?; + // 访问 &node.body 的 Block 子结构或跨度,借用不转移节点所有权。 visitor.visit_block(&node.body)?; + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_parameter 或自定义访问器调用,按字段顺序借用 Parameter 的子结构;本函数不进入/退出当前节点。 pub fn walk_parameter( visitor: &mut V, node: &Parameter, ) -> Result<(), V::Error> { + // 访问 &node.name 的 Name 子结构或跨度,借用不转移节点所有权。 visitor.visit_name(&node.name)?; + // 访问 &node.type_name 的 Name 子结构或跨度,借用不转移节点所有权。 visitor.visit_name(&node.type_name)?; + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_block 或自定义访问器调用,按字段顺序借用 Block 的子结构;本函数不进入/退出当前节点。 pub fn walk_block(visitor: &mut V, node: &Block) -> Result<(), V::Error> { + // 按顺序借用 &node.statements 中每个 Stmt,任一访问失败立即向上传播。 for value in &node.statements { visitor.visit_stmt(value)?; } + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_stmt 或自定义访问器调用,按字段顺序借用 Stmt 的子结构;本函数不进入/退出当前节点。 pub fn walk_stmt(visitor: &mut V, node: &Stmt) -> Result<(), V::Error> { + // 访问 &node.kind 的 StmtKind 子结构或跨度,借用不转移节点所有权。 visitor.visit_stmt_kind(&node.kind)?; + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_stmt_kind 或自定义访问器调用,按字段顺序借用 StmtKind 的子结构;本函数不进入/退出当前节点。 pub fn walk_stmt_kind( visitor: &mut V, node: &StmtKind, ) -> Result<(), V::Error> { + // 按构造器只递归当前实际载荷,字符串等标量没有语法子节点。 match node { + // let 新绑定,compiler 检查重名并分析右值后才标记目标已初始化。 StmtKind::Binding(value) => { + // 访问 value 的 Binding 子结构或跨度,借用不转移节点所有权。 visitor.visit_binding(value)?; } + // 普通名称赋值,compiler 按作用域写局部槽或全局环境。 StmtKind::Assignment(value) => { + // 访问 value 的 Assignment 子结构或跨度,借用不转移节点所有权。 visitor.visit_assignment(value)?; } + // 成员写入的对象、属性名与右值,compiler 生成 StoreAttr。 StmtKind::MemberAssignment(value) => { + // 访问 value 的 MemberAssignment 子结构或跨度,借用不转移节点所有权。 visitor.visit_member_assignment(value)?; } + // 条件、then 和可选 else,compiler 合并继续路径的初始化集合并生成跳转。 StmtKind::If(value) => { + // 访问 value 的 IfStatement 子结构或跨度,借用不转移节点所有权。 visitor.visit_if_statement(value)?; } + // 循环条件与体,compiler 生成回边并处理循环可能零次执行。 StmtKind::While(value) => { + // 访问 value 的 WhileStatement 子结构或跨度,借用不转移节点所有权。 visitor.visit_while_statement(value)?; } + // 函数返回,compiler 对缺省值补 Nil;当前源码语法要求 return 后有表达式。 StmtKind::Return(value) => { + // 访问 value 的 ReturnStatement 子结构或跨度,借用不转移节点所有权。 visitor.visit_return_statement(value)?; } + // 表达式载荷及完整范围,compiler 生成留下一个结果的求值序列。 StmtKind::Expression(value) => { + // 访问 value 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(value)?; } + // 顶层/交互声明的种类及完整范围,compiler 按函数或类分派。 StmtKind::Declaration(value) => { + // 访问 value 的 Decl 子结构或跨度,借用不转移节点所有权。 visitor.visit_decl(value)?; } } Ok(()) } +/// 默认 visit_binding 或自定义访问器调用,按字段顺序借用 Binding 的子结构;本函数不进入/退出当前节点。 pub fn walk_binding(visitor: &mut V, node: &Binding) -> Result<(), V::Error> { + // 访问 &node.name 的 Name 子结构或跨度,借用不转移节点所有权。 visitor.visit_name(&node.name)?; + // 访问 &node.value 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(&node.value)?; + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_assignment 或自定义访问器调用,按字段顺序借用 Assignment 的子结构;本函数不进入/退出当前节点。 pub fn walk_assignment( visitor: &mut V, node: &Assignment, ) -> Result<(), V::Error> { + // 访问 &node.name 的 Name 子结构或跨度,借用不转移节点所有权。 visitor.visit_name(&node.name)?; + // 访问 &node.value 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(&node.value)?; + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_member_assignment 或自定义访问器调用,按字段顺序借用 MemberAssignment 的子结构;本函数不进入/退出当前节点。 pub fn walk_member_assignment( visitor: &mut V, node: &MemberAssignment, ) -> Result<(), V::Error> { + // 访问 &node.object 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(&node.object)?; + // 访问 &node.name 的 Name 子结构或跨度,借用不转移节点所有权。 visitor.visit_name(&node.name)?; + // 访问 &node.value 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(&node.value)?; + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_if_statement 或自定义访问器调用,按字段顺序借用 IfStatement 的子结构;本函数不进入/退出当前节点。 pub fn walk_if_statement( visitor: &mut V, node: &IfStatement, ) -> Result<(), V::Error> { + // 访问 &node.condition 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(&node.condition)?; + // 访问 &node.body 的 Block 子结构或跨度,借用不转移节点所有权。 visitor.visit_block(&node.body)?; + // &node.otherwise 是可选字段,仅 Some 时访问其 Block 子节点。 if let Some(value) = &node.otherwise { visitor.visit_block(value)?; } + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_while_statement 或自定义访问器调用,按字段顺序借用 WhileStatement 的子结构;本函数不进入/退出当前节点。 pub fn walk_while_statement( visitor: &mut V, node: &WhileStatement, ) -> Result<(), V::Error> { + // 访问 &node.condition 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(&node.condition)?; + // 访问 &node.body 的 Block 子结构或跨度,借用不转移节点所有权。 visitor.visit_block(&node.body)?; + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_return_statement 或自定义访问器调用,按字段顺序借用 ReturnStatement 的子结构;本函数不进入/退出当前节点。 pub fn walk_return_statement( visitor: &mut V, node: &ReturnStatement, ) -> Result<(), V::Error> { + // &node.value 是可选字段,仅 Some 时访问其 Expr 子节点。 if let Some(value) = &node.value { visitor.visit_expr(value)?; } + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_interactive_input 或自定义访问器调用,按字段顺序借用 InteractiveInput 的子结构;本函数不进入/退出当前节点。 pub fn walk_interactive_input( visitor: &mut V, node: &InteractiveInput, ) -> Result<(), V::Error> { + // 按顺序借用 &node.statements 中每个 Stmt,任一访问失败立即向上传播。 for value in &node.statements { visitor.visit_stmt(value)?; } + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_expr 或自定义访问器调用,按字段顺序借用 Expr 的子结构;本函数不进入/退出当前节点。 pub fn walk_expr(visitor: &mut V, node: &Expr) -> Result<(), V::Error> { + // 访问 &node.kind 的 ExprKind 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr_kind(&node.kind)?; + // 访问 &node.span 的 span 子结构或跨度,借用不转移节点所有权。 visitor.visit_span(&node.span)?; Ok(()) } +/// 默认 visit_expr_kind 或自定义访问器调用,按字段顺序借用 ExprKind 的子结构;本函数不进入/退出当前节点。 pub fn walk_expr_kind( visitor: &mut V, node: &ExprKind, ) -> Result<(), V::Error> { + // 按构造器只递归当前实际载荷,字符串等标量没有语法子节点。 match node { + // 变量引用文本,compiler 决定局部槽或全局索引。 ExprKind::Name(_) => {} + // 原始十进制拼写,保留全部位数、前导零及尾零,不转浮点。 ExprKind::Number(_) => {} + // 已解码受支持转义的拥有文本,原引号范围在外层 Expr。 ExprKind::String(_) => {} + // 点开头的符号名,前端可表示,当前 compiler 拒绝生成运行时值。 ExprKind::Symbol(value) => { + // 访问 value 的 Name 子结构或跨度,借用不转移节点所有权。 visitor.visit_name(value)?; } + // 等待子表达式,VM 执行上下文决定是否允许停放。 ExprKind::Await(value) => { + // 访问 value 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(value)?; } + // 一元取负,可在纯数值链上合并符号。 ExprKind::Unary { operation, operand, .. } => { + // 访问 operation 的 UnaryOp 子结构或跨度,借用不转移节点所有权。 visitor.visit_unary_op(operation)?; + // 访问 operand 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(operand)?; } + // 左右操作数及算术/比较种类,保留解析优先级树。 ExprKind::Binary { operation, left, right, .. } => { + // 访问 operation 的 BinaryOp 子结构或跨度,借用不转移节点所有权。 visitor.visit_binary_op(operation)?; + // 访问 left 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(left)?; + // 访问 right 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(right)?; } + // 接收对象及属性名的读取。 ExprKind::Member { object, name, .. } => { + // 访问 object 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(object)?; + // 访问 name 的 Name 子结构或跨度,借用不转移节点所有权。 visitor.visit_name(name)?; } + // 被调用表达式与位置实参,由 VM 实际求值。 ExprKind::Call { callee, arguments, .. } => { + // 访问 callee 的 Expr 子结构或跨度,借用不转移节点所有权。 visitor.visit_expr(callee)?; + // 按顺序借用 arguments 中每个 Expr,任一访问失败立即向上传播。 for value in arguments { visitor.visit_expr(value)?; } @@ -389,12 +573,14 @@ pub fn walk_expr_kind( } Ok(()) } +/// 默认 visit_unary_op 或自定义访问器调用,按字段顺序借用 UnaryOp 的子结构;本函数不进入/退出当前节点。 pub fn walk_unary_op( _visitor: &mut V, _node: &UnaryOp, ) -> Result<(), V::Error> { Ok(()) } +/// 默认 visit_binary_op 或自定义访问器调用,按字段顺序借用 BinaryOp 的子结构;本函数不进入/退出当前节点。 pub fn walk_binary_op( _visitor: &mut V, _node: &BinaryOp, diff --git a/scripts/generate_ast.py b/scripts/generate_ast.py index cce2d9b..54611d7 100644 --- a/scripts/generate_ast.py +++ b/scripts/generate_ast.py @@ -23,6 +23,123 @@ HEADER = "// Generated by scripts/generate_ast.py from Endor.asdl. Do not edit.\ RESERVED = set("as break const continue crate else enum extern false fn for if impl in let loop match mod move mut pub ref return self Self static struct super trait true type unsafe use where while async await dyn abstract become box do final macro override priv typeof unsized virtual yield try gen".split()) +# 说明与 ASDL 分开维护,仅输出源码注释,不改变按 ASDL 原文计算的协议身份。 +TYPE_NOTES = { + "AstDocument": "宿主/编辑器传输的版本化语法文档,wire 校验后供 compiler 使用。", + "AstRoot": "parse_ast 按模式构造的模块、表达式或交互根,compiler 据此选择生成入口。", + "Name": "parser 复制的名称文本与位置,用于声明、类型注解或属性名。", + "Program": "模块声明序列,compiler 先注册全部顶层名称再分析各函数体。", + "Decl": "顶层/交互声明的种类及完整范围,compiler 按函数或类分派。", + "DeclKind": "函数与类声明的判别载荷,外层 Decl 另外保存完整跨度。", + "Class": "类名、可选基类、字段和方法,compiler 检查成员后生成构类指令。", + "Function": "显式参数/返回注解与函数体,compiler 生成独立模板及源码签名;解析本身不调用函数。", + "Parameter": "函数参数或类字段共用的名称/类型注解,实际类型身份由运行时环境解析。", + "Block": "顺序语句块,compiler 检查确定初始化并按语句生成指令。", + "Stmt": "语句种类及原文范围,compiler 处理副作用、控制流或返回。", + "StmtKind": "语句判别载荷,parser 选择分支,compiler 按绑定、赋值、控制流等规则处理。", + "Binding": "let 新绑定,compiler 检查重名并分析右值后才标记目标已初始化。", + "Assignment": "普通名称赋值,compiler 按作用域写局部槽或全局环境。", + "MemberAssignment": "成员写入的对象、属性名与右值,compiler 生成 StoreAttr。", + "IfStatement": "条件、then 和可选 else,compiler 合并继续路径的初始化集合并生成跳转。", + "WhileStatement": "循环条件与体,compiler 生成回边并处理循环可能零次执行。", + "ReturnStatement": "函数返回,compiler 对缺省值补 Nil;当前源码语法要求 return 后有表达式。", + "InteractiveInput": "单次交互提交容器,parser 产生一条语句,compiler 也检查数量,不保存宿主会话环境。", + "Expr": "表达式载荷及完整范围,compiler 生成留下一个结果的求值序列。", + "ExprKind": "名称、字面量、等待、运算和调用等表达式载荷,范围由外层 Expr 提供。", + "UnaryOp": "一元运算种类,compiler 合并纯数值负号或生成运行时取负。", + "BinaryOp": "二元算术/比较种类,parser 保留优先级树,compiler 映射为对应 VM 操作。", +} +# 字段说明以拥有类型和字段名为键,不将不同节点的同名字段混为相同用途。 +FIELD_NOTES = { + ("AstDocument", "schema"): "协议身份,wire 按 AST_SCHEMA_ID 核对;不是源码内容哈希。", + ("AstDocument", "root"): "拥有的语法根,文档校验及编译从这里递归。", + ("Name", "text"): "拥有名称文本,不依赖输入缓冲存活。", + ("Program", "declarations"): "按源码顺序的声明;源码模块解析要求非空,文档结构本身不限制数量。", + ("Decl", "kind"): "函数/类声明载荷,JSON 和类型采用 kind/data 标签。", + ("Class", "name"): "类声明名与精确位置。", + ("Class", "base"): "显式基类名,可省略;当前 compiler 对省略项使用 Object。", + ("Class", "fields"): "按出现顺序的字段名称/类型注解,不含方法。", + ("Class", "methods"): "按出现顺序的方法;显式 self 约束由 compiler 检查。", + ("Function", "name"): "函数/方法名,代码模板沿用为诊断名称。", + ("Function", "parameters"): "位置参数列表,compiler 依次分配前部局部槽。", + ("Function", "return_type"): "返回类型名,生成源码签名后由运行时解析。", + ("Function", "body"): "独立函数作用域的语句体,当前源码不支持嵌套声明闭包。", + ("Parameter", "name"): "参数/字段名,用于布局和重复检查。", + ("Parameter", "type_name"): "声明类型名,前端只保留文本与位置。", + ("Block", "statements"): "有序语句列表,不引入独立块级局部作用域。", + ("Stmt", "kind"): "具体语句载荷,外层另存整句范围。", + ("Binding", "name"): "let 目标名,重复声明由语义分析拒绝。", + ("Binding", "value"): "绑定右值,本局部不能在初始化前读取自身。", + ("Assignment", "name"): "普通赋值目标,函数内首次赋值可预分配局部槽。", + ("Assignment", "value"): "先求右值再更新目标。", + ("MemberAssignment", "object"): "属性写入接收对象表达式,前端不求值。", + ("MemberAssignment", "name"): "待写属性名,compiler 加入名字表。", + ("MemberAssignment", "value"): "待写右值,当前 compiler 先于接收对象生成求值以匹配 VM 栈布局。", + ("IfStatement", "condition"): "条件表达式,运行时按真值协议选择分支。", + ("IfStatement", "body"): "条件为真的 then 块。", + ("IfStatement", "otherwise"): "可选 else 块,缺省时存在直接继续路径。", + ("WhileStatement", "condition"): "每轮重新求值的条件,假值退出。", + ("WhileStatement", "body"): "循环体,可能一次也不执行。", + ("ReturnStatement", "value"): "可选返回表达式,手工 AST 缺省时由 compiler 补 Nil。", + ("InteractiveInput", "statements"): "当前 parse/compile 约定恰好一个完整提交的语句。", + ("Expr", "kind"): "求值形状,不缓存名称解析或运行时类型。", + ("ExprKind", "operation"): "运算种类,决定算术、比较或取负指令。", + ("ExprKind", "operand"): "一元子表达式,Rust 的递归回边间接存储。", + ("ExprKind", "left"): "二元左操作数,当前 compiler 先于右侧求值。", + ("ExprKind", "right"): "二元右操作数,与左侧一起被运行时运算消费。", + ("ExprKind", "object"): "成员读取接收对象。", + ("ExprKind", "name"): "成员或符号名,不是已解析的运行时属性句柄。", + ("ExprKind", "callee"): "调用目标,先于实参求值。", + ("ExprKind", "arguments"): "有序位置实参,AST 不隐式添加接收者。", +} +VARIANT_NOTES = { + ("AstRoot", "Module"): "模块根,包含函数/类声明。", + ("AstRoot", "Expression"): "表达式根,编译后直接返回求值结果。", + ("AstRoot", "Interactive"): "交互根,包含一次完整提交。", + ("ExprKind", "Name"): "变量引用文本,compiler 决定局部槽或全局索引。", + ("ExprKind", "Number"): "原始十进制拼写,保留全部位数、前导零及尾零,不转浮点。", + ("ExprKind", "String"): "已解码受支持转义的拥有文本,原引号范围在外层 Expr。", + ("ExprKind", "Symbol"): "点开头的符号名,前端可表示,当前 compiler 拒绝生成运行时值。", + ("ExprKind", "Await"): "等待子表达式,VM 执行上下文决定是否允许停放。", + ("ExprKind", "Unary"): "一元取负,可在纯数值链上合并符号。", + ("ExprKind", "Binary"): "左右操作数及算术/比较种类,保留解析优先级树。", + ("ExprKind", "Member"): "接收对象及属性名的读取。", + ("ExprKind", "Call"): "被调用表达式与位置实参,由 VM 实际求值。", + ("UnaryOp", "Negate"): "一元取负。", +} + + +# 操作符叶子没有子节点,仍需给各构造器明确语义;扩展项继续使用通用说明。 +VARIANT_NOTES.update({("BinaryOp", name): note for name, note in { + "Add": "加法。", "Subtract": "减法。", "Multiply": "乘法。", "Divide": "除法。", + "Equal": "相等比较。", "NotEqual": "不等比较。", "Less": "小于比较。", + "LessEqual": "小于等于比较。", "Greater": "大于比较。", "GreaterEqual": "大于等于比较。", +}.items()}) + + +# Rust/TS 类型声明生成共用,未记录的扩展节点仍能按结构正常生成。 +def type_note(name): + return TYPE_NOTES.get(name, f"ASDL 定义的 {name} 节点,由宿主构造并由访问器消费。") + + +# 字段说明共用;span 的位置协议在所有节点中保持一致。 +def field_note(owner, field): + # 内置 span 的统一位置协议优先于拥有节点的特定字段含义。 + if field.type == "span": + return "原始 UTF-8 文本的半开字节范围,定位与父子范围校验共用;不是字符列。" + return FIELD_NOTES.get((owner, field.name), f"{field.name} 保存 {field.type} 类型" + ("的有序列表。" if field.quantifier == "*" else "的可选值。" if field.quantifier == "?" else "的值。")) + + +# 和类型说明共用,包装已知节点时复用其语义,否则按构造器形状提供结构说明。 +def variant_note(owner, variant): + # 单载荷包装已知节点时复用节点说明,未知扩展构造器仍保留通用结构说明。 + if len(variant.fields) == 1 and variant.fields[0].type in TYPE_NOTES: + fallback = type_note(variant.fields[0].type) + else: + fallback = f"{owner} 的 {variant.name} 构造器,载荷由 ASDL 字段定义。" + return VARIANT_NOTES.get((owner, variant.name), fallback) + + @dataclass(frozen=True) # 不可变字段描述,由 parse_schema 构造,Rust/TS/JSON 及遍历器生成阶段共同消费。 class Field: @@ -264,10 +381,11 @@ def rust_types(definitions, schema_id): value = f"Box<{value}>" return f"Option<{value}>" if field.quantifier == "?" else value - lines = [HEADER, "use crate::Span;", "use super::Spanned;", f'pub const AST_SCHEMA_ID: &str = "{schema_id}";'] + lines = [HEADER, "use crate::Span;", "use super::Spanned;", "/// wire/编辑器核对的协议身份,来自 ASDL 原文哈希;生成节点注释不参与此值。", f'pub const AST_SCHEMA_ID: &str = "{schema_id}";'] # 依稳定 schema 顺序处理每个定义,保留生成结果的可重复性。 for definition in definitions.values(): # 所有 AST 类型支持拥有副本、相等比较及 serde,简单无载荷枚举才额外 Copy。 + lines.append("/// " + type_note(definition.name)) derives = "Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize" # 无载荷和类型可用简单枚举/字符串联合表示,无需 kind/data 的载荷包装。 if definition.simple(): @@ -281,11 +399,14 @@ def rust_types(definitions, schema_id): # 产品按字段顺序生成;和类型则按构造器分支选择对应形状。 if definition.fields is not None: lines.append(f"pub struct {definition.name} {{") - lines.extend(f"pub {f.name}: {field_type(definition.name, f)}," for f in definition.fields) + for field in definition.fields: + # 注释与字段同序输出,重新生成时一起保留。 + lines += ["/// " + field_note(definition.name, field), f"pub {field.name}: {field_type(definition.name, field)},"] else: lines.append(f"pub enum {definition.name} {{") # 每个构造器按稳定顺序生成,不合并名称相近但身份不同的分支。 for variant in definition.variants: + lines.append("/// " + variant_note(definition.name, variant)) # 无载荷构造器不携带额外字段,单值和多字段载荷分别采用相应表示。 if not variant.fields: lines.append(f"{variant.name},") @@ -294,15 +415,18 @@ def rust_types(definitions, schema_id): lines.append(f"{variant.name}({field_type(definition.name, variant.fields[0])}),") else: # 多字段构造器的具名成员保留每个字段自己的类型/数量修饰。 - fields = ", ".join(f"{f.name}: {field_type(definition.name, f)}" for f in variant.fields) + fields = ",\n".join(f"/// {field_note(definition.name, f)}\n{f.name}: {field_type(definition.name, f)}" for f in variant.fields) lines.append(f"{variant.name} {{ {fields} }},") lines.append("}") # 只为结构上可定位的类型生成 Spanned,不编造 ExprKind 这类混合载荷的范围。 if definition.name in spanned: - lines.append(f"impl Spanned for {definition.name} {{ fn span(&self) -> Span {{") + lines.append(f"impl Spanned for {definition.name} {{") + lines.append("/// 原文切片、定位和 Visitor 节点钩子调用,返回存储范围或被包装节点的范围。") + lines.append("fn span(&self) -> Span {") if definition.fields is not None: lines.append("self.span") else: + lines.append("// 按实际构造器取得唯一可定位子节点的范围。") lines.append("match self {") lines.extend(f"Self::{v.name}(node) => node.span()," for v in definition.variants) lines.append("}") @@ -316,16 +440,23 @@ def rust_visitor(definitions): spanned = spanned_types(definitions) # 按 schema 顺序导入所有节点类型,walk 可以直接引用它们。 names = ", ".join(definitions) - lines = [HEADER, f"use super::{{{names}, Spanned}};", "use crate::Span;", "pub trait Visitor {", "type Error;", + lines = [HEADER, f"use super::{{{names}, Spanned}};", "use crate::Span;", + "/// wire 校验与宿主分析实现的借用节点访问协议;默认按 ASDL 字段顺序递归,不修改树。", + "/// 覆盖 visit 方法后可显式调用对应 walk 继续子遍历;walk 本身不执行当前节点的进入/退出钩子。", + "pub trait Visitor {", "/// 实现者选择的错误类型,任一子访问失败即向上返回。", "type Error;", + "/// 默认可定位 visit 在遍历前调用;失败时本节点尚未成功进入,不再调用它的 leave_node。", "fn enter_node(&mut self, _span: Span) -> Result<(), Self::Error> { Ok(()) }", + "/// 已成功进入节点的遍历结束后调用,子节点错误也需退出;默认无操作。", "fn leave_node(&mut self) {}", + "/// walk 遇到显式 span 字段时调用,默认不处理;区别于围绕整个节点的 enter_node。", "fn visit_span(&mut self, _span: &Span) -> Result<(), Self::Error> { Ok(()) }"] # 按定义顺序遍历/输出每个命名类型,避免由无序集合决定源码结构。 for name in definitions: + lines.append(f"/// 上层 walk 或宿主调用的 {name} 入口;默认继续对应子结构,可覆盖此方法收集/校验节点。") lines.append(f"fn visit_{snake(name)}(&mut self, node: &{name}) -> Result<(), Self::Error> {{") # 可定位节点先进入,再保存子遍历 Result,退出后才返回错误,保证成功进入祖先的钩子配平。 if name in spanned: - lines += ["self.enter_node(node.span())?;", f"let result = walk_{snake(name)}(self, node);", "self.leave_node();", "result"] + lines += ["// 先进入;进入失败则不启动子遍历。", "self.enter_node(node.span())?;", "// 保存子遍历结果,不在此使用 ?,确保失败时仍退出已进入节点。", f"let result = walk_{snake(name)}(self, node);", "self.leave_node();", "result"] else: lines.append(f"walk_{snake(name)}(self, node)") lines.append("}") @@ -340,11 +471,11 @@ def rust_visitor(definitions): method = f"visitor.visit_{snake(field.type)}" # 序列优先映射到 Vec/Array/array;容器本身处理重复元素并打断 Rust 按值递归。 if field.quantifier == "*": - return [f"for value in {access} {{ {method}(value)?; }}"] + return [f"// 按顺序借用 {access} 中每个 {field.type},任一访问失败立即向上传播。", f"for value in {access} {{ {method}(value)?; }}"] # 可选字段只有 Some/非空时才递归,缺省或 null 不访问。 if field.quantifier == "?": - return [f"if let Some(value) = {access} {{ {method}(value)?; }}"] - return [f"{method}({access})?;"] + return [f"// {access} 是可选字段,仅 Some 时访问其 {field.type} 子节点。", f"if let Some(value) = {access} {{ {method}(value)?; }}"] + return [f"// 访问 {access} 的 {field.type} 子结构或跨度,借用不转移节点所有权。", f"{method}({access})?;"] # 依稳定 schema 顺序处理每个定义,保留生成结果的可重复性。 for definition in definitions.values(): @@ -357,6 +488,7 @@ def rust_visitor(definitions): body += children(field, f"&node.{field.name}") # 带载荷和类型按判别分支访问,简单枚举没有子节点。 elif not definition.simple(): + body.append("// 按构造器只递归当前实际载荷,字符串等标量没有语法子节点。") body.append("match node {") # 每个构造器按稳定顺序生成,不合并名称相近但身份不同的分支。 for variant in definition.variants: @@ -375,6 +507,7 @@ def rust_visitor(definitions): members = ", ".join(f.name for f in visited) # 按构造器载荷形状生成 Rust match 模式,仅绑定需要访问的成员。 pattern = f"{definition.name}::{variant.name} {{ " + (members + ", " if members else "") + ".. }" + body.append("// " + variant_note(definition.name, variant)) body.append(pattern + " => {") # 为每个被选中的载荷展开对应序列、可选或单值访问。 for field in visited: @@ -385,6 +518,7 @@ def rust_visitor(definitions): visitor = "visitor" if any("visitor." in line for line in body) else "_visitor" # 完全无子结构的 walker 也不使用 node,生成下划线名字以满足警告检查。 node = "node" if body else "_node" + lines.append(f"/// 默认 visit_{snake(definition.name)} 或自定义访问器调用,按字段顺序借用 {definition.name} 的子结构;本函数不进入/退出当前节点。") lines.append(f"pub fn walk_{snake(definition.name)}({visitor}: &mut V, {node}: &{definition.name}) -> Result<(), V::Error> {{") lines.extend(body) lines += ["Ok(())", "}"] @@ -409,9 +543,19 @@ def editor_types(definitions, schema_id): def object_type(fields): return "{ " + " ".join(f"{f.name}{'?' if f.quantifier == '?' else ''}: {field_type(f)};" for f in fields) + " }" - lines = [HEADER, f'export const AST_SCHEMA_ID = "{schema_id}" as const;', "export interface Span { start: number; end: number; }"] + lines = [HEADER, "// 与 Rust wire 共同核对的 ASDL 原文协议身份;生成源码注释不改变此值。", f'export const AST_SCHEMA_ID = "{schema_id}" as const;', "// 原 UTF-8 半开字节范围;number 类型本身不验证安全整数、顺序或原文边界。", "export interface Span { start: number; end: number; }"] # 依稳定 schema 顺序处理每个定义,保留生成结果的可重复性。 for definition in definitions.values(): + # 节点及字段说明均输出成独立注释行,不改变 TypeScript 的类型/执行语句。 + lines.append("// " + type_note(definition.name)) + if definition.fields is not None: + for field in definition.fields: + lines.append(f"// {field.name}: {field_note(definition.name, field)}") + else: + for variant in definition.variants: + lines.append(f"// {variant.name}: {variant_note(definition.name, variant)}") + for field in variant.fields: + lines.append(f"// {variant.name}.{field.name}: {field_note(definition.name, field)}") # 产品按字段顺序生成;和类型则按构造器分支选择对应形状。 if definition.fields is not None: lines.append(f"export type {definition.name} = {object_type(definition.fields)};") @@ -436,8 +580,9 @@ def editor_types(definitions, schema_id): value = '{ kind: ' + json.dumps(variant.name) + ';' + data + ' }' variants.append(value) lines.append(f"export type {definition.name} = " + " | ".join(variants) + ";") - lines += ["export interface Visitor {", "visitSpan?(node: Span): boolean | void;"] - lines.extend(f"visit{name}?(node: {name}): boolean | void;" for name in definitions) + lines += ["// 编辑器可选节点回调;walk 先回调,返回 false 只剪掉当前子树,其他值继续递归。", "export interface Visitor {", "// 跨度叶子回调没有子结构,其返回值不参与后续剪枝。", "visitSpan?(node: Span): boolean | void;"] + for name in definitions: + lines += [f"// walk{name} 到达 {name} 时调用,false 跳过该节点的后续子字段。", f"visit{name}?(node: {name}): boolean | void;"] lines.append("}") # 访问器生成共用的字段递归模板:跳过普通文本/标识,仅递归节点或显式跨度。 @@ -449,16 +594,18 @@ def editor_types(definitions, schema_id): function = "walkSpan" if field.type == "span" else f"walk{field.type}" # 序列优先映射到 Vec/Array/array;容器本身处理重复元素并打断 Rust 按值递归。 if field.quantifier == "*": - return [f"for (const value of {access}) {function}(value, visitor);"] + return [f"// 按声明顺序访问 {access} 列表中的每个 {field.type} 子节点。", f"for (const value of {access}) {function}(value, visitor);"] # 可选字段只有 Some/非空时才递归,缺省或 null 不访问。 if field.quantifier == "?": - return [f"if ({access} != null) {function}({access}, visitor);"] - return [f"{function}({access}, visitor);"] + return [f"// {access} 可缺省或为 null,只有存在时才访问该子节点。", f"if ({access} != null) {function}({access}, visitor);"] + return [f"// 递归访问 {access} 的 {field.type} 子结构或跨度。", f"{function}({access}, visitor);"] + lines.append("// 上层 walker/宿主调用的跨度叶子入口,仅通知 visitSpan,不验证输入或修改节点。") lines.append("export function walkSpan(node: Span, visitor: Visitor): void { visitor.visitSpan?.(node); }") # 依稳定 schema 顺序处理每个定义,保留生成结果的可重复性。 for definition in definitions.values(): - lines += [f"export function walk{definition.name}(node: {definition.name}, visitor: Visitor): void {{", f"if (visitor.visit{definition.name}?.(node) === false) return;"] + lines.append(f"// 上层 walker 或宿主调用,先通知 {definition.name} 回调,再按 ASDL 顺序访问子结构;不修改树。") + lines += [f"export function walk{definition.name}(node: {definition.name}, visitor: Visitor): void {{", "// 仅显式 false 剪枝,未提供回调或返回 void 则继续访问。", f"if (visitor.visit{definition.name}?.(node) === false) return;"] # 产品按字段顺序生成;和类型则按构造器分支选择对应形状。 if definition.fields is not None: # 产品字段依声明顺序递归,保证访问顺序与数据契约一致。 @@ -466,9 +613,11 @@ def editor_types(definitions, schema_id): lines += children(field, "node." + field.name) # 带载荷和类型按判别分支访问,简单枚举没有子节点。 elif not definition.simple(): + lines.append("// 判别 kind 只选择当前载荷,普通文本/数字拼写没有进一步语法子结构。") lines.append("switch (node.kind) {") # 每个构造器按稳定顺序生成,不合并名称相近但身份不同的分支。 for variant in definition.variants: + lines.append("// " + variant_note(definition.name, variant)) lines.append(f"case {json.dumps(variant.name)}:") for field in variant.fields: # 单项载荷取 node.data,多字段载荷再按字段名取 node.data.。