use super::*; use crate::diag::Deco; /// Parse the arguments to a function call. pub fn arguments(p: &mut Parser) -> ExprArgs { collection(p, vec![]) } /// Parse a parenthesized group, which can be either of: /// - Array literal /// - Dictionary literal /// - Parenthesized expression pub fn parenthesized(p: &mut Parser) -> Expr { p.start_group(Group::Paren); let state = if p.eat_if(Token::Colon) { collection(p, State::Dict(vec![])) } else { collection(p, State::Unknown) }; p.end_group(); state.into_expr() } /// Parse a collection. fn collection(p: &mut Parser, mut collection: T) -> T { let mut missing_coma = None; while !p.eof() { if let Some(arg) = p.span_if(argument) { collection.push_arg(p, arg); if let Some(pos) = missing_coma.take() { p.diag_expected_at("comma", pos); } if p.eof() { break; } let behind = p.last_end(); if p.eat_if(Token::Comma) { collection.push_comma(); } else { missing_coma = Some(behind); } } } collection } /// Parse an expression or a named pair. fn argument(p: &mut Parser) -> Option { let first = p.span_if(expr)?; if p.eat_if(Token::Colon) { if let Expr::Ident(ident) = first.v { let expr = p.span_if(expr)?; let name = ident.with_span(first.span); p.deco(Deco::Name.with_span(name.span)); Some(Argument::Named(Named { name, expr })) } else { p.diag(error!(first.span, "name must be identifier")); expr(p); None } } else { Some(Argument::Pos(first)) } } /// Abstraction for comma-separated list of expression / named pairs. trait Collection { fn push_arg(&mut self, p: &mut Parser, arg: Spanned); fn push_comma(&mut self) {} } impl Collection for ExprArgs { fn push_arg(&mut self, _: &mut Parser, arg: Spanned) { self.push(arg.v); } } /// State of collection parsing. #[derive(Debug)] enum State { Unknown, Expr(Spanned), Array(ExprArray), Dict(ExprDict), } impl State { fn into_expr(self) -> Expr { match self { Self::Unknown => Expr::Array(vec![]), Self::Expr(expr) => expr.v, Self::Array(array) => Expr::Array(array), Self::Dict(dict) => Expr::Dict(dict), } } } impl Collection for State { fn push_arg(&mut self, p: &mut Parser, arg: Spanned) { match self { Self::Unknown => match arg.v { Argument::Pos(expr) => *self = Self::Expr(expr), Argument::Named(named) => *self = Self::Dict(vec![named]), }, Self::Expr(prev) => match arg.v { Argument::Pos(expr) => *self = Self::Array(vec![take(prev), expr]), Argument::Named(_) => diag(p, arg), }, Self::Array(array) => match arg.v { Argument::Pos(expr) => array.push(expr), Argument::Named(_) => diag(p, arg), }, Self::Dict(dict) => match arg.v { Argument::Pos(_) => diag(p, arg), Argument::Named(named) => dict.push(named), }, } } fn push_comma(&mut self) { if let Self::Expr(expr) = self { *self = Self::Array(vec![take(expr)]); } } } fn take(expr: &mut Spanned) -> Spanned { // Replace with anything, it's overwritten anyway. std::mem::replace(expr, Spanned::zero(Expr::Bool(false))) } fn diag(p: &mut Parser, arg: Spanned) { p.diag(error!(arg.span, "{}", match arg.v { Argument::Pos(_) => "expected named pair, found expression", Argument::Named(_) => "expected expression, found named pair", })); }