Convert a chumsky Simple error to our internal Error type
(source: &str, error: &Simple<'_, char>, source_id: u16)
| 19 | |
| 20 | /// Convert a chumsky Simple error to our internal Error type |
| 21 | fn convert_lexer_error(source: &str, error: &Simple<'_, char>, source_id: u16) -> E { |
| 22 | // Get span information from the Simple error |
| 23 | // NOTE: When parsing &str, SimpleSpan uses BYTE offsets, not character offsets! |
| 24 | // We need to convert byte offsets to character offsets for compatibility with our error reporting. |
| 25 | let byte_span = error.span(); |
| 26 | let byte_start = byte_span.start(); |
| 27 | let byte_end = byte_span.end(); |
| 28 | |
| 29 | // Convert byte offsets to character offsets |
| 30 | let char_start = source[..byte_start].chars().count(); |
| 31 | let char_end = source[..byte_end].chars().count(); |
| 32 | |
| 33 | // Extract the "found" text using character-based slicing |
| 34 | let found: String = source |
| 35 | .chars() |
| 36 | .skip(char_start) |
| 37 | .take(char_end - char_start) |
| 38 | .collect(); |
| 39 | |
| 40 | // If found is empty, report as "end of input", otherwise wrap in quotes |
| 41 | let found_display = if found.is_empty() { |
| 42 | "end of input".to_string() |
| 43 | } else { |
| 44 | format!("'{}'", found) |
| 45 | }; |
| 46 | |
| 47 | // Create a new Error with the extracted information |
| 48 | let error_source = format!( |
| 49 | "Unexpected {} at position {}..{}", |
| 50 | found_display, char_start, char_end |
| 51 | ); |
| 52 | |
| 53 | WithErrorInfo::with_span( |
| 54 | Error::new(Reason::Unexpected { |
| 55 | found: found_display, |
| 56 | }), |
| 57 | Some(crate::span::Span { |
| 58 | start: char_start, |
| 59 | end: char_end, |
| 60 | source_id, |
| 61 | }), |
| 62 | ) |
| 63 | .with_source(ErrorSource::Lexer(error_source)) |
| 64 | } |
| 65 | |
| 66 | /// Lex PRQL into LR, returning both the LR and any errors encountered |
| 67 | pub fn lex_source_recovery(source: &str, source_id: u16) -> (Option<Vec<Token>>, Vec<E>) { |