diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-03-03 22:33:00 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-03-03 22:33:00 +0100 |
| commit | 193734f45385b179f40dc0145034204dad3cae8f (patch) | |
| tree | d6601602e57da3414302f9f8fd78b446593a07bf /src/parse/mod.rs | |
| parent | 1cfc3c72b59f96d1cef3f2e6e488b645648c36ab (diff) | |
Callee expressions 🍅
Diffstat (limited to 'src/parse/mod.rs')
| -rw-r--r-- | src/parse/mod.rs | 153 |
1 files changed, 82 insertions, 71 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs index afd85193..f23f1978 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -75,7 +75,7 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> { let group = if stmt { Group::Stmt } else { Group::Expr }; p.start_group(group, TokenMode::Code); - let expr = primary(p); + let expr = expr_with(p, true, 0); if stmt && expr.is_some() && !p.eof() { p.expected_at("semicolon or line break", p.end()); } @@ -166,8 +166,73 @@ fn unicode_escape(p: &mut Parser, token: TokenUnicodeEscape) -> String { text } +/// Parse an expression. +fn expr(p: &mut Parser) -> Option<Expr> { + expr_with(p, false, 0) +} + +/// Parse an expression with operators having at least the minimum precedence. +/// +/// If `atomic` is true, this does not parse binary operations and arrow +/// functions, which is exactly what we want in a shorthand expression directly +/// in markup. +/// +/// Stops parsing at operations with lower precedence than `min_prec`, +fn expr_with(p: &mut Parser, atomic: bool, min_prec: usize) -> Option<Expr> { + let start = p.start(); + let mut lhs = match p.eat_map(UnOp::from_token) { + Some(op) => { + let prec = op.precedence(); + let expr = Box::new(expr_with(p, atomic, prec)?); + Expr::Unary(ExprUnary { span: p.span(start), op, expr }) + } + None => primary(p, atomic)?, + }; + + loop { + // Parenthesis or bracket means this is a function call. + if matches!( + p.peek_direct(), + Some(Token::LeftParen) | Some(Token::LeftBracket), + ) { + lhs = call(p, lhs); + continue; + } + + if atomic { + break; + } + + let op = match p.peek().and_then(BinOp::from_token) { + Some(binop) => binop, + None => break, + }; + + let mut prec = op.precedence(); + if prec < min_prec { + break; + } + + p.eat(); + match op.associativity() { + Associativity::Left => prec += 1, + Associativity::Right => {} + } + + let rhs = match expr_with(p, atomic, prec) { + Some(rhs) => Box::new(rhs), + None => break, + }; + + let span = lhs.span().join(rhs.span()); + lhs = Expr::Binary(ExprBinary { span, lhs: Box::new(lhs), op, rhs }); + } + + Some(lhs) +} + /// Parse a primary expression. -fn primary(p: &mut Parser) -> Option<Expr> { +fn primary(p: &mut Parser, atomic: bool) -> Option<Expr> { if let Some(expr) = literal(p) { return Some(expr); } @@ -175,31 +240,23 @@ fn primary(p: &mut Parser) -> Option<Expr> { match p.peek() { // Things that start with an identifier. Some(Token::Ident(string)) => { - let ident = Ident { + let id = Ident { span: p.eat_span(), string: string.into(), }; - // Parenthesis or bracket means this is a function call. - if matches!( - p.peek_direct(), - Some(Token::LeftParen) | Some(Token::LeftBracket), - ) { - return Some(call(p, ident)); - } - - // Arrow means this is closure's lone parameter. - if p.eat_if(Token::Arrow) { + // Arrow means this is a closure's lone parameter. + Some(if !atomic && p.eat_if(Token::Arrow) { let body = expr(p)?; - return Some(Expr::Closure(ExprClosure { - span: ident.span.join(body.span()), + Expr::Closure(ExprClosure { + span: id.span.join(body.span()), name: None, - params: Rc::new(vec![ident]), + params: Rc::new(vec![id]), body: Rc::new(body), - })); - } - - Some(Expr::Ident(ident)) + }) + } else { + Expr::Ident(id) + }) } // Structures. @@ -260,7 +317,7 @@ pub fn parenthesized(p: &mut Parser) -> Option<Expr> { return Some(dict(p, items, span)); } - // Arrow means this is closure's parameter list. + // Arrow means this is a closure's parameter list. if p.eat_if(Token::Arrow) { let params = params(p, items); let body = expr(p)?; @@ -400,54 +457,8 @@ fn block(p: &mut Parser, scoping: bool) -> Expr { Expr::Block(ExprBlock { span, exprs, scoping }) } -/// Parse an expression. -fn expr(p: &mut Parser) -> Option<Expr> { - expr_with(p, 0) -} - -/// Parse an expression with operators having at least the minimum precedence. -fn expr_with(p: &mut Parser, min_prec: usize) -> Option<Expr> { - let start = p.start(); - let mut lhs = match p.eat_map(UnOp::from_token) { - Some(op) => { - let prec = op.precedence(); - let expr = Box::new(expr_with(p, prec)?); - Expr::Unary(ExprUnary { span: p.span(start), op, expr }) - } - None => primary(p)?, - }; - - loop { - let op = match p.peek().and_then(BinOp::from_token) { - Some(binop) => binop, - None => break, - }; - - let mut prec = op.precedence(); - if prec < min_prec { - break; - } - - p.eat(); - match op.associativity() { - Associativity::Left => prec += 1, - Associativity::Right => {} - } - - let rhs = match expr_with(p, prec) { - Some(rhs) => Box::new(rhs), - None => break, - }; - - let span = lhs.span().join(rhs.span()); - lhs = Expr::Binary(ExprBinary { span, lhs: Box::new(lhs), op, rhs }); - } - - Some(lhs) -} - /// Parse a function call. -fn call(p: &mut Parser, name: Ident) -> Expr { +fn call(p: &mut Parser, callee: Expr) -> Expr { let mut args = match p.peek_direct() { Some(Token::LeftParen) => { p.start_group(Group::Paren, TokenMode::Code); @@ -456,7 +467,7 @@ fn call(p: &mut Parser, name: Ident) -> Expr { args } _ => ExprArgs { - span: Span::at(name.span.end), + span: Span::at(callee.span().end), items: vec![], }, }; @@ -467,8 +478,8 @@ fn call(p: &mut Parser, name: Ident) -> Expr { } Expr::Call(ExprCall { - span: p.span(name.span.start), - callee: Box::new(Expr::Ident(name)), + span: p.span(callee.span().start), + callee: Box::new(callee), args, }) } |
