Files
endorphin-aster-vm/crates/frontend/src/grammar.peg
T
codex-bot 4bf889e5b7
Verify databases and deploy Aster VM / verify-and-deploy (push) Successful in 6m48s
Move PEG grammar into a dedicated source file
2026-10-19 02:56:33 +08:00

116 lines
5.0 KiB
Plaintext

peg::parser! {
grammar syntax(source: &str) for [Token] {
rule keyword(word: &str) -> Span
= [Token { kind: TokenKind::Name, span } if &source[span.start..span.end] == word] { span }
rule function_keyword() -> Span
= quiet!{keyword("function")} / expected!("'function'")
rule let_keyword() -> Span
= quiet!{keyword("let")} / expected!("'let'")
rule return_keyword() -> Span
= quiet!{keyword("return")} / expected!("'return'")
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!("')'")
rule lbrace() -> Span
= quiet!{[Token { kind: TokenKind::LeftBrace, span }] { span }} / expected!("'{'")
rule rbrace() -> Span
= quiet!{[Token { kind: TokenKind::RightBrace, span }] { span }} / expected!("'}'")
rule colon()
= quiet!{[Token { kind: TokenKind::Colon, .. }]} / expected!("':'")
rule comma()
= quiet!{[Token { kind: TokenKind::Comma, .. }]} / expected!("','")
rule equal()
= quiet!{[Token { kind: TokenKind::Equal, .. }]} / expected!("'='")
rule dot() -> Span
= quiet!{[Token { kind: TokenKind::Dot, span }] { span }} / expected!("'.'")
rule plus()
= [Token { kind: TokenKind::Plus, .. }]
rule minus() -> Span
= [Token { kind: TokenKind::Minus, span }] { span }
rule star()
= [Token { kind: TokenKind::Star, .. }]
rule slash()
= [Token { kind: TokenKind::Slash, .. }]
pub rule program() -> Program
= functions:function()+ {
let span = functions[0].span.through(functions.last().unwrap().span);
Program { functions, span }
}
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 }
}
rule block() -> Block
= start:lbrace() bindings:binding()* returned:return_statement() end:rbrace() {
Block { bindings, result: returned.0, return_span: returned.1, span: start.through(end) }
}
rule binding() -> Binding
= start:let_keyword() name:name() equal() value:expression() {
Binding { span: start.through(value.span), name, value }
}
rule return_statement() -> (Expr, Span)
= start:return_keyword() value:expression() {
let span = start.through(value.span);
(value, span)
}
pub rule expression() -> Expr = precedence! {
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) }
--
start:minus() operand:@ {
Expr { span: start.through(operand.span), kind: ExprKind::Unary { operation: UnaryOp::Negate, operand: Box::new(operand) } }
}
--
expression:postfix_expression() { expression }
}
rule postfix_expression() -> Expr
= expression:primary() suffixes:suffix()* {
suffixes.into_iter().fold(expression, postfix)
}
rule suffix() -> Suffix
= dot() name:name() { Suffix::Member(name) }
/ lparen() arguments:(expression() ** comma()) end:rparen() { Suffix::Call(arguments, end) }
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 }] {
Expr { span, kind: ExprKind::Number(source[span.start..span.end].into()) }
}
/ [Token { kind: TokenKind::String, span }] {
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) } }
}
}