Actually just three programming languages in a trenchcoat
at main 94 lines 3.7 kB view raw
1use super::{expression::Precedence, *}; 2use crate::{Parser, Spanned}; 3use source_span::Span; 4use trilogy_scanner::{Token, TokenType::*}; 5 6/// A procedure closure `do` expression. 7/// 8/// ```trilogy 9/// do() {} 10/// ``` 11#[derive(Clone, Debug)] 12pub struct DoExpression { 13 pub head: DoHead, 14 pub body: DoBody, 15 pub span: Span, 16} 17 18impl Spanned for DoExpression { 19 fn span(&self) -> Span { 20 self.span 21 } 22} 23 24impl DoExpression { 25 pub(crate) fn parse(parser: &mut Parser) -> SyntaxResult<Self> { 26 let head = DoHead::parse(parser)?; 27 Self::parse_with_head(parser, head) 28 } 29 30 pub(crate) fn parse_with_head(parser: &mut Parser, head: DoHead) -> SyntaxResult<Self> { 31 if head.parameter_list.is_none() { 32 parser.error(ErrorKind::DoMissingParameterList.at(head.span())); 33 } 34 let body = DoBody::parse(parser)?; 35 Ok(Self { 36 span: head.span().union(body.span()), 37 head, 38 body, 39 }) 40 } 41 42 pub fn r#do(&self) -> &Token { 43 &self.head.r#do 44 } 45} 46 47/// The body of a procedure closure. 48#[derive(Clone, Debug, Spanned)] 49pub enum DoBody { 50 /// A block used as the body of a `do` closure. 51 /// 52 /// Returns unit if no explicit `return` statement is reached. 53 Block(Box<Block>), 54 /// A single expression used as the body and return value of a `do` closure. 55 Expression(Box<Expression>), 56} 57 58impl DoBody { 59 pub(crate) fn parse(parser: &mut Parser) -> SyntaxResult<Self> { 60 if parser.check(OBrace).is_ok() { 61 Ok(Self::Block(Box::new(Block::parse(parser)?))) 62 } else { 63 Ok(Self::Expression(Box::new(Expression::parse_precedence( 64 parser, 65 Precedence::Continuation, 66 )?))) 67 } 68 } 69} 70 71#[cfg(test)] 72mod test { 73 use super::*; 74 75 test_parse!(do_block: "do() {}" => DoExpression::parse => DoExpression { body: DoBody::Block(..), .. }); 76 test_parse_error!(do_block_no_params: "do {}" => DoExpression::parse); 77 test_parse!(do_block_params: "do(a, b) {}" => DoExpression::parse => DoExpression { body: DoBody::Block(..), .. }); 78 test_parse!(do_block_params_trailing_comma: "do(a, b, ) {}" => DoExpression::parse => DoExpression { body: DoBody::Block(..), .. }); 79 test_parse_error!(do_block_params_leading_comma: "do(, a) {}" => DoExpression::parse); 80 test_parse_error!(do_block_params_empty_comma: "do(,) {}" => DoExpression::parse); 81 test_parse_error!(do_block_missing_paren: "do(a {}" => DoExpression::parse => "expected `)` to end parameter list"); 82 test_parse_error!(do_block_invalid: "do() { exit }" => DoExpression::parse); 83 84 test_parse!(do_expr_spaced: "do () 3" => DoExpression::parse => DoExpression { body: DoBody::Expression(..), .. }); 85 test_parse_error!(do_expr_bang: "do!() 3" => DoExpression::parse => "a `do` closure definition does not use `!`"); 86 test_parse_error!(do_no_parens: "do 3" => DoExpression::parse => "a `do` closure requires a parameter list, even if empty"); 87 88 test_parse!(do_expr: "do() 3" => DoExpression::parse => DoExpression { body: DoBody::Expression(..), .. }); 89 test_parse!(do_expr_params: "do(a, b) a + b" => DoExpression::parse => DoExpression { body: DoBody::Expression(..), .. }); 90 test_parse!(do_expr_params_trailing_comma: "do(a, b, ) a + b" => DoExpression::parse => DoExpression { body: DoBody::Expression(..), .. }); 91 test_parse_error!(do_expr_params_leading_comma: "do(, a) a" => DoExpression::parse); 92 test_parse_error!(do_expr_params_empty_comma: "do(,) 3" => DoExpression::parse); 93 test_parse_error!(do_expr_invalid: "do() return" => DoExpression::parse); 94}