docs(frontend): persist generated AST and visitor annotations
This commit is contained in:
+165
-16
@@ -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)}<V: Visitor + ?Sized>({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<Type> 先回调,返回 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.<field>。
|
||||
|
||||
Reference in New Issue
Block a user