From 422b8e640f00977177a5a7250a3c56009eed10c4 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 26 Jun 2021 18:07:05 +0200 Subject: With expressions --- src/parse/mod.rs | 70 +++++++++++++++++++++++++++++++++++------------------ src/parse/tokens.rs | 8 +++--- 2 files changed, 51 insertions(+), 27 deletions(-) (limited to 'src/parse') diff --git a/src/parse/mod.rs b/src/parse/mod.rs index d8000d8c..381d44e2 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -245,6 +245,10 @@ fn expr_with(p: &mut Parser, atomic: bool, min_prec: usize) -> Option { continue; } + if p.eat_if(Token::With) { + lhs = with_expr(p, lhs); + } + if atomic { break; } @@ -545,6 +549,19 @@ fn args(p: &mut Parser) -> CallArgs { CallArgs { span: p.span(start), items } } +/// Parse a with expression. +fn with_expr(p: &mut Parser, callee: Expr) -> Expr { + p.start_group(Group::Paren, TokenMode::Code); + let args = args(p); + p.end_group(); + + Expr::With(WithExpr { + span: p.span(callee.span().start), + callee: Box::new(callee), + args, + }) +} + /// Parse a let expression. fn let_expr(p: &mut Parser) -> Option { let start = p.next_start(); @@ -552,32 +569,37 @@ fn let_expr(p: &mut Parser) -> Option { let mut let_expr = None; if let Some(binding) = ident(p) { - // If a parenthesis follows, this is a function definition. - let mut params = None; - if p.peek_direct() == Some(Token::LeftParen) { - p.start_group(Group::Paren, TokenMode::Code); - let items = collection(p).0; - params = Some(idents(p, items)); - p.end_group(); - } - let mut init = None; - if p.eat_if(Token::Eq) { - init = expr(p); - } else if params.is_some() { - // Function definitions must have a body. - p.expected_at("body", p.prev_end()); - } - // Rewrite into a closure expression if it's a function definition. - if let Some(params) = params { - let body = init?; - init = Some(Expr::Closure(ClosureExpr { - span: binding.span.join(body.span()), - name: Some(binding.clone()), - params: Rc::new(params), - body: Rc::new(body), - })); + if p.eat_if(Token::With) { + init = Some(with_expr(p, Expr::Ident(binding.clone()))); + } else { + // If a parenthesis follows, this is a function definition. + let mut params = None; + if p.peek_direct() == Some(Token::LeftParen) { + p.start_group(Group::Paren, TokenMode::Code); + let items = collection(p).0; + params = Some(idents(p, items)); + p.end_group(); + } + + if p.eat_if(Token::Eq) { + init = expr(p); + } else if params.is_some() { + // Function definitions must have a body. + p.expected_at("body", p.prev_end()); + } + + // Rewrite into a closure expression if it's a function definition. + if let Some(params) = params { + let body = init?; + init = Some(Expr::Closure(ClosureExpr { + span: binding.span.join(body.span()), + name: Some(binding.clone()), + params: Rc::new(params), + body: Rc::new(body), + })); + } } let_expr = Some(Expr::Let(LetExpr { diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index a496010e..abc3d6a6 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -366,9 +366,6 @@ impl<'s> Tokens<'s> { fn ident(&mut self, start: usize) -> Token<'s> { self.s.eat_while(is_id_continue); match self.s.eaten_from(start) { - "not" => Token::Not, - "and" => Token::And, - "or" => Token::Or, "none" => Token::None, "auto" => Token::Auto, "true" => Token::Bool(true), @@ -489,6 +486,10 @@ impl Debug for Tokens<'_> { fn keyword(id: &str) -> Option> { Some(match id { + "not" => Token::Not, + "and" => Token::And, + "or" => Token::Or, + "with" => Token::With, "let" => Token::Let, "if" => Token::If, "else" => Token::Else, @@ -777,6 +778,7 @@ mod tests { fn test_tokenize_keywords() { // A list of a few (not all) keywords. let list = [ + ("not", Not), ("let", Let), ("if", If), ("else", Else), -- cgit v1.2.3