184 lines
8.1 KiB
Plaintext
184 lines
8.1 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
|
|
= declarations:declaration()+ {
|
|
let span = declarations[0].span.through(declarations.last().unwrap().span);
|
|
Program { declarations, span }
|
|
}
|
|
|
|
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() {
|
|
let mut fields = Vec::new();
|
|
let mut methods = Vec::new();
|
|
for member in members {
|
|
match member {
|
|
ClassMember::Field(field) => fields.push(field),
|
|
ClassMember::Method(method) => methods.push(method),
|
|
}
|
|
}
|
|
let span = start.through(end);
|
|
Decl { span, kind: DeclKind::Class(Class { name, base, fields, methods, span }) }
|
|
}
|
|
|
|
rule class_member() -> ClassMember
|
|
= function:function() { ClassMember::Method(function) }
|
|
/ field:parameter() { ClassMember::Field(field) }
|
|
|
|
pub rule interactive() -> InteractiveInput
|
|
= statement:interactive_statement() {
|
|
InteractiveInput { span: statement.span, statements: vec![statement] }
|
|
}
|
|
|
|
rule interactive_statement() -> Stmt
|
|
= declaration:declaration() {
|
|
Stmt { span: declaration.span, kind: StmtKind::Declaration(declaration) }
|
|
}
|
|
/ binding:binding() { binding_statement(binding) }
|
|
/ assignment:member_assignment() { assignment }
|
|
/ assignment:assignment() { assignment }
|
|
/ expression:expression() {
|
|
Stmt { span: expression.span, kind: StmtKind::Expression(expression) }
|
|
}
|
|
|
|
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() locals:local_statement()* returned:return_statement()? end:rbrace() {
|
|
let mut statements = locals;
|
|
if let Some(returned) = returned { statements.push(returned); }
|
|
Block { statements, span: start.through(end) }
|
|
}
|
|
|
|
rule local_statement() -> Stmt
|
|
= binding:binding() { binding_statement(binding) }
|
|
/ assignment:member_assignment() { assignment }
|
|
/ assignment:assignment() { assignment }
|
|
/ !return_keyword() expression:expression() {
|
|
Stmt { span: expression.span, kind: StmtKind::Expression(expression) }
|
|
}
|
|
|
|
rule assignment() -> Stmt
|
|
= name:name() equal() value:expression() {
|
|
let span = name.span.through(value.span);
|
|
Stmt { span, kind: StmtKind::Assignment(Assignment { name, value, span }) }
|
|
}
|
|
|
|
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"),
|
|
}
|
|
}
|
|
|
|
rule binding() -> Binding
|
|
= start:let_keyword() name:name() equal() value:expression() {
|
|
Binding { span: start.through(value.span), name, value }
|
|
}
|
|
|
|
rule return_statement() -> Stmt
|
|
= start:return_keyword() value:expression() {
|
|
let span = start.through(value.span);
|
|
Stmt { span, kind: StmtKind::Return(ReturnStatement { value: Some(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: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 }
|
|
}
|
|
|
|
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) } }
|
|
}
|
|
}
|