# AST 模型、ASDL 与跨工具接口 Endor 的 AST 节点统一由 [Endor.asdl](../../crates/frontend/Endor.asdl) 定义。它描述节点及字段;lexer.peg 描述字符识别,grammar.peg 描述语法与节点构造。ASDL 与语法各自维护一个职责,不引入 CST 或 Python type/元类;AST 位于 frontend,运行时对象位于 objects。 ## 生成关系 ```text Endor.asdl → scripts/generate_ast.py ├─ src/ast/generated.rs:Rust 节点、Serde 与 Spanned ├─ src/ast/visitor.rs:Rust Visitor/walk_* ├─ editor/ast.ts:TypeScript 类型、Visitor/walk* └─ editor/ast.schema.json:JSON Schema 2020-12 ``` 路径均相对 crates/frontend。生成器使用 Python 标准库和 rustfmt,无额外 Python 包、build.rs 或构建时网络访问。产物提交到 Git,普通 Cargo 构建直接使用它们。开发修改 schema 后,在仓库根执行: ```sh python3 scripts/generate_ast.py --write python3 scripts/generate_ast.py --check ``` 这两条命令是源码生成/静态检查,可在本地使用;行为测试仍仅在指定远端执行。CI 检查生成同步,并运行生成器回归检查与工作区测试。 ASDL 支持 module、产品类型 `(Type field, ...)`、构造器联合 `A(...) | B(...)`、`*` 序列、`?` 可选字段、`--` 行注释。内建 string 是拥有文本的 String,span 对应共享的 UTF-8 Span,schema_id 是交换契约标识。生成器检查重复名称、字段、未知类型和不支持的语法;这是项目使用的 ASDL 子集,不声称兼容 CPython ASDL 的全部扩展。 Rust 用 struct/enum、Vec/Option 表达结构;生成器在按值依赖图的回边放置 Box,Vec 已经断开按值递归。增加节点后,Rust 类型、两种语言的遍历接口和 JSON Schema 由同一次生成更新。 ## 根、声明、语句、表达式 | 层级 | 当前数据模型 | | --- | --- | | AstRoot | Module(Program)、Expression(Expr)、Interactive(InteractiveInput) | | Program | declarations: Vec 与 span;当前声明种类为 Function | | Decl/DeclKind | 声明的位置及其具体种类;Function 保存参数、返回类型名称和 Block | | Block | statements: Vec 与 span,不再用固定的 bindings/result 字段表示 | | Stmt/StmtKind | Binding、Return、Expression、Declaration;每条语句有自身范围 | | ReturnStatement | 可选表达式与范围;当前源码中的 return 仍要求表达式 | | Expr/ExprKind | 名称、数字原文、字符串、符号、一元/二元运算、成员和调用 | 参数与返回类型仍为 Name,只保留语法,不实施类型或名称检查。Program::functions() 提供函数迭代便捷接口。旧的 Program.functions、Block.bindings/result/return_span 字段已移除;源码构造和使用方改为声明/语句列表。ExprKind 的既有表达式结构保留。 AST 数据模型允许一般语句列表,不代表 parser 接受了所有组合。模块仍要求一个或多个函数;函数体仍接受 let* 后跟一个带值 return。Interactive 模式接受一次函数声明、let 绑定或表达式,必须消费完整输入;不创建交互会话,不处理续行提示,不执行语言代码。多条交互提交由宿主分别解析。条件、循环、导入、类等尚未实现,也不加入 FunctionType 这种 Python 专用入口。 ## 解析与位置 parse_ast(&str, ParseMode) 和 parse_source_ast(&SourceUnit, ParseMode) 返回 AstRoot;ParseMode 包含 Module、Expression、Interactive。原有 parse/parse_expression/parse_source/parse_source_expression 保留对应的具体返回类型。带来源入口的诊断仍记录名称/URI/修订和行列。 Spanned 为有位置的节点提供 span()、source_text(&str)、location(&str)。Span::slice/location 拒绝反向、越界或截断 UTF-8 字符的范围;行列是一基 Unicode 标量列,CRLF 算一次换行。Span 不携带源码身份,调用方需关联原始 SourceUnit;AST JSON 本身不含源码、文件路径、URI 或资源修订。 ## 遍历与分析 生成的 Rust Visitor 有每个节点/种类的 visit_* 方法、默认 walk_* 函数,以及 enter_node/leave_node 钩子。Error 为关联类型,子节点失败时默认遍历返回错误,并配对调用已进入节点的离开钩子。可在 visit_function、visit_binding、visit_expr 等位置实现名称分析或字节码生成。 覆盖 visit_* 后,调用 walk_* 可继续访问子节点;覆盖方法自身的钩子管理由实现者决定,直接调用 walk_* 不会重复调用当前节点的 visit_*。这些接口只提供遍历;独立 [Compiler](compiler.md) 实现当前模块/函数名称分析和最小字节码生成,闭包捕获分析尚未实现。TypeScript Visitor 对应编辑器使用,回调返回 false 可跳过子树。 ## JSON 交换 frontend 使用工作区已有的 serde/serde_json。AstDocument 是带 schema 和 root 的交换外壳,提供 new、to_json、from_json、validate、validate_source。构造/读取/写出时检查 schema 标识、位置顺序、父子范围包含关系及 JS 安全整数范围;validate_source 额外检查给定原文的长度和 UTF-8 字符边界,不比较原文与节点的程序含义。 构造器联合按 `{"kind":"Variant","data":...}` 编码,无字段的运算符联合使用字符串。产品类型使用字段对象;未知字段和未知构造器拒绝。可选字段可省略或为 null,标准写出为 null。数字保留源码拼写字符串,不经浮点转换。 AST_SCHEMA_ID 从 Endor.asdl 的 UTF-8 文本计算 SHA-256,包含注释与空白;生成器读取时使用 Python 的统一换行规则。它标识这套 AST 接口定义,区别于用户资源修订、编译器或 VM 版本。修改 schema 后旧标识被拒绝,尚无历史格式迁移。改变生成器的交换编码或内建位置类型契约时,也必须更新 ASDL 顶部的 Wire contract 版本说明,使标识随之变化。直接调用 Serde 是结构编解码,跨边界应使用 AstDocument 方法执行完整校验;JSON Schema 不表达所有位置约束。 CLI 可提供具体的交换输出: ```sh lyra parse --json source.endor lyra parse --mode expression --json - lyra parse --mode interactive --json - ``` 文件/stdin 阅读保持原有 UTF-8 和错误来源规则,stdin 到 EOF 后才解析。普通 lyra parse 输出 AstRoot 的 Debug 文本,稳定交换使用带 schema 的 JSON;parse 的各模式均不执行源码;run/eval 执行入口见 [编译链路](compiler.md)。命令在指定远端运行。 检查源码见 [frontend/tests](../../crates/frontend/tests/README.md) 和 [生成器检查](../../scripts/test_generate_ast.py),边界见 [验证](../operations/validation.md)。返回 [运行时索引](README.md)。