Skip to main content

cl_parser/parser/
pat.rs

1use super::{PResult, PResultExt, Parse, ParseError, Parser, expr::Prec as ExPrec};
2use cl_ast::{types::Path, *};
3use cl_token::{TKind, Token};
4
5/// Precedence levels of value and type pattern expressions.
6///
7/// Lower (toward [Prec::Min]) precedence levels can contain
8/// all higher (toward [Prec::Max]) precedence levels.
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
10pub enum Prec {
11    /// The lowest precedence
12    #[default]
13    Min,
14    /// "Alternate" pattern: `Pat | Pat`
15    Alt,
16    /// Tuple pattern: `Pat,+`
17    Tuple,
18    /// Type annotation: `Pat : Pat`
19    Typed,
20    /// Function signature: `foo(bar: baz, ..) -> qux`
21    Fn,
22    /// Range pattern: `Pat .. Pat`, `Pat ..= Pat`
23    Range,
24    /// The highest precedence
25    Max,
26}
27
28macro_rules! intify {
29    ($enum:ident($value:path) = $min:ident, $max: ident, $($variant:ident),*$(,)?) => {
30        #[expect(non_upper_case_globals)] {
31        const $min: u32 = $enum::$min as _;
32        const $max: u32 = $enum::$max as _;
33        $(const $variant: u32 = $enum::$variant as _;)*
34        match $value {
35            ..=$min => $enum::$min,
36            $($variant => $enum::$variant,)*
37            $max.. => $enum::$max,
38        }
39    }};
40}
41
42impl Prec {
43    const fn from_int(value: u32) -> Self {
44        intify! {Prec(value) = Min, Max, Alt, Tuple, Typed, Fn, Range}
45    }
46
47    /// Returns the level of precedence higher than this one
48    const fn next(self) -> Self {
49        Self::from_int(self as u32 + 1)
50    }
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54pub enum Prefix {
55    /// Consume (and disregard) this prefix token
56    Consume,
57    Underscore,
58    Never,
59    MetId,
60    Id,
61    Array,
62    Constant,
63    Op(PatOp),
64    Split(PatOp),
65}
66
67fn from_prefix(token: &Token) -> PResult<(Prefix, Prec)> {
68    Ok(match token.kind {
69        TKind::True
70        | TKind::False
71        | TKind::Character
72        | TKind::Integer
73        | TKind::String
74        | TKind::Minus
75        | TKind::Const => (Prefix::Constant, Prec::Max),
76        TKind::Identifier if token.lexeme.str() == Some("_") => (Prefix::Underscore, Prec::Max),
77        TKind::ColonColon | TKind::Identifier => (Prefix::Id, Prec::Max),
78        TKind::Bang => (Prefix::Never, Prec::Max),
79        TKind::Amp => (Prefix::Op(PatOp::Ref), Prec::Fn),
80        TKind::AmpAmp => (Prefix::Split(PatOp::Ref), Prec::Fn),
81        TKind::Star => (Prefix::Op(PatOp::Ptr), Prec::Max),
82        TKind::Mut => (Prefix::Op(PatOp::Mut), Prec::Max),
83        TKind::Pub => (Prefix::Op(PatOp::Pub), Prec::Max),
84        TKind::Dollar => (Prefix::MetId, Prec::Max),
85
86        TKind::Fn => (Prefix::Op(PatOp::Fn), Prec::Fn),
87        TKind::Bar => (Prefix::Consume, Prec::Alt),
88        TKind::DotDot => (Prefix::Op(PatOp::Rest), Prec::Max),
89        TKind::DotDotEq => (Prefix::Op(PatOp::RangeIn), Prec::Max),
90        TKind::LCurly => (Prefix::Op(PatOp::Record), Prec::Typed),
91        TKind::LParen => (Prefix::Op(PatOp::Tuple), Prec::Fn),
92        TKind::LBrack => (Prefix::Array, Prec::Max),
93        kind => Err(ParseError::NotPrefix(kind, token.span))?,
94    })
95}
96
97/// Tries to map the incoming Token to a [pattern operator](PatOp)
98/// and its following [precedence level](Prec)
99fn from_infix(token: &Token) -> Option<(PatOp, Prec)> {
100    Some(match token.kind {
101        TKind::Arrow => (PatOp::Fn, Prec::Fn),
102        TKind::Bar => (PatOp::Alt, Prec::Alt),
103        TKind::Colon => (PatOp::Typed, Prec::Typed),
104        TKind::Comma => (PatOp::Tuple, Prec::Tuple),
105        TKind::DotDot => (PatOp::RangeEx, Prec::Range),
106        TKind::DotDotEq => (PatOp::RangeIn, Prec::Range),
107        TKind::Lt => (PatOp::Generic, Prec::Fn),
108        TKind::LCurly => (PatOp::TypePrefixed, Prec::Typed),
109        // LParen is used in function signatures
110        TKind::LParen => (PatOp::TypePrefixed, Prec::Fn),
111        _ => None?,
112    })
113}
114
115impl<'t> Parse<'t> for Pat {
116    type Prec = Prec;
117
118    fn parse(p: &mut Parser<'t>, level: Prec) -> PResult<Self> {
119        let tok @ &Token { kind, span, .. } = p.peek()?;
120        let (op, prec) = from_prefix(tok)?;
121        if level > prec {
122            return Err(ParseError::NotPattern(kind, level, span));
123        }
124
125        let mut head = match op {
126            Prefix::Consume => p.consume().parse(level)?,
127            Prefix::Underscore => p.consume().then(Pat::Ignore),
128            Prefix::Never => p.consume().then(Pat::Never),
129            Prefix::MetId => Pat::MetId(p.consume().next()?.lexeme.to_string().as_str().into()),
130            Prefix::Constant => Pat::Value(p.parse(ExPrec::Unary.value())?),
131            Prefix::Array => parse_array_pat(p)?,
132            Prefix::Id => {
133                let At(mut path, span): At<Path> = p.parse(())?;
134                // TODO: make these postfix.
135                match path.parts.len() {
136                    1 => Pat::Name(path.parts.pop().expect("name has 1 part")),
137                    _ => Pat::Value(Box::new(At(Expr::Id(path), span))),
138                }
139            }
140            Prefix::Op(op @ (PatOp::Record | PatOp::Tuple)) => Pat::Op(
141                op,
142                p.consume()
143                    .list(vec![], Prec::Tuple.next(), TKind::Comma, kind.flip())?,
144            ),
145            Prefix::Op(op @ (PatOp::Rest | PatOp::RangeEx | PatOp::RangeIn)) => {
146                // next token must continue a pattern
147                match p.consume().peek().allow_eof()? {
148                    Some(tok) if from_prefix(tok).is_ok() => Pat::Op(op, vec![p.parse(prec)?]),
149                    _ => Pat::Op(op, vec![]),
150                }
151            }
152            Prefix::Op(op) => Pat::Op(op, vec![p.consume().parse(prec)?]),
153            Prefix::Split(op) => {
154                p.split()?;
155                Pat::Op(op, vec![p.parse(prec)?])
156            }
157        };
158
159        while let Ok(Some(tok @ &Token { kind, .. })) = p.peek().allow_eof()
160            && let Some((op, prec)) = from_infix(tok)
161            && level <= prec
162        {
163            head = match op {
164                PatOp::RangeEx | PatOp::RangeIn => Pat::Op(
165                    op,
166                    if let Some(tok) = p.consume().peek().allow_eof()?
167                        && from_prefix(tok).is_ok()
168                    {
169                        vec![head, p.parse(prec)?]
170                    } else {
171                        vec![head]
172                    },
173                ),
174                PatOp::Generic => Pat::Op(
175                    op,
176                    p.consume()
177                        .list(vec![head], Prec::Typed, TKind::Comma, kind.flip())?,
178                ),
179                PatOp::TypePrefixed => Pat::Op(op, vec![head, p.parse(prec)?]),
180                PatOp::Tuple => Pat::Op(op, p.consume().list_bare(vec![head], prec.next(), kind)?),
181                PatOp::Fn => Pat::Op(op, vec![head, p.consume().parse(prec)?]),
182                _ => Pat::Op(op, vec![head, p.consume().parse(prec.next())?]),
183            }
184        }
185        Ok(head)
186    }
187}
188
189fn parse_array_pat(p: &mut Parser<'_>) -> PResult<Pat> {
190    if p.consume().peek()?.kind == TKind::RBrack {
191        p.consume();
192        return Ok(Pat::Op(PatOp::Slice, vec![]));
193    }
194
195    let item = p.parse(Prec::Tuple)?;
196    let repeat = p.opt_if(Prec::Tuple, TKind::Semi)?;
197    p.expect(TKind::RBrack)?;
198
199    Ok(match (repeat, item) {
200        (Some(repeat), item) => Pat::Op(PatOp::ArRep, vec![item, repeat]),
201        (None, Pat::Op(PatOp::Tuple, items)) => Pat::Op(PatOp::Slice, items),
202        (None, item) => Pat::Op(PatOp::Slice, vec![item]),
203    })
204}