Skip to main content

cl_parser/parser/
pat.rs

1//! Conlang's [Pattern](Pat) parser.
2
3use super::{PResult, PResultExt, Parse, ParseError, Parser, expr::Prec as ExPrec};
4use cl_ast::{
5    types::{Literal, Path},
6    *,
7};
8use cl_token::{TKind, Token};
9
10/// Precedence levels of value and type pattern expressions.
11///
12/// Lower (toward [Prec::Min]) precedence levels can contain
13/// all higher (toward [Prec::Max]) precedence levels.
14#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
15pub enum Prec {
16    /// The lowest precedence
17    #[default]
18    Min,
19    /// "Alternate" pattern: `Pat | Pat`
20    Alt,
21    /// Tuple pattern: `Pat,+`
22    Tuple,
23    /// Type annotation: `Pat : Pat`
24    Typed,
25    /// Function signature: `foo(bar: baz, ..) -> qux`
26    Fn,
27    /// Range pattern: `Pat .. Pat`, `Pat ..= Pat`
28    Range,
29    /// The highest precedence
30    Max,
31}
32
33macro_rules! intify {
34    ($enum:ident($value:path) = $min:ident, $max: ident, $($variant:ident),*$(,)?) => {
35        #[expect(non_upper_case_globals)] {
36        const $min: u32 = $enum::$min as _;
37        const $max: u32 = $enum::$max as _;
38        $(const $variant: u32 = $enum::$variant as _;)*
39        match $value {
40            ..=$min => $enum::$min,
41            $($variant => $enum::$variant,)*
42            $max.. => $enum::$max,
43        }
44    }};
45}
46
47impl Prec {
48    const fn from_int(value: u32) -> Self {
49        intify! {Prec(value) = Min, Max, Alt, Tuple, Typed, Fn, Range}
50    }
51
52    /// Returns the level of precedence higher than this one
53    const fn next(self) -> Self {
54        Self::from_int(self as u32 + 1)
55    }
56}
57
58#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59pub enum Prefix {
60    /// Consume (and disregard) this prefix token
61    Consume,
62    Underscore,
63    Never,
64    MetId,
65    DocInner,
66    DocOuter,
67    Id,
68    Array,
69    Constant,
70    Op(PatOp),
71    Split(PatOp),
72}
73
74fn from_prefix(token: &Token) -> PResult<(Prefix, Prec)> {
75    Ok(match token.kind {
76        TKind::True
77        | TKind::False
78        | TKind::Character
79        | TKind::Integer
80        | TKind::String
81        | TKind::Minus
82        | TKind::Const => (Prefix::Constant, Prec::Max),
83        TKind::InDoc => (Prefix::DocInner, Prec::Typed),
84        TKind::OutDoc => (Prefix::DocOuter, Prec::Typed),
85        TKind::Hash => (Prefix::Op(PatOp::MetaOuter), Prec::Typed),
86        TKind::HashBang => (Prefix::Op(PatOp::MetaInner), Prec::Typed),
87        TKind::Identifier if token.lexeme.str() == Some("_") => (Prefix::Underscore, Prec::Max),
88        TKind::ColonColon | TKind::Identifier => (Prefix::Id, Prec::Max),
89        TKind::Bang => (Prefix::Never, Prec::Max),
90        TKind::Amp => (Prefix::Op(PatOp::Ref), Prec::Fn),
91        TKind::AmpAmp => (Prefix::Split(PatOp::Ref), Prec::Fn),
92        TKind::Star => (Prefix::Op(PatOp::Ptr), Prec::Max),
93        TKind::Mut => (Prefix::Op(PatOp::Mut), Prec::Typed),
94        TKind::Pub => (Prefix::Op(PatOp::Pub), Prec::Typed),
95        TKind::Dollar => (Prefix::MetId, Prec::Max),
96
97        TKind::Fn => (Prefix::Op(PatOp::Fn), Prec::Fn),
98        TKind::Bar => (Prefix::Consume, Prec::Alt),
99        TKind::DotDot => (Prefix::Op(PatOp::Rest), Prec::Max),
100        TKind::DotDotEq => (Prefix::Op(PatOp::RangeIn), Prec::Max),
101        TKind::LCurly => (Prefix::Op(PatOp::Record), Prec::Typed),
102        TKind::LParen => (Prefix::Op(PatOp::Tuple), Prec::Fn),
103        TKind::LBrack => (Prefix::Array, Prec::Max),
104        kind => Err(ParseError::NotPrefix(kind, token.span))?,
105    })
106}
107
108/// Tries to map the incoming Token to a [pattern operator](PatOp)
109/// and its following [precedence level](Prec)
110fn from_infix(token: &Token) -> Option<(PatOp, Prec)> {
111    Some(match token.kind {
112        TKind::Arrow => (PatOp::Fn, Prec::Fn),
113        TKind::Bar => (PatOp::Alt, Prec::Alt),
114        TKind::Comma => (PatOp::Tuple, Prec::Tuple),
115        TKind::Colon => (PatOp::Typed, Prec::Typed),
116        TKind::DotDot => (PatOp::RangeEx, Prec::Range),
117        TKind::DotDotEq => (PatOp::RangeIn, Prec::Range),
118        TKind::If => (PatOp::Guard, Prec::Fn),
119        TKind::LCurly => (PatOp::TypePrefixed, Prec::Fn),
120        TKind::LBrack => (PatOp::TypePrefixed, Prec::Fn),
121        TKind::LParen => (PatOp::TypePrefixed, Prec::Fn),
122        TKind::Lt => (PatOp::Generic, Prec::Fn),
123        _ => None?,
124    })
125}
126
127impl<'t> Parse<'t> for Pat {
128    type Prec = Prec;
129
130    fn parse(p: &mut Parser<'t>, level: Prec) -> PResult<Self> {
131        let tok @ &Token { kind, span, .. } = p.peek()?;
132        let (op, prec) = from_prefix(tok)?;
133
134        let mut head = match op {
135            Prefix::Consume => p.consume().parse(level)?,
136            Prefix::Underscore => p.consume().then(Pat::Ignore),
137            Prefix::Never => p.consume().then(Pat::Never),
138            Prefix::MetId => Pat::MetId(p.consume().next()?.lexeme.to_string().as_str().into()),
139            Prefix::Constant => Pat::Value(p.parse(ExPrec::Unary.value())?),
140            Prefix::Array => parse_array_pat(p)?,
141            Prefix::Id => {
142                let At(mut path, span): At<Path> = p.parse(())?;
143                // Name or Value pattern?
144                match path.parts.len() {
145                    1 => Pat::Name(path.parts.pop().expect("name has 1 part")),
146                    _ => Pat::Value(Box::new(At(Expr::Id(path), span))),
147                }
148            }
149            Prefix::Op(op @ (PatOp::Record | PatOp::Tuple)) => Pat::Op(
150                op,
151                p.consume()
152                    .list(vec![], Prec::Tuple.next(), TKind::Comma, kind.flip())?,
153            ),
154            Prefix::Op(op @ (PatOp::Rest | PatOp::RangeEx | PatOp::RangeIn)) => {
155                // next token must continue a pattern
156                match p.consume().peek().allow_eof()? {
157                    Some(tok) if from_prefix(tok).is_ok() => Pat::Op(op, vec![p.parse(prec)?]),
158                    _ => Pat::Op(op, vec![]),
159                }
160            }
161
162            Prefix::DocOuter | Prefix::DocInner => {
163                let comment = Literal::Str(p.take_lexeme()?.string().unwrap());
164                let comment = Expr::Lit(comment).at(span);
165                Pat::Op(
166                    match op {
167                        Prefix::DocOuter => PatOp::MetaOuter,
168                        _ => PatOp::MetaInner,
169                    },
170                    vec![Pat::Value(comment.into()).at(span), p.parse(prec.next())?],
171                )
172            }
173            Prefix::Op(op @ (PatOp::MetaOuter | PatOp::MetaInner)) => Pat::Op(
174                op,
175                vec![
176                    Pat::Value(Box::new(
177                        p.consume()
178                            .expect(TKind::LBrack)?
179                            .opt(ExPrec::MIN, TKind::RBrack)?
180                            .unwrap_or_else(|| Expr::Op(Op::Tuple, vec![]).at(span)),
181                    ))
182                    .at(span),
183                    p.parse(prec)?,
184                ],
185            ),
186            Prefix::Op(op) => Pat::Op(op, vec![p.consume().parse(prec)?]),
187            Prefix::Split(op) => {
188                p.split()?;
189                Pat::Op(op, vec![p.parse(prec)?])
190            }
191        };
192
193        while let Ok(Some(tok @ &Token { kind, span: end, .. })) = p.peek().allow_eof()
194            && let Some((op, prec)) = from_infix(tok)
195            && level <= prec
196        {
197            let span = span.merge(end);
198            head = match op {
199                PatOp::RangeEx | PatOp::RangeIn => Pat::Op(
200                    op,
201                    if let Some(tok) = p.consume().peek().allow_eof()?
202                        && from_prefix(tok).is_ok()
203                    {
204                        vec![head.at(span), p.parse(prec)?]
205                    } else {
206                        vec![head.at(span)]
207                    },
208                ),
209                PatOp::Guard => {
210                    let At(cond, cspan) = p.consume().parse(ExPrec::Logical.value())?;
211                    let cond = Pat::Value(Box::new(At(cond, cspan))).at(cspan);
212                    Pat::Op(op, vec![head.at(span), cond])
213                }
214                PatOp::Generic => Pat::Op(
215                    PatOp::Generic,
216                    p.consume().list(
217                        vec![head.at(span)],
218                        Prec::Typed,
219                        TKind::Comma,
220                        kind.flip(),
221                    )?,
222                ),
223                PatOp::TypePrefixed => match prefix_level(&head, level, tok) {
224                    Some(_prec) => add_typeprefix(p, head.at(span), Prec::Typed)?,
225                    _ => break,
226                },
227                PatOp::Tuple => Pat::Op(
228                    op,
229                    p.consume()
230                        .list_bare(vec![head.at(span)], prec.next(), kind)?,
231                ),
232                PatOp::Fn => Pat::Op(op, vec![head.at(span), p.consume().parse(prec)?]),
233                _ => Pat::Op(op, vec![head.at(span), p.consume().parse(prec.next())?]),
234            }
235        }
236        Ok(head)
237    }
238}
239
240fn prefix_level(pat: &Pat, level: Prec, tok: &Token) -> Option<Prec> {
241    let (_, prec) = from_prefix(tok).ok()?;
242    match pat {
243        Pat::Name(_) | Pat::Value(_) | Pat::Op(PatOp::Generic, _) if level <= prec => Some(prec),
244        _ => None,
245    }
246}
247
248fn add_typeprefix(p: &mut Parser<'_>, name: At<Pat>, level: Prec) -> PResult<Pat> {
249    let At(mut name, span) = name;
250    name = Pat::Op(
251        PatOp::TypePrefixed,
252        vec![name.at(span.merge(p.span())), p.parse(level)?],
253    );
254    Ok(name)
255}
256
257fn parse_array_pat(p: &mut Parser<'_>) -> PResult<Pat> {
258    if p.consume().peek()?.kind == TKind::RBrack {
259        p.consume();
260        return Ok(Pat::Op(PatOp::Slice, vec![]));
261    }
262
263    let item = p.parse(Prec::Tuple)?;
264    let repeat = p.opt_if(Prec::Tuple, TKind::Semi)?;
265    p.expect(TKind::RBrack)?;
266
267    Ok(match (repeat, item) {
268        (Some(repeat), item) => Pat::Op(PatOp::ArRep, vec![item, repeat]),
269        (None, At(Pat::Op(PatOp::Tuple, items), ..)) => Pat::Op(PatOp::Slice, items),
270        (None, item) => Pat::Op(PatOp::Slice, vec![item]),
271    })
272}