summaryrefslogtreecommitdiff
path: root/src/parse
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse')
-rw-r--r--src/parse/mod.rs70
-rw-r--r--src/parse/tokens.rs8
2 files changed, 51 insertions, 27 deletions
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<Expr> {
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<Expr> {
let start = p.next_start();
@@ -552,32 +569,37 @@ fn let_expr(p: &mut Parser) -> Option<Expr> {
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<Token<'static>> {
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),