Parses interpolated strings
(string: String, span_base: Span)
| 7 | |
| 8 | /// Parses interpolated strings |
| 9 | pub(crate) fn parse(string: String, span_base: Span) -> Result<Vec<InterpolateItem>, Vec<Error>> { |
| 10 | let res = interpolated_parser().parse(string.as_str()); |
| 11 | |
| 12 | let (output, errors) = res.into_output_errors(); |
| 13 | |
| 14 | if !errors.is_empty() { |
| 15 | return Err(errors |
| 16 | .into_iter() |
| 17 | .map(|e| { |
| 18 | // Adjust span to be relative to span_base |
| 19 | let span = Span { |
| 20 | start: span_base.start + e.span().start, |
| 21 | end: span_base.start + e.span().end, |
| 22 | source_id: span_base.source_id, |
| 23 | }; |
| 24 | |
| 25 | // Convert Rich error to our Error format |
| 26 | // Custom error formatting for consistent user experience across all PRQL errors. |
| 27 | // Chumsky's default format varies between versions and doesn't match our |
| 28 | // "{label} expected {X}, but found {Y}" pattern used elsewhere. |
| 29 | let message = { |
| 30 | // Get the label from contexts (most specific one) |
| 31 | let label = e.contexts().last().map(|(pat, _)| pat.to_string()); |
| 32 | |
| 33 | // Build expected list |
| 34 | let expected: Vec<_> = e.expected().map(|e| format!("{e}")).collect(); |
| 35 | let expected_str = match expected.len() { |
| 36 | 0 => String::new(), |
| 37 | 1 => expected[0].clone(), |
| 38 | 2 => format!("{} or {}", expected[0], expected[1]), |
| 39 | _ => format!( |
| 40 | "{}, or {}", |
| 41 | expected[..expected.len() - 1].join(", "), |
| 42 | expected.last().unwrap() |
| 43 | ), |
| 44 | }; |
| 45 | |
| 46 | // Format the found token consistently: quote actual tokens, but not "end of input" |
| 47 | let found = if let Some(f) = e.found() { |
| 48 | format!("\"{}\"", f) |
| 49 | } else { |
| 50 | "end of input".to_string() |
| 51 | }; |
| 52 | |
| 53 | if let Some(label) = label { |
| 54 | if expected_str.is_empty() { |
| 55 | format!("unexpected {found}") |
| 56 | } else { |
| 57 | format!("{label} expected {expected_str}, but found {found}") |
| 58 | } |
| 59 | } else if expected_str.is_empty() { |
| 60 | format!("unexpected {found}") |
| 61 | } else { |
| 62 | format!("expected {expected_str}, but found {found}") |
| 63 | } |
| 64 | }; |
| 65 | |
| 66 | WithErrorInfo::with_span(Error::new_simple(message), Some(span)) |