//! 区分完整 Module 与顶层 Script,并保留输入源码位置。 //! 解析包装或片段拼接产生的 span 偏移要递归修正,保证诊断和 Sources 视图仍指向用户原始行列。 //! //! Feed a source snapshot to Aster's existing module or interactive entry points. //! Statement syntax, tokens and ASTs all remain Aster-owned. The host only batches //! complete interactive inputs, just as a REPL feeds consecutive inputs to a VM. use anyhow::Result; use frontend::{ ParseMode, SourceUnit, Span, TokenKind, ast::{AstRoot, Decl, DeclKind, Expr, ExprKind, InteractiveInput, Stmt, StmtKind}, }; /// 待编译输入是完整模块还是顶层脚本;两者保留不同根结构但使用统一后续接口。 pub enum SourcePlan { Module(AstRoot), Script(Vec), } impl SourcePlan { /// 按输入计划返回需要编译的语法根,隐藏 Script 与 Module 组织差异。 pub fn roots(&self) -> &[AstRoot] { match self { Self::Module(root) => std::slice::from_ref(root), Self::Script(roots) => roots, } } } /// 把外部源码或属性表示转换为内部枚举/结构,并反馈不支持的输入。 pub fn parse(source: &SourceUnit) -> Result { if let Ok(module) = frontend::parse_source_ast(source, ParseMode::Module) { return Ok(SourcePlan::Module(module)); } let tokens = frontend::lex_source(source)?; let mut roots = Vec::new(); let mut start = 0; let mut depth = 0_i64; for (index, token) in tokens.iter().enumerate() { match token.kind { TokenKind::LeftParen | TokenKind::LeftBrace => depth += 1, TokenKind::RightParen | TokenKind::RightBrace => depth -= 1, _ => {} } let end = token.span.end; let boundary = tokens.get(index + 1).is_none_or(|next| { 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 { continue; } match frontend::parse_ast(&source.text()[start..end], ParseMode::Interactive) { Ok(mut root) => { let AstRoot::Interactive(input) = &mut root else { unreachable!("interactive mode"); }; shift_input(input, start); roots.push(root); start = end; } // An incomplete interactive input can continue on the next line. // Only Aster decides whether its combined syntax is valid. Err(diagnostic) if index + 1 < tokens.len() && diagnostic.span.start == end - start => { } Err(mut diagnostic) => { shift_span(&mut diagnostic.span, start); return Err(source.annotate(diagnostic).into()); } } } if roots.is_empty() { // Produce Aster's original empty-input diagnostic and provenance. frontend::parse_source_ast(source, ParseMode::Interactive)?; } Ok(SourcePlan::Script(roots)) } /// 递归调整对应语法节点的源码范围,保持片段解析后的错误位置仍指向原输入。 fn shift_span(span: &mut Span, offset: usize) { span.start += offset; span.end += offset; } /// 递归调整对应语法节点的源码范围,保持片段解析后的错误位置仍指向原输入。 fn shift_input(input: &mut InteractiveInput, offset: usize) { shift_span(&mut input.span, offset); for statement in &mut input.statements { shift_statement(statement, offset); } } /// 递归调整对应语法节点的源码范围,保持片段解析后的错误位置仍指向原输入。 fn shift_statement(statement: &mut Stmt, offset: usize) { shift_span(&mut statement.span, offset); match &mut statement.kind { 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) => { 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), } } /// 递归调整对应语法节点的源码范围,保持片段解析后的错误位置仍指向原输入。 fn shift_declaration(declaration: &mut Decl, offset: usize) { shift_span(&mut declaration.span, offset); match &mut declaration.kind { DeclKind::Function(function) => shift_function(function, offset), DeclKind::Class(class) => { shift_span(&mut class.span, offset); shift_span(&mut class.name.span, offset); if let Some(base) = &mut class.base { 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); } } } } /// 递归调整对应语法节点的源码范围,保持片段解析后的错误位置仍指向原输入。 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 { shift_statement(statement, offset); } } /// 递归调整对应语法节点的源码范围,保持片段解析后的错误位置仍指向原输入。 fn shift_expression(expression: &mut Expr, offset: usize) { shift_span(&mut expression.span, offset); match &mut expression.kind { ExprKind::Name(_) | ExprKind::Number(_) | ExprKind::String(_) => {} ExprKind::Symbol(name) => shift_span(&mut name.span, offset), ExprKind::Unary { operand, .. } | ExprKind::Await(operand) => { shift_expression(operand, offset) } ExprKind::Binary { left, right, .. } => { shift_expression(left, offset); shift_expression(right, offset); } ExprKind::Member { object, name } => { shift_expression(object, offset); shift_span(&mut name.span, offset); } ExprKind::Call { callee, arguments } => { shift_expression(callee, offset); for argument in arguments { shift_expression(argument, offset); } } } }