Files
endorphin-aster-vm/crates/frontend/src/parser.rs
T

152 lines
6.8 KiB
Rust

//! compiler 与宿主使用的 Token 解析门面;grammar.peg 构造拥有文本的 AST,类型名及名称绑定留待编译分析。
use crate::ast::{
Assignment, AstRoot, BinaryOp, Binding, Block, Class, Decl, DeclKind, Expr, ExprKind, Function,
IfStatement, InteractiveInput, MemberAssignment, Name, Parameter, Program, ReturnStatement,
Stmt, StmtKind, UnaryOp, WhileStatement,
};
use crate::lexer::decode_string;
use crate::{Diagnostic, DiagnosticKind, Span, Token, TokenKind, lex};
/// Select the syntax root without implying any execution or REPL session.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// parse_ast/parse_source_ast 调用方选择的语法根,不表示开启执行器或 REPL 状态。
pub enum ParseMode {
// 一个或多个顶层函数/类声明。
Module,
// 恰好一个完整表达式,不能附带其他语句。
Expression,
// 一个完整的交互语句,可为声明、绑定、赋值、控制流或表达式。
Interactive,
}
/// compile_source 和语法测试调用,按 mode 返回 AstRoot;词法失败先返回,语法失败转换为原文跨度诊断。
pub fn parse_ast(source: &str, mode: ParseMode) -> Result<AstRoot, Diagnostic> {
// 模块/表达式复用专门入口,交互入口直接调用对应 PEG 根规则。
match mode {
ParseMode::Module => parse(source).map(AstRoot::Module),
ParseMode::Expression => parse_expression(source).map(AstRoot::Expression),
// 语法只收一个完整提交,不负责多次提交共享环境或未完成输入提示。
ParseMode::Interactive => {
// 先分词,保留 Token 的原文跨度,供语法错误从 Token 索引映射回字节。
let tokens = lex(source)?;
syntax::interactive(&tokens, source)
.map(AstRoot::Interactive)
.map_err(|error| diagnostic(error, &tokens, source.len()))
}
}
}
/// compiler 与宿主的快照入口;语法根由 mode 决定,错误增加该快照的来源和行列。
pub fn parse_source_ast(
source: &crate::SourceUnit,
mode: ParseMode,
) -> Result<AstRoot, crate::SourceDiagnostic> {
parse_ast(source.text(), mode).map_err(|error| source.annotate(error))
}
/// Parse a reader snapshot, retaining its origin and resolved revision on error.
/// 宿主从 Reader 快照解析模块时调用,保留来源及 Endor 版本身份到错误包装。
pub fn parse_source(source: &crate::SourceUnit) -> Result<Program, crate::SourceDiagnostic> {
parse(source.text()).map_err(|error| source.annotate(error))
}
/// 宿主从快照解析一个表达式时调用,与 parse_expression 共享语法,仅增加错误来源。
pub fn parse_source_expression(
source: &crate::SourceUnit,
) -> Result<Expr, crate::SourceDiagnostic> {
parse_expression(source.text()).map_err(|error| source.annotate(error))
}
/// Parse one or more typed function or class declarations. This is a syntax
/// operation: type names and name references are retained without resolution.
/// parse_ast 的 Module 分支、parse_source 和语法测试调用;只构造声明树,不注册 VM 全局值。
pub fn parse(source: &str) -> Result<Program, Diagnostic> {
// 词法诊断原样传播;语法 PEG 处理的是 Token 序列,不直接按 UTF-8 字节推进。
let tokens = lex(source)?;
syntax::program(&tokens, source).map_err(|error| diagnostic(error, &tokens, source.len()))
}
/// Parse exactly one complete expression, useful independently of declarations.
/// parse_ast 的 Expression 分支、快照包装及表达式测试调用;要求整份 Token 输入被消费。
pub fn parse_expression(source: &str) -> Result<Expr, Diagnostic> {
// 词法诊断原样传播;语法 PEG 处理的是 Token 序列,不直接按 UTF-8 字节推进。
let tokens = lex(source)?;
syntax::expression(&tokens, source).map_err(|error| diagnostic(error, &tokens, source.len()))
}
/// 各 PEG 语法根的 map_err 调用,把失败 Token 索引映射为原文范围,并保留期望项列表。
fn diagnostic(error: peg::error::ParseError<usize>, tokens: &[Token], end: usize) -> Diagnostic {
// 命中 Token 则定位该 Token,EOF 或越界索引使用原文尾部空范围。
let span = tokens
.get(error.location)
.map_or(Span::new(end, end), |token| token.span);
// PEG 期望项转成拥有字符串,既写入可读消息,也保留供宿主程序化显示。
let expected: Vec<String> = error.expected.tokens().map(str::to_owned).collect();
Diagnostic {
kind: DiagnosticKind::Syntax,
span,
message: format!("expected {}", expected.join(" or ")),
expected,
}
}
/// grammar.peg 的交互绑定/局部绑定规则调用,复用 Binding 自身范围包装成语句节点。
fn binding_statement(binding: Binding) -> Stmt {
Stmt {
span: binding.span,
kind: StmtKind::Binding(binding),
}
}
/// grammar.peg 的优先级规则调用,把已解析左右操作数包装为二元节点;跨度覆盖两端及中间原文。
fn binary(left: Expr, operation: BinaryOp, right: Expr) -> Expr {
Expr {
span: left.span.through(right.span),
kind: ExprKind::Binary {
operation,
left: Box::new(left),
right: Box::new(right),
},
}
}
/// grammar.peg 暂存一个后缀,postfix 按源码从左到右把链式成员/调用折叠成 AST。
enum Suffix {
// 点后属性名节点,自身含原文字节跨度。
Member(Name),
// 参数表达式与右括号范围,后者用于延长整个调用节点的跨度。
Call(Vec<Expr>, Span),
}
/// grammar.peg 的 class_member 返回中间分类,再由 class 规则分别收集字段和方法。
enum ClassMember {
// 字段使用名称加类型名结构,与 Parameter 共用形状。
Field(Parameter),
// 类方法沿用函数 AST;显式 self 等约束由 compiler::analyze 检查。
Method(Function),
}
/// grammar.peg 的 postfix_expression fold 调用;每步把前一个结果作为新的接收对象或被调表达式。
fn postfix(expression: Expr, suffix: Suffix) -> Expr {
// 成员节点延长到属性名末尾,调用节点延长到右括号,保持连续后缀完整跨度。
match suffix {
Suffix::Member(name) => Expr {
span: expression.span.through(name.span),
kind: ExprKind::Member {
object: Box::new(expression),
name,
},
},
Suffix::Call(arguments, end) => Expr {
span: expression.span.through(end),
kind: ExprKind::Call {
callee: Box::new(expression),
arguments,
},
},
}
}
// 编译时引入 syntax 根规则;上述公开入口与 AST 构造辅助函数在规则中互相调用。
include!("grammar.peg");