1pub mod security;
5
6use ariadne::{Color, Label, Report, ReportKind, Source};
7use std::fmt;
8use std::ops::Range;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct Span {
13 pub start: usize,
14 pub end: usize,
15}
16
17impl Span {
18 pub fn new(start: usize, end: usize) -> Self {
19 Self { start, end }
20 }
21
22 pub fn range(&self) -> Range<usize> {
23 self.start..self.end
24 }
25}
26
27impl From<Range<usize>> for Span {
28 fn from(range: Range<usize>) -> Self {
29 Self {
30 start: range.start,
31 end: range.end,
32 }
33 }
34}
35
36#[derive(Debug, Clone)]
40pub enum TlError {
41 Lexer(LexerError),
42 Parser(ParserError),
43 Runtime(RuntimeError),
44 Type(TypeError),
45}
46
47#[derive(Debug, Clone)]
48pub struct TypeError {
49 pub message: String,
50 pub span: Span,
51 pub expected: Option<String>,
52 pub found: Option<String>,
53 pub hint: Option<String>,
54}
55
56#[derive(Debug, Clone)]
57pub struct LexerError {
58 pub message: String,
59 pub span: Span,
60}
61
62#[derive(Debug, Clone)]
63pub struct ParserError {
64 pub message: String,
65 pub span: Span,
66 pub hint: Option<String>,
67}
68
69#[derive(Debug, Clone)]
70pub struct RuntimeError {
71 pub message: String,
72 pub span: Option<Span>,
73 pub stack_trace: Vec<StackFrame>,
74}
75
76#[derive(Debug, Clone)]
78pub struct StackFrame {
79 pub function: String,
80 pub line: u32,
81}
82
83impl fmt::Display for TlError {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 match self {
86 TlError::Lexer(e) => write!(f, "Lexer error: {}", e.message),
87 TlError::Parser(e) => write!(f, "Parse error: {}", e.message),
88 TlError::Runtime(e) => write!(f, "Runtime error: {}", e.message),
89 TlError::Type(e) => {
90 write!(f, "Type error: {}", e.message)?;
91 if let (Some(expected), Some(found)) = (&e.expected, &e.found) {
92 write!(f, " (expected `{expected}`, found `{found}`)")?;
93 }
94 Ok(())
95 }
96 }
97 }
98}
99
100impl std::error::Error for TlError {}
101
102pub fn report_parser_error(source: &str, filename: &str, error: &ParserError) {
104 let mut builder = Report::build(ReportKind::Error, filename, error.span.start)
105 .with_message(&error.message)
106 .with_label(
107 Label::new((filename, error.span.range()))
108 .with_message(&error.message)
109 .with_color(Color::Red),
110 );
111
112 if let Some(hint) = &error.hint {
113 builder = builder.with_help(hint);
114 }
115
116 builder
117 .finish()
118 .eprint((filename, Source::from(source)))
119 .unwrap();
120}
121
122pub fn report_type_error(source: &str, filename: &str, error: &TypeError) {
124 let mut builder =
125 Report::build(ReportKind::Error, filename, error.span.start).with_message(&error.message);
126
127 let mut label_msg = error.message.clone();
128 if let (Some(expected), Some(found)) = (&error.expected, &error.found) {
129 label_msg = format!("expected `{expected}`, found `{found}`");
130 }
131
132 builder = builder.with_label(
133 Label::new((filename, error.span.range()))
134 .with_message(&label_msg)
135 .with_color(Color::Red),
136 );
137
138 if let Some(hint) = &error.hint {
139 builder = builder.with_help(hint);
140 }
141
142 builder
143 .finish()
144 .eprint((filename, Source::from(source)))
145 .unwrap();
146}
147
148pub fn report_type_warning(source: &str, filename: &str, error: &TypeError) {
150 let mut builder =
151 Report::build(ReportKind::Warning, filename, error.span.start).with_message(&error.message);
152
153 let mut label_msg = error.message.clone();
154 if let (Some(expected), Some(found)) = (&error.expected, &error.found) {
155 label_msg = format!("expected `{expected}`, found `{found}`");
156 }
157
158 builder = builder.with_label(
159 Label::new((filename, error.span.range()))
160 .with_message(&label_msg)
161 .with_color(Color::Yellow),
162 );
163
164 if let Some(hint) = &error.hint {
165 builder = builder.with_help(hint);
166 }
167
168 builder
169 .finish()
170 .eprint((filename, Source::from(source)))
171 .unwrap();
172}
173
174pub fn report_runtime_error(source: &str, filename: &str, error: &RuntimeError) {
176 if let Some(span) = &error.span {
178 Report::build(ReportKind::Error, filename, span.start)
179 .with_message(&error.message)
180 .with_label(
181 Label::new((filename, span.range()))
182 .with_message(&error.message)
183 .with_color(Color::Red),
184 )
185 .finish()
186 .eprint((filename, Source::from(source)))
187 .unwrap();
188 } else if !error.stack_trace.is_empty() && error.stack_trace[0].line > 0 {
189 let line = error.stack_trace[0].line as usize;
191 let lines: Vec<&str> = source.lines().collect();
192 if line > 0 && line <= lines.len() {
193 let mut offset = 0;
194 for l in &lines[..line - 1] {
195 offset += l.len() + 1; }
197 let line_len = lines[line - 1].len().max(1);
198 let span = Span::new(offset, offset + line_len);
199 Report::build(ReportKind::Error, filename, span.start)
200 .with_message(&error.message)
201 .with_label(
202 Label::new((filename, span.range()))
203 .with_message(&error.message)
204 .with_color(Color::Red),
205 )
206 .finish()
207 .eprint((filename, Source::from(source)))
208 .unwrap();
209 } else {
210 eprintln!("Runtime error (line {}): {}", line, error.message);
211 }
212 } else {
213 eprintln!("Runtime error: {}", error.message);
214 }
215 if error.stack_trace.len() > 1
217 || (error.stack_trace.len() == 1 && error.stack_trace[0].function != "<main>")
218 {
219 eprintln!("Stack trace:");
220 for frame in &error.stack_trace {
221 if frame.line > 0 {
222 eprintln!(" at {} (line {})", frame.function, frame.line);
223 } else {
224 eprintln!(" at {}", frame.function);
225 }
226 }
227 }
228}