1use super::{Column, LineNumber};
2use std::sync::Arc;
3
4#[derive(Clone)]
5pub struct Error {
6 code: u16,
7 line_number: LineNumber,
8 column: Column,
9 message: Arc<str>,
10}
11
12#[doc(hidden)]
13#[macro_export]
14macro_rules! error {
15 ($err:ident) => {
16 $crate::lang::Error::new($crate::lang::ErrorCode::$err)
17 };
18 ($err:ident, ..$col:expr) => {
19 $crate::lang::Error::new($crate::lang::ErrorCode::$err).in_column($col)
20 };
21 ($err:ident, $line:expr) => {
22 $crate::lang::Error::new($crate::lang::ErrorCode::$err).in_line_number($line)
23 };
24 ($err:ident; $msg:expr) => {
25 $crate::lang::Error::new($crate::lang::ErrorCode::$err).message($msg)
26 };
27 ($err:ident, ..$col:expr; $msg:expr) => {
28 $crate::lang::Error::new($crate::lang::ErrorCode::$err)
29 .in_column($col)
30 .message($msg)
31 };
32 ($err:ident, $line:expr, ..$col:expr) => {
33 $crate::lang::Error::new($crate::lang::ErrorCode::$err)
34 .in_line_number($line)
35 .in_column($col)
36 };
37 ($err:ident, $line:expr; $msg:expr) => {
38 $crate::lang::Error::new($crate::lang::ErrorCode::$err)
39 .in_line_number($line)
40 .message($msg)
41 };
42 ($err:ident, $line:expr, ..$col:expr; $msg:expr) => {
43 $crate::lang::Error::new($crate::lang::ErrorCode::$err)
44 .in_line_number($line)
45 .in_column($col)
46 .message($msg)
47 };
48}
49
50impl Error {
51 pub fn new(code: ErrorCode) -> Error {
52 Error {
53 code: code as u16,
54 line_number: None,
55 column: 0..0,
56 message: "".into(),
57 }
58 }
59
60 pub fn is_direct(&self) -> bool {
61 self.line_number.is_none()
62 }
63
64 pub fn line_number(&self) -> LineNumber {
65 self.line_number
66 }
67
68 pub fn in_line_number(&self, line: LineNumber) -> Error {
69 debug_assert!(self.line_number.is_none());
70 Error {
71 code: self.code,
72 line_number: line,
73 column: self.column.clone(),
74 message: self.message.clone(),
75 }
76 }
77
78 pub fn column(&self) -> Column {
79 match self.line_number {
80 Some(num) => {
81 let offset = num.to_string().len() + 1;
82 (self.column.start + offset)..(self.column.end + offset)
83 }
84 None => self.column.clone(),
85 }
86 }
87
88 pub fn in_column(&self, column: &Column) -> Error {
89 debug_assert_eq!(self.column, 0..0);
90 Error {
91 code: self.code,
92 line_number: self.line_number,
93 column: column.clone(),
94 message: self.message.clone(),
95 }
96 }
97
98 pub fn message(&self, message: &str) -> Error {
99 debug_assert_eq!(self.message.len(), 0);
100 Error {
101 code: self.code,
102 line_number: self.line_number,
103 column: self.column.clone(),
104 message: message.into(),
105 }
106 }
107}
108
109pub enum ErrorCode {
110 Break = 0,
111 NextWithoutFor = 1,
112 SyntaxError = 2,
113 ReturnWithoutGosub = 3,
114 OutOfData = 4,
115 IllegalFunctionCall = 5,
116 Overflow = 6,
117 OutOfMemory = 7,
118 UndefinedLine = 8,
119 SubscriptOutOfRange = 9,
120 RedimensionedArray = 10,
121 DivisionByZero = 11,
122 IllegalDirect = 12,
123 TypeMismatch = 13,
124 OutOfStringSpace = 14,
125 StringTooLong = 15,
126 CantContinue = 17,
127 UndefinedUserFunction = 18,
128 RedoFromStart = 21,
129 LineBufferOverflow = 23,
130 ForWithoutNext = 26,
131 WhileWithoutWend = 29,
132 WendWithoutWhile = 30,
133 InternalError = 51,
134 FileNotFound = 53,
135 FileAlreadyExists = 58,
136 BadFileName = 64,
137 DirectStatementInFile = 66,
138}
139
140impl std::fmt::Debug for Error {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 write!(f, "Error {{ {} }}", self.to_string())
143 }
144}
145
146impl std::fmt::Display for Error {
147 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
148 let code_str = match self.code {
149 0 => "BREAK",
150 1 => "NEXT WITHOUT FOR",
151 2 => "SYNTAX ERROR",
152 3 => "RETURN WITHOUT GOSUB",
153 4 => "OUT OF DATA",
154 5 => "ILLEGAL FUNCTION CALL",
155 6 => "OVERFLOW",
156 7 => "OUT OF MEMORY",
157 8 => "UNDEFINED LINE",
158 9 => "SUBSCRIPT OUT OF RANGE",
159 10 => "REDIMENSIONED ARRAY",
160 11 => "DIVISION BY ZERO",
161 12 => "ILLEGAL DIRECT",
162 13 => "TYPE MISMATCH",
163 14 => "OUT OF STRING SPACE",
164 15 => "STRING TOO LONG",
165 16 => "STRING FORMULA TOO COMPLEX",
166 17 => "CAN'T CONTINUE",
167 18 => "UNDEFINED USER FUNCTION",
168 19 => "NO RESUME",
169 20 => "RESUME WITHOUT ERROR",
170 21 => "REDO FROM START",
171 22 => "MISSING OPERAND",
172 23 => "LINE BUFFER OVERFLOW",
173 26 => "FOR WITHOUT NEXT",
174 29 => "WHILE WITHOUT WEND",
175 30 => "WEND WITHOUT WHILE",
176 50 => "FIELD OVERFLOW",
177 51 => "INTERNAL ERROR",
178 52 => "BAD FILE NUMBER",
179 53 => "FILE NOT FOUND",
180 54 => "BAD FILE MODE",
181 55 => "FILE ALREADY OPEN",
182 56 => "DISK NOT MOUNTED",
183 57 => "DISK I/O ERROR",
184 58 => "FILE ALREADY EXISTS",
185 59 => "SET TO NON-DISK STRING",
186 60 => "DISK ALREADY MOUNTED",
187 61 => "DISK FULL",
188 62 => "INPUT PAST END",
189 63 => "BAD RECORD NUMBER",
190 64 => "BAD FILE NAME",
191 65 => "MODE-MISMATCH",
192 66 => "DIRECT STATEMENT IN FILE",
193 67 => "TOO MANY FILES",
194 68 => "OUT OF RANDOM BLOCKS",
195 _ => "",
196 };
197 let mut suffix = String::new();
198 if let Some(line_number) = self.line_number {
199 suffix.push_str(&format!(" {}", line_number));
200 if (0..0) != self.column {
201 suffix.push_str(&format!(":{}", self.column().start + 1));
202 }
203 }
204 if !suffix.is_empty() {
205 suffix.insert_str(0, " IN");
206 }
207 if !self.message.is_empty() {
208 suffix.push_str(&format!("; {}", self.message));
209 }
210 if code_str.is_empty() {
211 write!(f, "?PROGRAM ERROR {}{}", self.code, suffix)
212 } else {
213 write!(f, "?{}{}", code_str, suffix)
214 }
215 }
216}