docs(frontend): explain parsing entry points and source diagnostics
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
//! 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,
|
||||
@@ -8,17 +9,25 @@ 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)
|
||||
@@ -27,6 +36,7 @@ pub fn parse_ast(source: &str, mode: ParseMode) -> Result<AstRoot, Diagnostic> {
|
||||
}
|
||||
}
|
||||
|
||||
/// compiler 与宿主的快照入口;语法根由 mode 决定,错误增加该快照的来源和行列。
|
||||
pub fn parse_source_ast(
|
||||
source: &crate::SourceUnit,
|
||||
mode: ParseMode,
|
||||
@@ -35,10 +45,12 @@ pub fn parse_source_ast(
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
@@ -47,21 +59,28 @@ pub fn parse_source_expression(
|
||||
|
||||
/// 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,
|
||||
@@ -71,6 +90,7 @@ fn diagnostic(error: peg::error::ParseError<usize>, tokens: &[Token], end: usize
|
||||
}
|
||||
}
|
||||
|
||||
/// grammar.peg 的交互绑定/局部绑定规则调用,复用 Binding 自身范围包装成语句节点。
|
||||
fn binding_statement(binding: Binding) -> Stmt {
|
||||
Stmt {
|
||||
span: binding.span,
|
||||
@@ -78,6 +98,7 @@ fn binding_statement(binding: Binding) -> Stmt {
|
||||
}
|
||||
}
|
||||
|
||||
/// grammar.peg 的优先级规则调用,把已解析左右操作数包装为二元节点;跨度覆盖两端及中间原文。
|
||||
fn binary(left: Expr, operation: BinaryOp, right: Expr) -> Expr {
|
||||
Expr {
|
||||
span: left.span.through(right.span),
|
||||
@@ -89,17 +110,25 @@ fn binary(left: Expr, operation: BinaryOp, right: Expr) -> Expr {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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),
|
||||
@@ -118,4 +147,5 @@ fn postfix(expression: Expr, suffix: Suffix) -> Expr {
|
||||
}
|
||||
}
|
||||
|
||||
// 编译时引入 syntax 根规则;上述公开入口与 AST 构造辅助函数在规则中互相调用。
|
||||
include!("grammar.peg");
|
||||
|
||||
Reference in New Issue
Block a user