Skip to main content

patch_rexx/
error.rs

1//! REXX error types and error message formatting.
2//!
3//! REXX defines specific error numbers (e.g., Error 41 = Bad arithmetic conversion).
4//! This module maps Rust error handling to REXX's error numbering system
5//! while providing modern, helpful diagnostics.
6
7use std::fmt;
8
9/// Source location for error reporting.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct SourceLoc {
12    pub line: usize,
13    pub col: usize,
14    /// Original source line text for display.
15    pub source_line: Option<String>,
16}
17
18impl SourceLoc {
19    pub fn new(line: usize, col: usize) -> Self {
20        Self {
21            line,
22            col,
23            source_line: None,
24        }
25    }
26
27    pub fn with_source(mut self, text: String) -> Self {
28        self.source_line = Some(text);
29        self
30    }
31}
32
33/// REXX error numbers per ANSI X3.274-1996 §A.
34/// Not all are used initially but the numbering must be correct.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum RexxError {
37    /// 4 — Program interrupted (HALT condition)
38    Halt,
39    /// 5 — System resources exhausted
40    ResourceExhausted,
41    /// 6 — Unmatched /*
42    UnmatchedComment,
43    /// 7 — WHEN or OTHERWISE expected
44    ExpectedWhenOtherwise,
45    /// 8 — Unexpected THEN or ELSE
46    UnexpectedThenElse,
47    /// 9 — Unexpected WHEN or OTHERWISE
48    UnexpectedWhenOtherwise,
49    /// 10 — Unexpected or unmatched END
50    UnexpectedEnd,
51    /// 13 — Invalid character in program
52    InvalidCharacter,
53    /// 14 — Incomplete DO/SELECT/IF
54    IncompleteBlock,
55    /// 15 — Invalid hexadecimal or binary string
56    InvalidHexBinary,
57    /// 16 — Label not found (SIGNAL target)
58    LabelNotFound,
59    /// 17 — Unexpected PROCEDURE
60    UnexpectedProcedure,
61    /// 18 — THEN expected
62    ExpectedThen,
63    /// 19 — String or symbol expected
64    ExpectedStringOrSymbol,
65    /// 20 — Symbol expected
66    ExpectedSymbol,
67    /// 21 — Invalid data on end of clause
68    InvalidDataOnEnd,
69    /// 24 — Invalid TRACE request
70    InvalidTrace,
71    /// 25 — Invalid sub-keyword found
72    InvalidSubKeyword,
73    /// 26 — Invalid whole number
74    InvalidWholeNumber,
75    /// 27 — Invalid DO syntax
76    InvalidDoSyntax,
77    /// 28 — Invalid LEAVE or ITERATE
78    InvalidLeaveIterate,
79    /// 29 — Environment name too long
80    EnvironmentNameTooLong,
81    /// 30 — Name or string too long
82    NameTooLong,
83    /// 31 — Name starts with number or "."
84    InvalidName,
85    /// 33 — Invalid expression result
86    InvalidExpressionResult,
87    /// 34 — Logical value not 0 or 1
88    InvalidLogicalValue,
89    /// 35 — Invalid expression
90    InvalidExpression,
91    /// 36 — Unmatched "(" in expression
92    UnmatchedParen,
93    /// 37 — Unexpected "," or ")"
94    UnexpectedCommaOrParen,
95    /// 38 — Invalid template or pattern
96    InvalidTemplate,
97    /// 40 — Incorrect call to routine
98    IncorrectCall,
99    /// 41 — Bad arithmetic conversion
100    BadArithmetic,
101    /// 42 — Arithmetic overflow/underflow
102    ArithmeticOverflow,
103    /// 43 — Routine not found
104    RoutineNotFound,
105    /// 44 — Function did not return data
106    NoReturnData,
107    /// 45 — No data specified on function RETURN
108    NoReturnValue,
109    /// 46 — Invalid variable reference
110    InvalidVariableRef,
111    /// 48 — Failure in system service
112    SystemFailure,
113    /// 49 — Interpretation error (INTERPRET issues)
114    InterpretationError,
115}
116
117impl RexxError {
118    /// The REXX error number per the ANSI spec.
119    pub fn number(self) -> u32 {
120        match self {
121            Self::Halt => 4,
122            Self::ResourceExhausted => 5,
123            Self::UnmatchedComment => 6,
124            Self::ExpectedWhenOtherwise => 7,
125            Self::UnexpectedThenElse => 8,
126            Self::UnexpectedWhenOtherwise => 9,
127            Self::UnexpectedEnd => 10,
128            Self::InvalidCharacter => 13,
129            Self::IncompleteBlock => 14,
130            Self::InvalidHexBinary => 15,
131            Self::LabelNotFound => 16,
132            Self::UnexpectedProcedure => 17,
133            Self::ExpectedThen => 18,
134            Self::ExpectedStringOrSymbol => 19,
135            Self::ExpectedSymbol => 20,
136            Self::InvalidDataOnEnd => 21,
137            Self::InvalidTrace => 24,
138            Self::InvalidSubKeyword => 25,
139            Self::InvalidWholeNumber => 26,
140            Self::InvalidDoSyntax => 27,
141            Self::InvalidLeaveIterate => 28,
142            Self::EnvironmentNameTooLong => 29,
143            Self::NameTooLong => 30,
144            Self::InvalidName => 31,
145            Self::InvalidExpressionResult => 33,
146            Self::InvalidLogicalValue => 34,
147            Self::InvalidExpression => 35,
148            Self::UnmatchedParen => 36,
149            Self::UnexpectedCommaOrParen => 37,
150            Self::InvalidTemplate => 38,
151            Self::IncorrectCall => 40,
152            Self::BadArithmetic => 41,
153            Self::ArithmeticOverflow => 42,
154            Self::RoutineNotFound => 43,
155            Self::NoReturnData => 44,
156            Self::NoReturnValue => 45,
157            Self::InvalidVariableRef => 46,
158            Self::SystemFailure => 48,
159            Self::InterpretationError => 49,
160        }
161    }
162
163    /// Standard REXX error message text.
164    pub fn message(self) -> &'static str {
165        match self {
166            Self::Halt => "Program interrupted",
167            Self::ResourceExhausted => "System resources exhausted",
168            Self::UnmatchedComment => "Unmatched /* in source",
169            Self::ExpectedWhenOtherwise => "WHEN or OTHERWISE expected",
170            Self::UnexpectedThenElse => "Unexpected THEN or ELSE",
171            Self::UnexpectedWhenOtherwise => "Unexpected WHEN or OTHERWISE",
172            Self::UnexpectedEnd => "Unexpected or unmatched END",
173            Self::InvalidCharacter => "Invalid character in program",
174            Self::IncompleteBlock => "Incomplete DO/SELECT/IF",
175            Self::InvalidHexBinary => "Invalid hexadecimal or binary string",
176            Self::LabelNotFound => "Label not found",
177            Self::UnexpectedProcedure => "Unexpected PROCEDURE",
178            Self::ExpectedThen => "THEN expected",
179            Self::ExpectedStringOrSymbol => "String or symbol expected",
180            Self::ExpectedSymbol => "Symbol expected",
181            Self::InvalidDataOnEnd => "Invalid data on end of clause",
182            Self::InvalidTrace => "Invalid TRACE request",
183            Self::InvalidSubKeyword => "Invalid sub-keyword found",
184            Self::InvalidWholeNumber => "Invalid whole number",
185            Self::InvalidDoSyntax => "Invalid DO syntax",
186            Self::InvalidLeaveIterate => "Invalid LEAVE or ITERATE",
187            Self::EnvironmentNameTooLong => "Environment name too long",
188            Self::NameTooLong => "Name or string too long",
189            Self::InvalidName => "Name starts with number or \".\"",
190            Self::InvalidExpressionResult => "Invalid expression result",
191            Self::InvalidLogicalValue => "Logical value not 0 or 1",
192            Self::InvalidExpression => "Invalid expression",
193            Self::UnmatchedParen => "Unmatched \"(\" in expression",
194            Self::UnexpectedCommaOrParen => "Unexpected \",\" or \")\"",
195            Self::InvalidTemplate => "Invalid template or pattern",
196            Self::IncorrectCall => "Incorrect call to routine",
197            Self::BadArithmetic => "Bad arithmetic conversion",
198            Self::ArithmeticOverflow => "Arithmetic overflow/underflow",
199            Self::RoutineNotFound => "Routine not found",
200            Self::NoReturnData => "Function did not return data",
201            Self::NoReturnValue => "No data specified on function RETURN",
202            Self::InvalidVariableRef => "Invalid variable reference",
203            Self::SystemFailure => "Failure in system service",
204            Self::InterpretationError => "Interpretation error",
205        }
206    }
207}
208
209/// A REXX runtime/parse error with location and context.
210#[derive(Debug, Clone)]
211pub struct RexxDiagnostic {
212    pub error: RexxError,
213    pub location: Option<SourceLoc>,
214    pub detail: Option<String>,
215}
216
217impl RexxDiagnostic {
218    pub fn new(error: RexxError) -> Self {
219        Self {
220            error,
221            location: None,
222            detail: None,
223        }
224    }
225
226    pub fn at(mut self, loc: SourceLoc) -> Self {
227        self.location = Some(loc);
228        self
229    }
230
231    pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
232        self.detail = Some(detail.into());
233        self
234    }
235}
236
237impl fmt::Display for RexxDiagnostic {
238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239        write!(
240            f,
241            "Error {} — {}",
242            self.error.number(),
243            self.error.message()
244        )?;
245
246        if let Some(ref detail) = self.detail {
247            write!(f, ": {detail}")?;
248        }
249
250        if let Some(ref loc) = self.location {
251            write!(f, "\n  at line {}, column {}", loc.line, loc.col)?;
252            if let Some(ref source) = loc.source_line {
253                write!(f, "\n  | {source}")?;
254                write!(f, "\n  | {:>width$}", "^", width = loc.col)?;
255            }
256        }
257
258        Ok(())
259    }
260}
261
262impl std::error::Error for RexxDiagnostic {}
263
264/// Convenience alias.
265pub type RexxResult<T> = Result<T, RexxDiagnostic>;