lex_core/lex/ast/
error.rs1use crate::lex::ast::range::Range;
4use std::fmt;
5
6#[cfg(test)]
7use crate::lex::ast::range::Position;
8
9#[derive(Debug, Clone)]
11pub enum PositionLookupError {
12 InvalidPositionFormat(String),
14 NotFound { line: usize, column: usize },
16}
17
18impl fmt::Display for PositionLookupError {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 match self {
21 PositionLookupError::InvalidPositionFormat(msg) => {
22 write!(f, "Invalid position format: {msg}")
23 }
24 PositionLookupError::NotFound { line, column } => {
25 write!(f, "No element found at position {line}:{column}")
26 }
27 }
28 }
29}
30
31impl std::error::Error for PositionLookupError {}
32
33#[derive(Debug, Clone)]
35pub enum ParserError {
36 InvalidNesting {
38 container: String,
39 invalid_child: String,
40 invalid_child_text: String,
41 location: Range,
42 source_context: String,
43 },
44}
45
46impl fmt::Display for ParserError {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 match self {
49 ParserError::InvalidNesting {
50 container,
51 invalid_child,
52 invalid_child_text,
53 location: _,
54 source_context,
55 } => {
56 writeln!(f, "Error: Invalid nesting in {container}")?;
57 writeln!(f)?;
58 writeln!(f, "{container} cannot contain {invalid_child} elements")?;
59 writeln!(f)?;
60 write!(f, "{source_context}")?;
61 writeln!(f)?;
62 writeln!(
63 f,
64 "The parser identified a {} (\"{}\") inside a {}, which violates lex grammar rules.",
65 invalid_child, invalid_child_text.trim(), container
66 )
67 }
68 }
69 }
70}
71
72impl std::error::Error for ParserError {}
73
74pub type ParserResult<T> = Result<T, Box<ParserError>>;
76
77pub fn format_source_context(source: &str, range: &Range) -> String {
82 let lines: Vec<&str> = source.lines().collect();
83 let error_line = range.start.line;
84
85 let start_line = error_line.saturating_sub(2);
86 let end_line = (error_line + 3).min(lines.len());
87
88 let mut context = String::new();
89
90 for line_num in start_line..end_line {
91 let marker = if line_num == error_line { ">>" } else { " " };
92 let display_line_num = line_num + 1; if line_num < lines.len() {
95 context.push_str(&format!(
96 "{} {:3} | {}\n",
97 marker, display_line_num, lines[line_num]
98 ));
99 }
100 }
101
102 context
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_format_source_context() {
111 let source = "line 1\nline 2\nline 3\nerror line\nline 5\nline 6\nline 7";
112 let range = Range::new(20..30, Position::new(3, 0), Position::new(3, 10));
113
114 let context = format_source_context(source, &range);
115
116 assert!(context.contains("line 2"));
118 assert!(context.contains(">> "));
119 assert!(context.contains("error line"));
120 assert!(context.contains("line 5"));
121 }
122}