diff --git a/src/syn/ast.rs b/src/syn/ast.rs index 6a7eddc..90343d7 100644 --- a/src/syn/ast.rs +++ b/src/syn/ast.rs @@ -26,17 +26,19 @@ pub enum Expr { Un(Box), FunCall(Box), Index(Box), + Fun(Box), Base(BaseExpr), } impl Spanned for Expr { fn span(&self) -> Span { match self { - Expr::Base(b) => b.span(), Expr::Bin(b) => b.span(), Expr::Un(u) => u.span(), Expr::FunCall(f) => f.span(), Expr::Index(i) => i.span(), + Expr::Fun(f) => f.span(), + Expr::Base(b) => b.span(), } } } @@ -120,6 +122,21 @@ impl Spanned for IndexExpr { } } +#[derive(Derivative, Clone, PartialEq, Eq)] +#[derivative(Debug)] +pub struct FunExpr { + pub params: Vec, + pub expr: Expr, + #[derivative(Debug = "ignore")] + pub span: Span, +} + +impl Spanned for FunExpr { + fn span(&self) -> Span { + self.span + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum BaseExprKind { Ident, diff --git a/src/syn/lexer.rs b/src/syn/lexer.rs index 95be49a..7c70065 100644 --- a/src/syn/lexer.rs +++ b/src/syn/lexer.rs @@ -26,6 +26,10 @@ impl<'t> Lexer<'t> { self.chars().next().is_none() } + pub fn text(&self) -> &'t str { + &self.text + } + //////////////////////////////////////////////////////////////////////////////// // Character advancement //////////////////////////////////////////////////////////////////////////////// diff --git a/src/syn/parser.rs b/src/syn/parser.rs index 538b400..d2c8795 100644 --- a/src/syn/parser.rs +++ b/src/syn/parser.rs @@ -253,26 +253,34 @@ impl<'t> Parser<'t> { } TokenKind::LParen => { let prev_skip = self.set_skip_newlines(true)?; - let first = self.next_expr()?; - if let Some(_) = self.match_token_kind(TokenKind::Comma)? { - let mut list = self.next_expr_list(TokenKind::RParen)?; - - self.set_skip_newlines(prev_skip)?; - let end_token = self.expect_token_kind(TokenKind::RParen, "end of tuple")?; - + if let Some(end_token) = self.match_token_kind(TokenKind::RParen)? { + // empty tuple let span = token.span().union(end_token.span()); - list.insert(0, first); - Expr::Base( - BaseExpr { - kind: BaseExprKind::Tuple(list), - span, - } - .into(), - ) + Expr::Base(BaseExpr { + kind: BaseExprKind::Tuple(Default::default()), + span, + }) } else { - self.set_skip_newlines(prev_skip)?; - self.expect_token_kind(TokenKind::RParen, "end of expression")?; - first + let first = self.next_expr()?; + if let Some(_) = self.match_token_kind(TokenKind::Comma)? { + let mut list = self.next_expr_list(TokenKind::RParen)?; + + self.set_skip_newlines(prev_skip)?; + let end_token = self.expect_token_kind(TokenKind::RParen, "end of tuple")?; + let span = token.span().union(end_token.span()); + list.insert(0, first); + Expr::Base( + BaseExpr { + kind: BaseExprKind::Tuple(list), + span, + } + .into(), + ) + } else { + self.set_skip_newlines(prev_skip)?; + self.expect_token_kind(TokenKind::RParen, "end of expression")?; + first + } } } _ => unreachable!(), @@ -306,6 +314,51 @@ impl<'t> Parser<'t> { let span = expr.span().union(end_token.span()); let index_expr = Expr::Index(IndexExpr { expr, index, span }.into()); self.next_suffix_expr(index_expr) + } else if self.match_token_kind(TokenKind::Arrow)?.is_some() { + // check that the LHS is a tuple of idents, and get the parameters as well. + let mut params: Vec; + match expr { + Expr::Base(BaseExpr { kind: BaseExprKind::Tuple(ref tuple), .. }) => { + // Multi-parameter case + params = Vec::with_capacity(tuple.len()); + for param_expr in tuple.iter() { + if let Expr::Base(BaseExpr { kind: BaseExprKind::Ident, span, }) = ¶m_expr { + let name = span.text_at(self.lexer.text()); + params.push(name.to_string()); + } else { + return Err(Error::ExpectedGot { + expected: "identifier".to_string(), + // TODO : something more specific + got: "some other expression".to_string(), + pos: param_expr.span().start, + }); + } + } + } + Expr::Base(BaseExpr { kind: BaseExprKind::Ident, span }) => { + // Single parameter case + params = vec![span.text_at(self.lexer.text()).to_string()] + } + _ => { + return Err(Error::ExpectedGot { + expected: "tuple of function parameters".to_string(), + // TODO : something more specific + got: "something else".to_string(), + pos: expr.span().start, + }); + } + } + + // function definition + let rhs = self.next_expr()?; + let span = expr.span().union(rhs.span()); + // TODO : get params from tuple expr - it's a little funky to do it at this stage but I + // think it will be fine + Ok(Expr::Fun(FunExpr { + params, + expr: rhs, + span, + }.into())) } else { Ok(expr) } @@ -624,6 +677,17 @@ mod test { ) } + fn fun_expr(params: Vec, expr: Expr) -> Expr { + Expr::Fun( + FunExpr { + params, + expr, + span: Default::default(), + } + .into(), + ) + } + #[test] fn test_base_expr() { test_parser!( @@ -732,6 +796,66 @@ mod test { ); } + #[test] + fn test_fun_expr() { + test_parser!( + Parser::try_from( + r#"() -> ()"# + ) + .unwrap(), + next_expr, + fun_expr( + vec![], + base_expr(BaseExprKind::Tuple(vec![])) + ) + ); + + test_parser!( + Parser::try_from( + r#"() -> 1"# + ) + .unwrap(), + next_expr, + fun_expr( + vec![], + base_expr(BaseExprKind::Num), + ) + ); + + test_parser!( + Parser::try_from( + r#"(a) -> 1"# + ) + .unwrap(), + next_expr, + fun_expr( + vec![ + String::from("a"), + ], + base_expr(BaseExprKind::Num), + ) + ); + + test_parser!( + Parser::try_from( + r#"(a, b) -> a + b"# + ) + .unwrap(), + next_expr, + fun_expr( + vec![ + String::from("a"), + String::from("b"), + ], + bin_expr( + base_expr(BaseExprKind::Ident), + BinOp::Plus, + base_expr(BaseExprKind::Ident) + ) + ) + ); + } + #[test] fn test_bin_expr() { test_parser!(