196 lines
7.8 KiB
Rust
196 lines
7.8 KiB
Rust
//! 区分完整 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<AstRoot>),
|
|
}
|
|
|
|
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<SourcePlan> {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|