1#[allow(unused_imports)]
4use alloc::format;
5use alloc::string::String;
6#[allow(unused_imports)]
7use alloc::vec;
8use alloc::vec::Vec;
9use core::fmt;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct Span {
18 pub line: u32,
20 pub col: u32,
22 pub offset: usize,
24 pub len: usize,
26}
27
28impl Span {
29 #[must_use]
31 pub fn new(line: u32, col: u32, offset: usize, len: usize) -> Self {
32 Self {
33 line,
34 col,
35 offset,
36 len,
37 }
38 }
39
40 #[must_use]
42 pub fn dummy() -> Self {
43 Self {
44 line: 0,
45 col: 0,
46 offset: 0,
47 len: 0,
48 }
49 }
50}
51
52impl fmt::Display for Span {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 write!(f, "{}:{}", self.line, self.col)
55 }
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
61pub enum ArchName {
62 X86,
64 X86_64,
66 Arm,
68 Thumb,
70 Aarch64,
72 Rv32,
74 Rv64,
76}
77
78impl fmt::Display for ArchName {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 match self {
81 ArchName::X86 => write!(f, "x86"),
82 ArchName::X86_64 => write!(f, "x86_64"),
83 ArchName::Arm => write!(f, "ARM"),
84 ArchName::Thumb => write!(f, "Thumb"),
85 ArchName::Aarch64 => write!(f, "AArch64"),
86 ArchName::Rv32 => write!(f, "RV32"),
87 ArchName::Rv64 => write!(f, "RV64"),
88 }
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Eq)]
94#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95pub enum AsmError {
96 UnknownMnemonic {
98 mnemonic: String,
100 arch: ArchName,
102 span: Span,
104 },
105
106 InvalidOperands {
108 detail: String,
110 span: Span,
112 },
113
114 ImmediateOverflow {
116 value: i128,
118 min: i128,
120 max: i128,
122 span: Span,
124 },
125
126 UndefinedLabel {
128 label: String,
130 span: Span,
132 },
133
134 DuplicateLabel {
136 label: String,
138 span: Span,
140 first_span: Span,
142 },
143
144 BranchOutOfRange {
146 label: String,
148 disp: i64,
150 max: i64,
152 span: Span,
154 },
155
156 Syntax {
158 msg: String,
160 span: Span,
162 },
163
164 RelaxationLimit {
166 max: usize,
168 },
169
170 ResourceLimitExceeded {
172 resource: String,
174 limit: usize,
176 },
177
178 Multiple {
180 errors: Vec<AsmError>,
182 },
183}
184
185impl fmt::Display for AsmError {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 match self {
188 AsmError::UnknownMnemonic {
189 mnemonic,
190 arch,
191 span,
192 } => {
193 write!(f, "{}: unknown mnemonic '{}' for {}", span, mnemonic, arch)
194 }
195 AsmError::InvalidOperands { detail, span } => {
196 write!(f, "{}: invalid operand combination: {}", span, detail)
197 }
198 AsmError::ImmediateOverflow {
199 value,
200 min,
201 max,
202 span,
203 } => {
204 write!(
205 f,
206 "{}: immediate value {} out of range [{}..{}]",
207 span, value, min, max
208 )
209 }
210 AsmError::UndefinedLabel { label, span } => {
211 write!(f, "{}: undefined label '{}'", span, label)
212 }
213 AsmError::DuplicateLabel {
214 label,
215 span,
216 first_span,
217 } => {
218 write!(
219 f,
220 "{}: duplicate label '{}' (first defined at {})",
221 span, label, first_span
222 )
223 }
224 AsmError::BranchOutOfRange {
225 label,
226 disp,
227 max,
228 span,
229 } => {
230 write!(
231 f,
232 "{}: branch target '{}' out of range (displacement={}, max=±{})",
233 span, label, disp, max
234 )
235 }
236 AsmError::Syntax { msg, span } => {
237 write!(f, "{}: {}", span, msg)
238 }
239 AsmError::RelaxationLimit { max } => {
240 write!(
241 f,
242 "assembly exceeded maximum of {} relaxation passes (possible oscillation)",
243 max
244 )
245 }
246 AsmError::ResourceLimitExceeded { resource, limit } => {
247 write!(
248 f,
249 "resource limit exceeded: {} (limit: {})",
250 resource, limit
251 )
252 }
253 AsmError::Multiple { errors } => {
254 for (i, e) in errors.iter().enumerate() {
255 if i > 0 {
256 writeln!(f)?;
257 }
258 write!(f, "{}", e)?;
259 }
260 Ok(())
261 }
262 }
263 }
264}
265
266#[cfg(feature = "std")]
267impl std::error::Error for AsmError {}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn span_display() {
275 let span = Span::new(3, 12, 45, 5);
276 assert_eq!(format!("{}", span), "3:12");
277 }
278
279 #[test]
280 fn span_dummy() {
281 let span = Span::dummy();
282 assert_eq!(span.line, 0);
283 assert_eq!(span.col, 0);
284 }
285
286 #[test]
287 fn error_unknown_mnemonic_display() {
288 let err = AsmError::UnknownMnemonic {
289 mnemonic: "foobar".into(),
290 arch: ArchName::X86_64,
291 span: Span::new(3, 12, 0, 6),
292 };
293 assert_eq!(
294 format!("{}", err),
295 "3:12: unknown mnemonic 'foobar' for x86_64"
296 );
297 }
298
299 #[test]
300 fn error_syntax_display() {
301 let err = AsmError::Syntax {
302 msg: "unexpected token '!'".into(),
303 span: Span::new(1, 5, 4, 1),
304 };
305 assert_eq!(format!("{}", err), "1:5: unexpected token '!'");
306 }
307
308 #[test]
309 fn error_undefined_label_display() {
310 let err = AsmError::UndefinedLabel {
311 label: "my_label".into(),
312 span: Span::new(10, 1, 100, 8),
313 };
314 assert_eq!(format!("{}", err), "10:1: undefined label 'my_label'");
315 }
316
317 #[test]
318 fn error_immediate_overflow_display() {
319 let err = AsmError::ImmediateOverflow {
320 value: 256,
321 min: -128,
322 max: 127,
323 span: Span::new(5, 10, 50, 3),
324 };
325 assert_eq!(
326 format!("{}", err),
327 "5:10: immediate value 256 out of range [-128..127]"
328 );
329 }
330
331 #[test]
332 fn error_duplicate_label_display() {
333 let err = AsmError::DuplicateLabel {
334 label: "loop".into(),
335 span: Span::new(20, 1, 200, 4),
336 first_span: Span::new(5, 1, 50, 4),
337 };
338 assert_eq!(
339 format!("{}", err),
340 "20:1: duplicate label 'loop' (first defined at 5:1)"
341 );
342 }
343
344 #[test]
345 fn error_relaxation_limit_display() {
346 let err = AsmError::RelaxationLimit { max: 20 };
347 assert_eq!(
348 format!("{}", err),
349 "assembly exceeded maximum of 20 relaxation passes (possible oscillation)"
350 );
351 }
352
353 #[test]
354 fn error_branch_out_of_range_display() {
355 let err = AsmError::BranchOutOfRange {
356 label: "far_away".into(),
357 disp: 500000,
358 max: 127,
359 span: Span::new(1, 1, 0, 10),
360 };
361 assert_eq!(
362 format!("{}", err),
363 "1:1: branch target 'far_away' out of range (displacement=500000, max=±127)"
364 );
365 }
366
367 #[test]
368 fn error_multiple_display() {
369 let err = AsmError::Multiple {
370 errors: vec![
371 AsmError::Syntax {
372 msg: "err1".into(),
373 span: Span::new(1, 1, 0, 1),
374 },
375 AsmError::Syntax {
376 msg: "err2".into(),
377 span: Span::new(2, 1, 5, 1),
378 },
379 ],
380 };
381 let s = format!("{}", err);
382 assert!(s.contains("err1"));
383 assert!(s.contains("err2"));
384 }
385
386 #[test]
387 fn error_resource_limit_exceeded_display() {
388 let err = AsmError::ResourceLimitExceeded {
389 resource: "statements".into(),
390 limit: 1_000_000,
391 };
392 assert_eq!(
393 format!("{}", err),
394 "resource limit exceeded: statements (limit: 1000000)"
395 );
396 }
397}