设计文档:Auto 语言“面向场景编程”与 UI 编译架构
主题:基于上下文环境的语法解析与 UI-IR 生成架构 目标:在保持 Auto 语言核心编译器轻量、类型安全的前提下,优雅地支持 UI、Shell 等特定领域语法(方言)。确保全局命名空间不被污染,同时为 LSP(语言服务器)和多端转译器提供精准的上下文。
1. 架构概述 (Architecture Overview)
“面向场景编程”的核心理念是环境注入 (Environment Injection) 与 上下文关键字 (Contextual Keywords)。
编译器不再试图用一个包罗万象的庞大 Parser 来理解所有语法,而是将编译过程视为一个受全局场景状态 (Scenario State) 驱动的状态机。UI 独有的概念(如 widget, view)作为第一类公民(First-Class Citizens)保留在特定场景的 AST 中,拒绝过早降级(Desugaring),从而实现到 UI-IR(Atom 格式)的无损抽取。
2. 工程配置层:pac.at (The Source of Truth)
为了让 IDE (LSP) 能够无歧义地提供代码补全和语法高亮,项目的编译场景必须在工程的根配置文件中明确声明。pac.at 使用 Auto 语言原生的语法定义:
// pac.at (前端 UI 工程示例)
name: "my-counter-app"
version: "1.0.0"
scenario: "ui" // 核心标识:声明当前工程使用 UI 方言
backend: "vue" // 声明编译目标转译器// pac.at (后端或核心库工程示例)
name: "my-core-service"
version: "1.0.0"
scenario: "core" // 标准 Auto 语言环境
backend: "a2r" // 编译为 Rust3. CLI 调度与编译器会话 (CLI & Compiler Session)
统一使用 auto 作为入口。CLI 支持通过命令行 Flag 显式覆盖场景配置(适用于单文件脚本或 CI/CD 流水线)。
3.1 命令行接口设计
- 标准编译:
auto build src/main.at(默认读取pac.at中的scenario,若无则默认为core) - 显式指定场景:
auto build -s ui src/App.at
3.2 核心会话机制 (Compiler Session)
在编译器启动时,构建全局的 CompilerSession,向下游传递场景信息,绝对避免使用全局静态变量。
// auto-core/src/session.rs
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Scenario {
Core,
UI,
Shell,
}
pub struct CompilerSession {
pub scenario: Scenario,
// 可扩展:错误收集器、SourceMap 引用等
}4. 解析器架构:软隔离与上下文关键字 (Parser & Contextual Keywords)
这是保证 Auto 语言普通代码不出错的关键。在 Lexer 层面,widget、view、model 仅仅是普通的 Identifier。
Parser 在工作时,必须校验当前的 Session.scenario:
// auto-core/src/parser.rs
impl<'a> Parser<'a> {
pub fn new(source: &'a str, session: CompilerSession) -> Self { ... }
fn parse_statement(&mut self) -> Result<AstNode> {
let token = self.peek();
// 仅在 UI 场景下,拦截特定的 Identifier 作为关键字
if self.session.scenario == Scenario::UI {
match token.as_str() {
"widget" => return self.parse_widget_decl(),
// ... 处理其他 UI 独有语法
_ => {}
}
}
// 如果不是 UI 场景,或者标识符不匹配,走常规 Auto 语句解析
self.parse_expression_statement()
}
}优势:在 scenario: "core" 的后端项目中,开发者完全可以定义 let widget = create_window(); 而不会触发任何语法冲突。
5. AST 设计与 UI-IR 抽取 (AST & Extraction Pipeline)
为了保证 UI 声明的意图不丢失,AST 必须原生支持 UI 节点。
5.1 原生 UI AST 节点定义
// auto-core/src/ast.rs
pub enum AstNode {
FunctionDecl(FuncDecl),
ClassDecl(ClassDecl),
// 场景扩展节点
WidgetDecl(WidgetDecl),
}
pub struct WidgetDecl {
pub name: String,
pub model: Vec<FieldDef>, // 状态声明
pub view: ViewBlock, // 原生视图树结构
pub on_handlers: Vec<OnHandler>, // 事件逻辑块
}5.2 编译流水线 (The Pipeline)
当 auto build -s ui 执行时,流水线严格分为三个阶段:
- 解析 (Parsing):生成包含
WidgetDecl的原生 AST。TypeChecker 会对.count和.Inc进行自顶向下的精确类型检查。 - 抽取 (Extraction to UI-IR):编译器提取
model和view节点,1:1 无损映射为结构化的 Atom 格式文本/数据结构(UI-IR)。 - 转译/编译 (Backend Dispatch):
on_handlers内部的逻辑代码被编译为 Auto ByteCode (ABC) 或生成目标语言的 AST 片段。- 后端转译器(如
auto-vue-transpiler)接管 UI-IR 和逻辑载荷,生成最终的 Kotlin/JS 代码。
6. LSP (语言服务器) 行为规范
LSP 的稳定运行依赖于这套架构:
- 初始化:当 VSCode 打开工程时,LSP 扫描项目根目录的
pac.at。 - 环境绑定:读取到
scenario: "ui"后,LSP 在内存中实例化一个CompilerSession { scenario: Scenario::UI }。 - 服务提供:后续的 Diagnostics (报错)、Hover (悬停)、Completion (补全) 全部经由挂载了 UI 插件的 Parser 处理。
目前编译器前端(解析和 IR 生成)的基建蓝图已经非常完整了。接下来,是否需要拿之前的 Counter 例子,实战演练一下后端的转译过程?例如演示 auto-vue-transpiler 是如何读取这颗包含状态的 AST/UI-IR,并最终生成带有 useState 和 dispatch 的现代 vue 源码的?