1use super::{PResult, PResultExt, Parse, ParseError, Parser, expr::Prec as ExPrec};
2use cl_ast::{types::Path, *};
3use cl_token::{TKind, Token};
4
5#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
10pub enum Prec {
11 #[default]
13 Min,
14 Alt,
16 Tuple,
18 Typed,
20 Fn,
22 Range,
24 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 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,
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
97fn 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 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 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 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}