Files
endorphin-tessera-gui/src/runtime/endor_script.rs
T

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);
}
}
}
}