1use 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
15pub enum Prec {
16 #[default]
18 Min,
19 Alt,
21 Tuple,
23 Typed,
25 Fn,
27 Range,
29 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 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,
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
108fn 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 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 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}