use super::{PResult, PResultExt, Parse, ParseError, Parser}; use crate::{ ast::*, token::{TKind, Token}, }; /// Precedence levels of value and type pattern expressions. /// /// Lower (toward [Prec::Min]) precedence levels can contain /// all higher (toward [Prec::Max]) precedence levels. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Prec { /// The lowest precedence Min, /// "Alternate" pattern: `Pat | Pat` Alt, /// Tuple pattern: `Pat,+` Tuple, /// Type annotation: `Pat : Pat` Typed, /// Function pattern: `Pat -> Pat` Fn, /// Range pattern: `Pat .. Pat`, `Pat ..= Pat` Range, /// The highest precedence Max, } macro_rules! intify { ($enum:ident($value:path) = $min:ident, $max: ident, $($variant:ident),*$(,)?) => { #[expect(non_upper_case_globals)] { const $min: u32 = $enum::$min as _; const $max: u32 = $enum::$max as _; $(const $variant: u32 = $enum::$variant as _;)* match $value { ..=$min => $enum::$min, $($variant => $enum::$variant,)* $max.. => $enum::$max, } }}; } impl Prec { const fn from_int(value: u32) -> Self { intify! {Prec(value) = Min, Max, Alt, Tuple, Typed, Fn, Range} } /// Returns the level of precedence higher than this one const fn next(self) -> Self { Self::from_int(self as u32 + 1) } } /// Tries to map the incoming Token to a [pattern operator](PatOp) /// and its [precedence level](Prec) fn from_infix(token: &Token) -> Option<(PatOp, Prec)> { Some(match token.kind { TKind::Arrow => (PatOp::Fn, Prec::Fn), TKind::Bar => (PatOp::Alt, Prec::Alt), TKind::Colon => (PatOp::Typed, Prec::Typed), TKind::Comma => (PatOp::Tuple, Prec::Tuple), TKind::DotDot => (PatOp::RangeEx, Prec::Range), TKind::DotDotEq => (PatOp::RangeIn, Prec::Range), _ => None?, }) } impl<'t> Parse<'t> for Pat { type Prec = Prec; fn parse(p: &mut Parser<'t>, level: Prec) -> PResult { let tok = p.peek()?; // Prefix let mut head = match tok.kind { TKind::Fn => return p.consume().parse(Prec::Fn), TKind::True | TKind::False | TKind::Character | TKind::Integer | TKind::String => { Pat::Lit(p.parse(())?) } TKind::Bar => p.consume().parse(level)?, TKind::Bang => p.consume().then(Pat::Never), TKind::Amp => Pat::Op(PatOp::Ref, vec![p.consume().parse(Prec::Max)?]), TKind::Star => Pat::Op(PatOp::Ptr, vec![p.consume().parse(Prec::Max)?]), TKind::Mut => Pat::Op(PatOp::Mut, vec![p.consume().parse(Prec::Max)?]), TKind::Pub => Pat::Op(PatOp::Pub, vec![p.consume().parse(Prec::Max)?]), TKind::AmpAmp => Pat::Op( PatOp::Ref, vec![Pat::Op(PatOp::Ref, vec![p.consume().parse(Prec::Max)?])], ), TKind::Identifier => match tok.lexeme.str() { Some("_") => p.consume().then(Pat::Ignore), _ => { let mut path: Path = p.parse(())?; // TODO: make these postfix. match p.peek().map(Token::kind) { Ok(TKind::LParen) => Pat::NamedTuple(path, p.parse(Prec::Typed)?), Ok(TKind::LCurly) if level <= Prec::Tuple.next() => Pat::NamedStruct( path, p.consume() .opt(Prec::Tuple, TKind::RCurly)? .unwrap_or_else(|| Box::new(Pat::Op(PatOp::Tuple, vec![]))), ), Ok(_) | Err(ParseError::EOF(_)) => match path.parts.len() { 1 => Self::Name(path.parts.pop().expect("name has 1 part")), _ => Self::Path(path), }, Err(e) => Err(e)?, } } }, TKind::Grave => Pat::MetId(p.consume().next()?.lexeme.to_string()), TKind::DotDot => Pat::Op( PatOp::Rest, // Identifier in Rest position always becomes binder match p.consume().peek().allow_eof()?.map(Token::kind) { Some(TKind::Identifier) => vec![Pat::Name( p.take_lexeme() .expect("should have lexeme") .string() .expect("should be string"), )], Some(TKind::Grave | TKind::Integer | TKind::Character) => vec![p.parse(level)?], _ => vec![], }, ), TKind::DotDotEq => Pat::Op( PatOp::RangeIn, match p.consume().peek().allow_eof()?.map(Token::kind) { Some(TKind::Grave | TKind::Integer | TKind::Character) => vec![p.parse(level)?], _ => vec![], }, ), TKind::LParen => Pat::Op( PatOp::Tuple, p.consume() .list(vec![], Prec::Typed, TKind::Comma, TKind::RParen)?, ), TKind::LBrack => parse_array_pat(p)?, _ => Err(ParseError::NotPattern(tok.kind, tok.span))?, }; while let Ok(Some(tok)) = p.peek().allow_eof() && let Some((op, prec)) = from_infix(tok) && level <= prec { let kind = tok.kind; head = match op { PatOp::Typed => Pat::Op(PatOp::Typed, vec![head, p.consume().parse(prec.next())?]), PatOp::Fn => Pat::Op(PatOp::Fn, vec![head, p.consume().parse(Prec::Fn.next())?]), op @ (PatOp::RangeEx | PatOp::RangeIn) => Pat::Op( op, match p.consume().peek().map(Token::kind) { Ok(TKind::Integer | TKind::Character | TKind::Identifier) => { vec![head, p.parse(prec.next())?] } _ => vec![head], }, ), op => Pat::Op(op, p.consume().list_bare(vec![head], prec.next(), kind)?), } } Ok(head) } } fn parse_array_pat(p: &mut Parser<'_>) -> PResult { if p.consume().peek()?.kind == TKind::RBrack { p.consume(); return Ok(Pat::Op(PatOp::Slice, vec![])); } let item = p.parse(Prec::Tuple)?; let repeat = p.opt_if(Prec::Tuple, TKind::Semi)?; p.expect(TKind::RBrack)?; Ok(match (repeat, item) { (Some(repeat), item) => Pat::Op(PatOp::ArRep, vec![item, repeat]), (None, Pat::Op(PatOp::Tuple, items)) => Pat::Op(PatOp::Slice, items), (None, item) => Pat::Op(PatOp::Slice, vec![item]), }) }