| 1 | use nom::branch::alt; |
| 2 | use nom::bytes::complete::tag; |
| 3 | use nom::character::complete::{char, one_of}; |
| 4 | use nom::combinator::{consumed, map, map_res, opt}; |
| 5 | use nom::multi::separated_list1; |
| 6 | use nom::sequence::{pair, preceded, tuple}; |
| 7 | |
| 8 | use crate::{ |
| 9 | CharLit, ErrorContext, Num, ParseErr, ParseResult, PathOrIdentifier, State, StrLit, WithSpan, |
| 10 | bool_lit, char_lit, identifier, keyword, num_lit, path_or_identifier, str_lit, ws, |
| 11 | }; |
| 12 | |
| 13 | #[derive (Clone, Debug, PartialEq)] |
| 14 | pub enum Target<'a> { |
| 15 | Name(&'a str), |
| 16 | Tuple(Vec<&'a str>, Vec<Target<'a>>), |
| 17 | Array(Vec<&'a str>, Vec<Target<'a>>), |
| 18 | Struct(Vec<&'a str>, Vec<(&'a str, Target<'a>)>), |
| 19 | NumLit(&'a str, Num<'a>), |
| 20 | StrLit(StrLit<'a>), |
| 21 | CharLit(CharLit<'a>), |
| 22 | BoolLit(&'a str), |
| 23 | Path(Vec<&'a str>), |
| 24 | OrChain(Vec<Target<'a>>), |
| 25 | Placeholder(&'a str), |
| 26 | /// The `Option` is the variable name (if any) in `var_name @ ..`. |
| 27 | Rest(WithSpan<'a, Option<&'a str>>), |
| 28 | } |
| 29 | |
| 30 | impl<'a> Target<'a> { |
| 31 | /// Parses multiple targets with `or` separating them |
| 32 | pub(super) fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
| 33 | map( |
| 34 | separated_list1(ws(tag("or" )), |i| s.nest(i, |i| Self::parse_one(i, s))), |
| 35 | |mut opts| match opts.len() { |
| 36 | 1 => opts.pop().unwrap(), |
| 37 | _ => Self::OrChain(opts), |
| 38 | }, |
| 39 | )(i) |
| 40 | } |
| 41 | |
| 42 | /// Parses a single target without an `or`, unless it is wrapped in parentheses. |
| 43 | fn parse_one(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
| 44 | let mut opt_opening_paren = map(opt(ws(char('(' ))), |o| o.is_some()); |
| 45 | let mut opt_opening_brace = map(opt(ws(char('{' ))), |o| o.is_some()); |
| 46 | let mut opt_opening_bracket = map(opt(ws(char('[' ))), |o| o.is_some()); |
| 47 | |
| 48 | let (i, lit) = opt(Self::lit)(i)?; |
| 49 | if let Some(lit) = lit { |
| 50 | return Ok((i, lit)); |
| 51 | } |
| 52 | |
| 53 | // match tuples and unused parentheses |
| 54 | let (i, target_is_tuple) = opt_opening_paren(i)?; |
| 55 | if target_is_tuple { |
| 56 | let (i, (singleton, mut targets)) = collect_targets(i, s, ')' , Self::unnamed)?; |
| 57 | if singleton { |
| 58 | return Ok((i, targets.pop().unwrap())); |
| 59 | } |
| 60 | return Ok(( |
| 61 | i, |
| 62 | Self::Tuple(Vec::new(), only_one_rest_pattern(targets, false, "tuple" )?), |
| 63 | )); |
| 64 | } |
| 65 | let (i, target_is_array) = opt_opening_bracket(i)?; |
| 66 | if target_is_array { |
| 67 | let (i, (singleton, mut targets)) = collect_targets(i, s, ']' , Self::unnamed)?; |
| 68 | if singleton { |
| 69 | return Ok((i, targets.pop().unwrap())); |
| 70 | } |
| 71 | return Ok(( |
| 72 | i, |
| 73 | Self::Array(Vec::new(), only_one_rest_pattern(targets, true, "array" )?), |
| 74 | )); |
| 75 | } |
| 76 | |
| 77 | let path = |i| { |
| 78 | map_res(path_or_identifier, |v| match v { |
| 79 | PathOrIdentifier::Path(v) => Ok(v), |
| 80 | PathOrIdentifier::Identifier(v) => Err(v), |
| 81 | })(i) |
| 82 | }; |
| 83 | |
| 84 | // match structs |
| 85 | let (i, path) = opt(path)(i)?; |
| 86 | if let Some(path) = path { |
| 87 | let i_before_matching_with = i; |
| 88 | let (i, _) = opt(ws(keyword("with" )))(i)?; |
| 89 | |
| 90 | let (i, is_unnamed_struct) = opt_opening_paren(i)?; |
| 91 | if is_unnamed_struct { |
| 92 | let (i, (_, targets)) = collect_targets(i, s, ')' , Self::unnamed)?; |
| 93 | return Ok(( |
| 94 | i, |
| 95 | Self::Tuple(path, only_one_rest_pattern(targets, false, "struct" )?), |
| 96 | )); |
| 97 | } |
| 98 | |
| 99 | let (i, is_named_struct) = opt_opening_brace(i)?; |
| 100 | if is_named_struct { |
| 101 | let (i, (_, targets)) = collect_targets(i, s, '}' , Self::named)?; |
| 102 | return Ok((i, Self::Struct(path, targets))); |
| 103 | } |
| 104 | |
| 105 | return Ok((i_before_matching_with, Self::Path(path))); |
| 106 | } |
| 107 | |
| 108 | // neither literal nor struct nor path |
| 109 | let (new_i, name) = identifier(i)?; |
| 110 | let target = match name { |
| 111 | "_" => Self::Placeholder(name), |
| 112 | _ => verify_name(i, name)?, |
| 113 | }; |
| 114 | Ok((new_i, target)) |
| 115 | } |
| 116 | |
| 117 | fn lit(i: &'a str) -> ParseResult<'a, Self> { |
| 118 | alt(( |
| 119 | map(str_lit, Self::StrLit), |
| 120 | map(char_lit, Self::CharLit), |
| 121 | map(consumed(num_lit), |(full, num)| Target::NumLit(full, num)), |
| 122 | map(bool_lit, Self::BoolLit), |
| 123 | ))(i) |
| 124 | } |
| 125 | |
| 126 | fn unnamed(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
| 127 | alt((Self::rest, |i| Self::parse(i, s)))(i) |
| 128 | } |
| 129 | |
| 130 | fn named(init_i: &'a str, s: &State<'_>) -> ParseResult<'a, (&'a str, Self)> { |
| 131 | let (i, rest) = opt(consumed(Self::rest))(init_i)?; |
| 132 | if let Some(rest) = rest { |
| 133 | let (_, chr) = ws(opt(one_of(",:" )))(i)?; |
| 134 | if let Some(chr) = chr { |
| 135 | return Err(nom::Err::Failure(ErrorContext::new( |
| 136 | format!( |
| 137 | "unexpected ` {chr}` character after `..` \n\ |
| 138 | note that in a named struct, `..` must come last to ignore other members" |
| 139 | ), |
| 140 | i, |
| 141 | ))); |
| 142 | } |
| 143 | if let Target::Rest(ref s) = rest.1 { |
| 144 | if s.inner.is_some() { |
| 145 | return Err(nom::Err::Failure(ErrorContext::new( |
| 146 | "`@ ..` cannot be used in struct" , |
| 147 | s.span, |
| 148 | ))); |
| 149 | } |
| 150 | } |
| 151 | return Ok((i, rest)); |
| 152 | } |
| 153 | |
| 154 | let (i, (src, target)) = pair( |
| 155 | identifier, |
| 156 | opt(preceded(ws(char(':' )), |i| Self::parse(i, s))), |
| 157 | )(init_i)?; |
| 158 | |
| 159 | if src == "_" { |
| 160 | return Err(nom::Err::Failure(ErrorContext::new( |
| 161 | "cannot use placeholder `_` as source in named struct" , |
| 162 | init_i, |
| 163 | ))); |
| 164 | } |
| 165 | |
| 166 | let target = match target { |
| 167 | Some(target) => target, |
| 168 | None => verify_name(init_i, src)?, |
| 169 | }; |
| 170 | Ok((i, (src, target))) |
| 171 | } |
| 172 | |
| 173 | fn rest(start: &'a str) -> ParseResult<'a, Self> { |
| 174 | let (i, (ident, _)) = tuple((opt(tuple((identifier, ws(char('@' ))))), tag(".." )))(start)?; |
| 175 | Ok(( |
| 176 | i, |
| 177 | Self::Rest(WithSpan::new(ident.map(|(ident, _)| ident), start)), |
| 178 | )) |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | fn verify_name<'a>( |
| 183 | input: &'a str, |
| 184 | name: &'a str, |
| 185 | ) -> Result<Target<'a>, nom::Err<ErrorContext<'a>>> { |
| 186 | match name { |
| 187 | "self" | "writer" => Err(nom::Err::Failure(ErrorContext::new( |
| 188 | message:format!("cannot use ` {name}` as a name" ), |
| 189 | input, |
| 190 | ))), |
| 191 | _ => Ok(Target::Name(name)), |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | fn collect_targets<'a, T>( |
| 196 | i: &'a str, |
| 197 | s: &State<'_>, |
| 198 | delim: char, |
| 199 | mut one: impl FnMut(&'a str, &State<'_>) -> ParseResult<'a, T>, |
| 200 | ) -> ParseResult<'a, (bool, Vec<T>)> { |
| 201 | let opt_comma = |i| map(ws(opt(char(',' ))), |o| o.is_some())(i); |
| 202 | let opt_end = |i| map(ws(opt(char(delim))), |o| o.is_some())(i); |
| 203 | |
| 204 | let (i, has_end) = opt_end(i)?; |
| 205 | if has_end { |
| 206 | return Ok((i, (false, Vec::new()))); |
| 207 | } |
| 208 | |
| 209 | let (i, targets) = opt(separated_list1(ws(char(',' )), |i| one(i, s)))(i)?; |
| 210 | let Some(targets) = targets else { |
| 211 | return Err(nom::Err::Failure(ErrorContext::new( |
| 212 | "expected comma separated list of members" , |
| 213 | i, |
| 214 | ))); |
| 215 | }; |
| 216 | |
| 217 | let (i, (has_comma, has_end)) = pair(opt_comma, opt_end)(i)?; |
| 218 | if !has_end { |
| 219 | let msg = match has_comma { |
| 220 | true => format!("expected member, or ` {delim}` as terminator" ), |
| 221 | false => format!("expected `,` for more members, or ` {delim}` as terminator" ), |
| 222 | }; |
| 223 | return Err(nom::Err::Failure(ErrorContext::new(msg, i))); |
| 224 | } |
| 225 | |
| 226 | let singleton = !has_comma && targets.len() == 1; |
| 227 | Ok((i, (singleton, targets))) |
| 228 | } |
| 229 | |
| 230 | fn only_one_rest_pattern<'a>( |
| 231 | targets: Vec<Target<'a>>, |
| 232 | allow_named_rest: bool, |
| 233 | type_kind: &str, |
| 234 | ) -> Result<Vec<Target<'a>>, ParseErr<'a>> { |
| 235 | let mut found_rest: bool = false; |
| 236 | |
| 237 | for target: &Target<'a> in &targets { |
| 238 | if let Target::Rest(s: &WithSpan<'_, Option<&str>>) = target { |
| 239 | if !allow_named_rest && s.inner.is_some() { |
| 240 | return Err(nom::Err::Failure(ErrorContext::new( |
| 241 | message:"`@ ..` is only allowed in slice patterns" , |
| 242 | input:s.span, |
| 243 | ))); |
| 244 | } |
| 245 | if found_rest { |
| 246 | return Err(nom::Err::Failure(ErrorContext::new( |
| 247 | message:format!("`..` can only be used once per {type_kind} pattern" ), |
| 248 | input:s.span, |
| 249 | ))); |
| 250 | } |
| 251 | found_rest = true; |
| 252 | } |
| 253 | } |
| 254 | Ok(targets) |
| 255 | } |
| 256 | |