basic/lang/
error.rs

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}