telltale_language/compiler/parser/
error.rs1use thiserror::Error;
7
8#[derive(Debug, Clone)]
10pub struct ErrorSpan {
11 pub line: usize,
12 pub column: usize,
13 pub line_end: usize,
14 pub column_end: usize,
15 pub snippet: String,
16}
17
18impl ErrorSpan {
19 pub fn from_pest_span(span: pest::Span, input: &str) -> Self {
21 let (line, column) = span.start_pos().line_col();
22 let (line_end, column_end) = span.end_pos().line_col();
23
24 let snippet = input.lines().nth(line - 1).unwrap_or("").to_string();
26
27 Self {
28 line,
29 column,
30 line_end,
31 column_end,
32 snippet,
33 }
34 }
35
36 pub fn from_line_col(line: usize, column: usize, input: &str) -> Self {
38 let snippet = input
39 .lines()
40 .nth(line.saturating_sub(1))
41 .unwrap_or("")
42 .to_string();
43 Self {
44 line,
45 column,
46 line_end: line,
47 column_end: column + 1,
48 snippet,
49 }
50 }
51
52 #[must_use]
54 pub fn format_error(&self, message: &str) -> String {
55 let line_num_width = self.line.to_string().len().max(3);
56 let mut output = String::new();
57
58 output.push_str(&format!("\n{message}\n"));
60
61 output.push_str(&format!(
63 " {} {}:{}:{}\n",
64 "-->", "input", self.line, self.column
65 ));
66
67 output.push_str(&format!("{:width$} |\n", " ", width = line_num_width));
69
70 output.push_str(&format!(
72 "{:>width$} | {}\n",
73 self.line,
74 self.snippet,
75 width = line_num_width
76 ));
77
78 let spaces = " ".repeat(line_num_width + 3 + self.column - 1);
80 let underline_len = if self.line == self.line_end {
81 (self.column_end - self.column).max(1)
82 } else {
83 self.snippet.len() - self.column + 1
84 };
85 let underline = "^".repeat(underline_len);
86 output.push_str(&format!("{spaces}{underline}\n"));
87
88 output
89 }
90}
91
92use super::Rule;
93
94#[derive(Error, Debug)]
96pub enum ParseError {
97 #[error("{}", format_pest_error(.0))]
98 Pest(#[from] Box<pest::error::Error<Rule>>),
99
100 #[error("{}", .span.format_error(&format!("Layout error: {}", .message)))]
101 Layout { span: ErrorSpan, message: String },
102
103 #[error("{}", .span.format_error(&format!("Syntax error: {}", .message)))]
104 Syntax { span: ErrorSpan, message: String },
105
106 #[error("{}", .span.format_error(&format!("Undefined role '{}'", .role)))]
107 UndefinedRole { role: String, span: ErrorSpan },
108
109 #[error("{}", .span.format_error(&format!("Duplicate role declaration '{}'", .role)))]
110 DuplicateRole { role: String, span: ErrorSpan },
111
112 #[error("Empty choreography: no statements found")]
113 EmptyChoreography,
114
115 #[error("{}", .span.format_error(&format!("Invalid message format: {}", .message)))]
116 InvalidMessage { message: String, span: ErrorSpan },
117
118 #[error("{}", .span.format_error(&format!("Invalid condition: {}", .message)))]
119 InvalidCondition { message: String, span: ErrorSpan },
120
121 #[error("{}", .span.format_error(&format!("Undefined protocol '{}'", .protocol)))]
122 UndefinedProtocol { protocol: String, span: ErrorSpan },
123
124 #[error("{}", .span.format_error(&format!("Duplicate protocol definition '{}'", .protocol)))]
125 DuplicateProtocol { protocol: String, span: ErrorSpan },
126
127 #[error("Grammar composition failed: {0}")]
128 GrammarComposition(#[from] crate::compiler::grammar::GrammarCompositionError),
129}
130
131fn format_pest_error(err: &pest::error::Error<Rule>) -> String {
133 format!("\nParse error:\n{err}")
134}