//! 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 { // 模块/表达式复用专门入口,交互入口直接调用对应 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 { 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 { parse(source.text()).map_err(|error| source.annotate(error)) } /// 宿主从快照解析一个表达式时调用,与 parse_expression 共享语法,仅增加错误来源。 pub fn parse_source_expression( source: &crate::SourceUnit, ) -> Result { 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 { // 词法诊断原样传播;语法 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 { // 词法诊断原样传播;语法 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, 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 = 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, 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");