//! 区分完整 Module 与顶层 Script,并保留输入源码位置。 //! 解析包装或片段拼接产生的 span 偏移要递归修正,保证诊断和 Sources 视图仍指向用户原始行列。 //! //! runtime::endor_host 的一次性 compile 和持久会话加载调用 parse,再按 roots 进行 AST 预算检查与编译。 //! 语句、词法和 AST 定义属于 Aster;这里只把完整源码组织成 Module 或连续 Interactive 输入。 //! 片段先用局部字节位置解析,shift_* 递归加回起点,编译错误和 Sources 浏览才能仍对应原文件。 use anyhow::Result; use frontend::{ ParseMode, SourceUnit, Span, TokenKind, ast::{AstRoot, Decl, DeclKind, Expr, ExprKind, InteractiveInput, Stmt, StmtKind}, }; /// 待编译输入是完整模块还是顶层脚本;两者保留不同根结构但使用统一后续接口。 pub enum SourcePlan { // 保留 Aster 完整模块根,后续作为一个编译/执行单元,不拆开函数或类的定义体。 Module(AstRoot), // 顶层脚本按完整交互输入连续执行;各段仍共享所属运行时的全局环境,而非各建一个 VM。 Script(Vec), } impl SourcePlan { /// endor_host 的 AST 预算访问和 compile_plan 调用,借用统一根切片,不克隆 AST 或重新解析。 pub fn roots(&self) -> &[AstRoot] { match self { // 单模块以单元素切片适配,让调用方不必维护两套遍历分支。 Self::Module(root) => std::slice::from_ref(root), // 脚本直接保留原输入段顺序,声明和后续使用语句的先后关系不能重排。 Self::Script(roots) => roots, } } } /// endor_host 执行入口调用,优先接受完整 Module,失败则按顶层换行候选边界拼出 Aster 可解析的交互输入。 /// 模块解析失败本身不立即作为最终错误:普通顶层脚本本就需要走后面的 Interactive 解析路径。 pub fn parse(source: &SourceUnit) -> Result { // 完整模块优先;只有模块解析失败才退回连续交互输入模式,宿主不自行实现 Endor 语法。 if let Ok(module) = frontend::parse_source_ast(source, ParseMode::Module) { // 模块成功就不再按行切分,避免破坏跨行声明与完整模块语义。 return Ok(SourcePlan::Module(module)); } // 一次词法分析获得原始字节 span,候选分段依赖 token 之间的实际换行而非直接按行切字符串。 let tokens = frontend::lex_source(source)?; // 只保存已被 Aster 成功接受的交互根,失败候选不留下半棵 AST。 let mut roots = Vec::new(); // start 为当前待解析片段在原文件中的字节起点,仅在候选成功时前移。 let mut start = 0; // 深度只是候选边界启发式;括号失配是否合法仍由 Aster 判定,不在宿主偷偷修正。 let mut depth = 0_i64; for (index, token) in tokens.iter().enumerate() { // 追踪圆/花括号嵌套,括号内换行不能直接结束输入;最终语法是否合法仍交给 Aster。 match token.kind { // token 已由词法器识别,字符串/注释里的括号不会当作结构括号改变深度。 TokenKind::LeftParen | TokenKind::LeftBrace => depth += 1, TokenKind::RightParen | TokenKind::RightBrace => depth -= 1, // 其他 token 不改变边界嵌套,不能靠普通字符扫描替代此判断。 _ => {} } // token 尾为原文本字节位置,用它取与下一 token 之间的空白/注释区域查换行。 let end = token.span.end; // 末尾 token 总是尝试解析;非末尾要求顶层换行,且下一 token 不像成员访问、调用或算术续接。 let boundary = tokens.get(index + 1).is_none_or(|next| { // 尚在括号内,或下一 token 明显续接上个表达式时,不尝试提前提交当前交互片段。 depth == 0 && source.text()[end..next.span.start].contains(['\r', '\n']) && !matches!( next.kind, TokenKind::Dot | TokenKind::LeftParen | TokenKind::Plus | TokenKind::Minus | TokenKind::Star | TokenKind::Slash ) }); if !boundary { // 保留 start,之后尝试更长的片段;不是丢弃当前 token 对应源码。 continue; } // 当前片段从上一次成功边界 start 开始,保留期间的空白与注释;解析器看到的是片段局部字节位置。 match frontend::parse_ast(&source.text()[start..end], ParseMode::Interactive) { Ok(mut root) => { // Interactive 模式的成功根必须为 Interactive;违反是前端接口不变量错误。 let AstRoot::Interactive(input) = &mut root else { unreachable!("interactive mode"); }; // 成功后递归加回 start,把整个 AST 的局部 span 转成原文件位置,再推进下一段起点。 shift_input(input, start); roots.push(root); start = end; } // 错误恰在片段末尾且还有 token 时,视为可能未完成的交互输入,保持 start 不动继续拼接。 Err(diagnostic) if index + 1 < tokens.len() && diagnostic.span.start == end - start => { // 只有“诊断正好指向片段末尾”才暂缓,下一候选会连同当前片段一起再交 Aster 解析。 } Err(mut diagnostic) => { // 其他语法错误立即恢复原始偏移,再用 SourceUnit 添加原文件来源与行列诊断。 shift_span(&mut diagnostic.span, start); return Err(source.annotate(diagnostic).into()); } } } // 空输入不能静默产生空计划;再次委托交互入口给出解析器自己的空输入诊断与来源。 if roots.is_empty() { // 不自造宿主错误字符串,保留前端对空输入的原始诊断和 SourceUnit 来源信息。 frontend::parse_source_ast(source, ParseMode::Interactive)?; } Ok(SourcePlan::Script(roots)) } /// 下方所有 shift_* 调用的基本操作,给单个半开字节范围加偏移,不递归、不改变源码内容。 fn shift_span(span: &mut Span, offset: usize) { // span 采用原文本字节偏移,start/end 同增 offset 保持长度;不是按字符数或行数偏移。 span.start += offset; span.end += offset; } /// parse 在接受一个 Interactive 根后调用,平移交互根范围,再把语句交给 shift_statement 递归。 fn shift_input(input: &mut InteractiveInput, offset: usize) { shift_span(&mut input.span, offset); // 根范围之外,逐条语句递归处理所有内层节点,避免错误只在顶层位置正确。 for statement in &mut input.statements { shift_statement(statement, offset); } } /// shift_input/函数体/分支体的递归入口,按语句种类平移自己的范围和所有内嵌表达式/声明。 fn shift_statement(statement: &mut Stmt, offset: usize) { shift_span(&mut statement.span, offset); match &mut statement.kind { StmtKind::If(branch) => { // If 节点、条件、then 块各有独立 span,平移外层 Stmt 不能自动更新它们。 shift_span(&mut branch.span, offset); shift_expression(&mut branch.condition, offset); shift_span(&mut branch.body.span, offset); for s in &mut branch.body.statements { // then 内可能继续嵌套 if/while/声明,不能只调整第一层语句外框。 shift_statement(s, offset); } if let Some(body) = &mut branch.otherwise { // else 可省略;存在时块范围和其中每条语句都使用同一个原片段偏移。 shift_span(&mut body.span, offset); for s in &mut body.statements { shift_statement(s, offset); } } } StmtKind::While(loop_) => { // while 条件和循环体均可含更深的调用/控制流,沿各自 AST 子结构递归而非重新扫描文本。 shift_span(&mut loop_.span, offset); shift_expression(&mut loop_.condition, offset); shift_span(&mut loop_.body.span, offset); for s in &mut loop_.body.statements { shift_statement(s, offset); } } StmtKind::Binding(binding) => { // 新绑定独立保存名字和右值范围,诊断未定义符号时需要内部表达式位置正确。 shift_span(&mut binding.span, offset); shift_span(&mut binding.name.span, offset); shift_expression(&mut binding.value, offset); } StmtKind::Assignment(assignment) => { // 赋值不同于声明,但同样保留名字与右值两个来源范围,不能仅平移整个语句。 shift_span(&mut assignment.span, offset); shift_span(&mut assignment.name.span, offset); shift_expression(&mut assignment.value, offset); } // 成员赋值同时包含目标对象表达式、成员名和右值,三部分都要恢复原始位置。 StmtKind::MemberAssignment(assignment) => { shift_span(&mut assignment.span, offset); shift_span(&mut assignment.name.span, offset); shift_expression(&mut assignment.object, offset); shift_expression(&mut assignment.value, offset); } StmtKind::Return(returned) => { // return 自身 span 必须平移;无返回表达式的 Unit 返回不能访问不存在的 value。 shift_span(&mut returned.span, offset); if let Some(expression) = &mut returned.value { // 返回值可以是任意复合调用/算术表达式,继续下钻到全部名称和实参。 shift_expression(expression, offset); } } // 表达式语句复用完整表达式递归,避免调用参数中的错误仍显示片段局部行列。 StmtKind::Expression(expression) => shift_expression(expression, offset), // 顶层/局部声明转交类与函数专用入口,处理参数、类型名及函数体。 StmtKind::Declaration(declaration) => shift_declaration(declaration, offset), } } /// shift_statement 在声明语句中调用,平移 Decl 外层,再区分函数定义与类定义的内部位置。 fn shift_declaration(declaration: &mut Decl, offset: usize) { shift_span(&mut declaration.span, offset); match &mut declaration.kind { // 普通函数和类方法使用相同函数结构,统一由 shift_function 平移签名和函数体。 DeclKind::Function(function) => shift_function(function, offset), // 类的名称、基类、字段类型和方法均保存独立 span,不能仅移动声明外层范围。 DeclKind::Class(class) => { shift_span(&mut class.span, offset); shift_span(&mut class.name.span, offset); if let Some(base) = &mut class.base { // 无显式基类时不新增默认基类 span,保留前端原始 AST 的可选结构。 shift_span(&mut base.span, offset); } for field in &mut class.fields { // 字段节点整体、字段名、类型名分别保存位置,错误类型注解必须指回原文件。 shift_span(&mut field.span, offset); shift_span(&mut field.name.span, offset); shift_span(&mut field.type_name.span, offset); } for method in &mut class.methods { // 方法体可能包含更多声明,沿同一套递归入口继续处理。 shift_function(method, offset); } } } } /// shift_declaration 调用,平移函数名、参数/返回注解和函数体,供编译器类型错误使用原输入坐标。 fn shift_function(function: &mut frontend::ast::Function, offset: usize) { shift_span(&mut function.span, offset); shift_span(&mut function.name.span, offset); shift_span(&mut function.return_type.span, offset); // 参数自身、名称、类型各有范围;函数返回类型和函数体范围也独立移动。 for parameter in &mut function.parameters { shift_span(&mut parameter.span, offset); shift_span(&mut parameter.name.span, offset); shift_span(&mut parameter.type_name.span, offset); } shift_span(&mut function.body.span, offset); for statement in &mut function.body.statements { // 函数体内每条语句继续按 kind 递归,不能用整体函数范围覆盖内部诊断。 shift_statement(statement, offset); } } /// 语句/函数递归助手调用,平移表达式与名称成员的范围;语义值和运算符本身保持不变。 fn shift_expression(expression: &mut Expr, offset: usize) { shift_span(&mut expression.span, offset); match &mut expression.kind { // 这些叶节点只用表达式外层 span;Symbol/Member 的名称还保存额外 span,需继续调整。 ExprKind::Name(_) | ExprKind::Number(_) | ExprKind::String(_) => {} ExprKind::Symbol(name) => shift_span(&mut name.span, offset), ExprKind::Unary { operand, .. } | ExprKind::Await(operand) => { // unary 与 await 都只有一个子表达式,操作符来源已在外层表达式 span 覆盖。 shift_expression(operand, offset) } ExprKind::Binary { left, right, .. } => { // 两侧都可能含复合表达式,不能只调整最终报错概率更高的右侧。 shift_expression(left, offset); shift_expression(right, offset); } ExprKind::Member { object, name } => { // object 可能是调用返回值,name 则是独立标识符,分别调整两类来源。 shift_expression(object, offset); shift_span(&mut name.span, offset); } // 调用目标可继续是成员/调用等复合表达式;逐个递归实参而非只改整个 Call 的范围。 ExprKind::Call { callee, arguments } => { shift_expression(callee, offset); for argument in arguments { // 保持实参顺序和值不变,只平移每个实参树内部保存的字节位置。 shift_expression(argument, offset); } } } }