Files
endorphin-aster-vm/crates/frontend/src/grammar.peg
T

267 lines
16 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// parser.rs 编译时引入,生成 syntax 的 Token 解析规则;source 是这些 Token 对应的原始 UTF-8 文本。
// 规则只构造拥有文本及原文跨度的 AST,不解析名称/类型,也不执行函数或操作 VM。
peg::parser! {
// parse/parse_expression/parse_ast 的交互分支调用相应公开根,默认要求完整消费 Token 输入。
grammar syntax(source: &str) for [Token] {
// 各语法上下文按原文精确匹配 Name;词法器不把这些词全局保留为独立关键字种类。
rule keyword(word: &str) -> Span
= [Token { kind: TokenKind::Name, span } if &source[span.start..span.end] == word] { span }
// function 规则的开头;quiet 隐藏内部 Token 细节,失败期望显示可读的 function。
rule function_keyword() -> Span
= quiet!{keyword("function")} / expected!("'function'")
// binding 的开头,同样提供稳定可读的期望项。
rule let_keyword() -> Span
= quiet!{keyword("let")} / expected!("'let'")
// return_statement 的开头及 local_statement 的负向前瞻,避免 return 被普通表达式语句消费。
rule return_keyword() -> Span
= quiet!{keyword("return")} / expected!("'return'")
// 声明、类型名及表达式引用共用;从 Token 范围复制名称文本,AST 不借用原源码缓冲。
rule name() -> Name
= span:quiet!{[Token { kind: TokenKind::Name, span }] { span }} {
Name { text: source[span.start..span.end].into(), span }
}
/ expected!("identifier")
// 函数参数、调用及括号表达式复用左括号匹配,返回范围供组合节点使用。
rule lparen() -> Span
= quiet!{[Token { kind: TokenKind::LeftParen, span }] { span }} / expected!("'('")
// 参数/调用/括号表达式复用右括号匹配,其终点可成为完整表达式终点。
rule rparen() -> Span
= quiet!{[Token { kind: TokenKind::RightParen, span }] { span }} / expected!("')'")
// 类和 block 的左边界,缺失时报告可读的左花括号期望项。
rule lbrace() -> Span
= quiet!{[Token { kind: TokenKind::LeftBrace, span }] { span }} / expected!("'{'")
// 类和 block 的右边界,节点跨度包含这个闭合括号。
rule rbrace() -> Span
= quiet!{[Token { kind: TokenKind::RightBrace, span }] { span }} / expected!("'}'")
// 类型注解和类继承共用冒号,不额外构造 AST 节点。
rule colon()
= quiet!{[Token { kind: TokenKind::Colon, .. }]} / expected!("':'")
// 参数/实参列表分隔;列表组合器决定空列表及分隔规则。
rule comma()
= quiet!{[Token { kind: TokenKind::Comma, .. }]} / expected!("','")
// 绑定和赋值共用单等号 Token,不接受相等比较 Token。
rule equal()
= quiet!{[Token { kind: TokenKind::Equal, .. }]} / expected!("'='")
// 成员后缀和符号字面量共用点,返回其范围以保留符号起点。
rule dot() -> Span
= quiet!{[Token { kind: TokenKind::Dot, span }] { span }} / expected!("'.'")
// 加法优先级层消费 Plus Token。
rule plus()
= [Token { kind: TokenKind::Plus, .. }]
// 减法及一元取负共用 Minus,前缀负号需要它的跨度作为节点起点。
rule minus() -> Span
= [Token { kind: TokenKind::Minus, span }] { span }
// 乘法优先级层消费 Star Token。
rule star()
= [Token { kind: TokenKind::Star, .. }]
// 除法优先级层消费 Slash,注释已在词法 trivia 中跳过。
rule slash()
= [Token { kind: TokenKind::Slash, .. }]
// parse 的模块入口;至少一个顶层函数或类声明,默认拒绝剩余 Token。
pub rule program() -> Program
= declarations:declaration()+ {
// 声明列表由 + 保证非空,根范围从首声明起点延伸到末声明终点。
let span = declarations[0].span.through(declarations.last().unwrap().span);
Program { declarations, span }
}
// program/interactive_statement 的声明分支;函数直接包装,类则收集基类及成员。
rule declaration() -> Decl
= function:function() {
Decl { span: function.span, kind: DeclKind::Function(function) }
}
/ start:keyword("class") name:name() base:(colon() base:name() { base })?
lbrace() members:class_member()* end:rbrace() {
// 按字段出现顺序收集字段参数结构,类的布局稍后由编译器及 VM 解释。
let mut fields = Vec::new();
// 按方法出现顺序收集函数结构,与字段分开存储但不改变各自顺序。
let mut methods = Vec::new();
// 解析阶段先统一取得成员,再按中间分类分发到两份 AST 列表。
for member in members {
// 字段和方法保留各自原节点及跨度,不在这里检查成员重名或 self。
match member {
ClassMember::Field(field) => fields.push(field),
ClassMember::Method(method) => methods.push(method),
}
}
// 类范围包括 class 开头与闭合花括号,中间空白/注释仍属于原文范围。
let span = start.through(end);
Decl { span, kind: DeclKind::Class(Class { name, base, fields, methods, span }) }
}
// declaration 的类体调用,先尝试完整方法,再尝试名称加类型的字段。
rule class_member() -> ClassMember
= function:function() { ClassMember::Method(function) }
/ field:parameter() { ClassMember::Field(field) }
// parse_ast 的交互入口;恰好一个完整语句包装为列表,不管理多次提交的环境。
pub rule interactive() -> InteractiveInput
= statement:interactive_statement() {
InteractiveInput { span: statement.span, statements: vec![statement] }
}
// 交互允许声明、控制流、let、属性/普通赋值及表达式,不提供根级 return。
rule interactive_statement() -> Stmt
= declaration:declaration() {
Stmt { span: declaration.span, kind: StmtKind::Declaration(declaration) }
}
/ control:control_statement() { control }
/ binding:binding() { binding_statement(binding) }
/ assignment:member_assignment() { assignment }
/ assignment:assignment() { assignment }
/ expression:expression() {
Stmt { span: expression.span, kind: StmtKind::Expression(expression) }
}
// 顶层和类方法共用函数语法,显式参数类型、返回类型及函数体;self 合法性留给分析。
rule function() -> Function
= start:function_keyword() name:name() lparen()
parameters:(parameter() ** comma()) rparen() colon() return_type:name() body:block() {
Function { span: start.through(body.span), name, parameters, return_type, body }
}
// 函数参数或类字段共用名称/类型名节点,语法只保留注解文本而不解析类型身份。
rule parameter() -> Parameter
= name:name() colon() type_name:name() {
Parameter { span: name.span.through(type_name.span), name, type_name }
}
// function 和控制流共用块语法,普通语句后可接一个末尾 return;return 当前要求表达式。
rule block() -> Block
= start:lbrace() locals:local_statement()* returned:return_statement()? end:rbrace() {
// 先保留普通语句序列,下面仅在存在末尾 return 时追加,保持源码顺序。
let mut statements = locals;
// 可选 return 不会被当作普通表达式消费,也不能后接其他块内语句。
if let Some(returned) = returned { statements.push(returned); }
Block { statements, span: start.through(end) }
}
// 块内语句分派;成员赋值先于普通赋值,最后表达式分支显式排除 return 前缀。
rule local_statement() -> Stmt
= control:control_statement() { control }
/ binding:binding() { binding_statement(binding) }
/ assignment:member_assignment() { assignment }
/ assignment:assignment() { assignment }
/ !return_keyword() expression:expression() {
Stmt { span: expression.span, kind: StmtKind::Expression(expression) }
}
// 交互或块内解析 if/else、while,条件和嵌套 block 都保留为语法树,尚不推断初始化。
rule control_statement() -> Stmt
= start:keyword("if") condition:expression() body:block()
otherwise:(keyword("else") body:block() { body })? {
// if 范围有 else 时延伸到 else 块末尾,否则只延伸到 then 块末尾。
let span=start.through(otherwise.as_ref().map_or(body.span,|b|b.span));
Stmt {span,kind:StmtKind::If(IfStatement {condition,body,otherwise,span})}
}
/ start:keyword("while") condition:expression() body:block() {
// while 范围覆盖关键字、条件及整个循环体。
let span=start.through(body.span);
Stmt {span,kind:StmtKind::While(WhileStatement {condition,body,span})}
}
// 交互或局部普通赋值;允许语法上未声明目标,具体全局/局部规则由 compiler::analyze 决定。
rule assignment() -> Stmt
= name:name() equal() value:expression() {
// 赋值语句的原文范围从目标名延伸到右值末尾。
let span = name.span.through(value.span);
Stmt { span, kind: StmtKind::Assignment(Assignment { name, value, span }) }
}
// 先解析后缀目标再检查其最外层确为 Member;错误目标使本规则失败,不执行对象写入。
rule member_assignment() -> Stmt
= target:postfix_expression() equal() value:expression() {?
// 属性赋值保留整个接收对象/属性链及右值的原文范围。
let span = target.span.through(value.span);
// 只有成员访问可拆成接收对象与字段名,裸名字留给普通赋值分支识别。
match target.kind {
ExprKind::Member { object, name } => Ok(Stmt {
span, kind: StmtKind::MemberAssignment(MemberAssignment { object: *object, name, value, span }),
}),
_ => Err("member assignment target"),
}
}
// 交互/局部 let 绑定,范围包括 let 关键字;重复名和右值初始化检查在后续语义分析。
rule binding() -> Binding
= start:let_keyword() name:name() equal() value:expression() {
Binding { span: start.through(value.span), name, value }
}
// block 的可选末尾返回,语法要求一个表达式,构造 ReturnStatement 的 Some 值。
rule return_statement() -> Stmt
= start:return_keyword() value:expression() {
// return 范围从关键字到返回表达式末尾,包括其间空白。
let span = start.through(value.span);
Stmt { span, kind: StmtKind::Return(ReturnStatement { value: Some(value), span }) }
}
// parse_expression 公开入口及各子表达式共用;precedence 层由低到高组织,二元操作在同层左结合。
pub rule expression() -> Expr = precedence! {
// 比较层优先级最低;六种比较均复用 binary 包装左右节点和范围。
left:(@) [Token {kind:TokenKind::EqualEqual,..}] right:@ {binary(left,BinaryOp::Equal,right)}
left:(@) [Token {kind:TokenKind::NotEqual,..}] right:@ {binary(left,BinaryOp::NotEqual,right)}
left:(@) [Token {kind:TokenKind::Less,..}] right:@ {binary(left,BinaryOp::Less,right)}
left:(@) [Token {kind:TokenKind::LessEqual,..}] right:@ {binary(left,BinaryOp::LessEqual,right)}
left:(@) [Token {kind:TokenKind::Greater,..}] right:@ {binary(left,BinaryOp::Greater,right)}
left:(@) [Token {kind:TokenKind::GreaterEqual,..}] right:@ {binary(left,BinaryOp::GreaterEqual,right)}
--
// 加减层高于比较、低于乘除,同层按左结合嵌套。
left:(@) plus() right:@ { binary(left, BinaryOp::Add, right) }
left:(@) minus() right:@ { binary(left, BinaryOp::Subtract, right) }
--
// 乘除层先构造节点,再交给外围的加减/比较层。
left:(@) star() right:@ { binary(left, BinaryOp::Multiply, right) }
left:(@) slash() right:@ { binary(left, BinaryOp::Divide, right) }
--
// 前缀 await 构造等待节点,是否允许停放由执行上下文决定。
start:keyword("await") operand:@ {
Expr { span: start.through(operand.span), kind: ExprKind::Await(Box::new(operand)) }
}
// 前缀取负包住当前高优先级操作数,不在语法阶段计算数值。
start:minus() operand:@ {
Expr { span: start.through(operand.span), kind: ExprKind::Unary { operation: UnaryOp::Negate, operand: Box::new(operand) } }
}
--
// 后缀表达式位于最高优先级层,先形成调用/成员链,再被一元或二元节点包裹。
expression:postfix_expression() { expression }
}
// 优先级最高的 primary 后接零或多个成员/调用后缀,从左到右 fold 保持链式求值结构。
rule postfix_expression() -> Expr
= expression:primary() suffixes:suffix()* {
// parser::postfix 每次把先前结果作为接收对象或被调用表达式,构造 a.b(c).d 一类链。
suffixes.into_iter().fold(expression, postfix)
}
// postfix_expression 共用后缀;点后取 Name,调用保存参数列表及右括号范围。
rule suffix() -> Suffix
= dot() name:name() { Suffix::Member(name) }
/ lparen() arguments:(expression() ** comma()) end:rparen() { Suffix::Call(arguments, end) }
// 原子表达式:括号、符号、数字、字符串或名称;AST 文本均从匹配 Token 原文复制/解码。
rule primary() -> Expr
= start:lparen() expression:expression() end:rparen() {
// 括号不新增语义节点种类,只把子表达式范围扩大到两侧括号。
Expr { span: start.through(end), ..expression }
}
/ start:dot() name:name() {
// 点开头的符号与成员后缀不同;前端保留它,编译器当前尚不支持生成该运行时值。
Expr { span: start.through(name.span), kind: ExprKind::Symbol(name) }
}
/ [Token { kind: TokenKind::Number, span }] {
// 数值保存原始十进制拼写,精确解析与负号处理留给 codegen。
Expr { span, kind: ExprKind::Number(source[span.start..span.end].into()) }
}
/ [Token { kind: TokenKind::String, span }] {
// Token 跨度含引号,去掉引号后调用已验证转义的 decode_string,AST 保存解码结果。
Expr { span, kind: ExprKind::String(decode_string(&source[span.start + 1..span.end - 1])) }
}
/ name:name() { Expr { span: name.span, kind: ExprKind::Name(name.text) } }
}
}