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 MisalignedBranchTarget {
164 label: String,
166 disp: i64,
168 alignment: u8,
170 span: Span,
172 },
173
174 Syntax {
176 msg: String,
178 span: Span,
180 },
181
182 RelaxationLimit {
184 max: usize,
186 },
187
188 ResourceLimitExceeded {
190 resource: String,
192 limit: usize,
194 },
195
196 Multiple {
198 errors: Vec<AsmError>,
200 },
201}
202
203impl fmt::Display for AsmError {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 match self {
206 AsmError::UnknownMnemonic {
207 mnemonic,
208 arch,
209 span,
210 } => {
211 write!(f, "{}: unknown mnemonic '{}' for {}", span, mnemonic, arch)
212 }
213 AsmError::InvalidOperands { detail, span } => {
214 write!(f, "{}: invalid operand combination: {}", span, detail)
215 }
216 AsmError::ImmediateOverflow {
217 value,
218 min,
219 max,
220 span,
221 } => {
222 write!(
223 f,
224 "{}: immediate value {} out of range [{}..{}]",
225 span, value, min, max
226 )
227 }
228 AsmError::UndefinedLabel { label, span } => {
229 write!(f, "{}: undefined label '{}'", span, label)
230 }
231 AsmError::DuplicateLabel {
232 label,
233 span,
234 first_span,
235 } => {
236 write!(
237 f,
238 "{}: duplicate label '{}' (first defined at {})",
239 span, label, first_span
240 )
241 }
242 AsmError::BranchOutOfRange {
243 label,
244 disp,
245 max,
246 span,
247 } => {
248 write!(
249 f,
250 "{}: branch target '{}' out of range (displacement={}, max=±{})",
251 span, label, disp, max
252 )
253 }
254 AsmError::MisalignedBranchTarget {
255 label,
256 disp,
257 alignment,
258 span,
259 } => {
260 write!(
261 f,
262 "{}: branch target '{}' is not {}-byte aligned (displacement={})",
263 span, label, alignment, disp
264 )
265 }
266 AsmError::Syntax { msg, span } => {
267 write!(f, "{}: {}", span, msg)
268 }
269 AsmError::RelaxationLimit { max } => {
270 write!(
271 f,
272 "assembly exceeded maximum of {} relaxation passes (possible oscillation)",
273 max
274 )
275 }
276 AsmError::ResourceLimitExceeded { resource, limit } => {
277 write!(
278 f,
279 "resource limit exceeded: {} (limit: {})",
280 resource, limit
281 )
282 }
283 AsmError::Multiple { errors } => {
284 for (i, e) in errors.iter().enumerate() {
285 if i > 0 {
286 writeln!(f)?;
287 }
288 write!(f, "{}", e)?;
289 }
290 Ok(())
291 }
292 }
293 }
294}
295
296#[cfg(feature = "std")]
297impl std::error::Error for AsmError {}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 #[test]
304 fn span_display() {
305 let span = Span::new(3, 12, 45, 5);
306 assert_eq!(format!("{}", span), "3:12");
307 }
308
309 #[test]
310 fn span_dummy() {
311 let span = Span::dummy();
312 assert_eq!(span.line, 0);
313 assert_eq!(span.col, 0);
314 }
315
316 #[test]
317 fn error_unknown_mnemonic_display() {
318 let err = AsmError::UnknownMnemonic {
319 mnemonic: "foobar".into(),
320 arch: ArchName::X86_64,
321 span: Span::new(3, 12, 0, 6),
322 };
323 assert_eq!(
324 format!("{}", err),
325 "3:12: unknown mnemonic 'foobar' for x86_64"
326 );
327 }
328
329 #[test]
330 fn error_syntax_display() {
331 let err = AsmError::Syntax {
332 msg: "unexpected token '!'".into(),
333 span: Span::new(1, 5, 4, 1),
334 };
335 assert_eq!(format!("{}", err), "1:5: unexpected token '!'");
336 }
337
338 #[test]
339 fn error_undefined_label_display() {
340 let err = AsmError::UndefinedLabel {
341 label: "my_label".into(),
342 span: Span::new(10, 1, 100, 8),
343 };
344 assert_eq!(format!("{}", err), "10:1: undefined label 'my_label'");
345 }
346
347 #[test]
348 fn error_immediate_overflow_display() {
349 let err = AsmError::ImmediateOverflow {
350 value: 256,
351 min: -128,
352 max: 127,
353 span: Span::new(5, 10, 50, 3),
354 };
355 assert_eq!(
356 format!("{}", err),
357 "5:10: immediate value 256 out of range [-128..127]"
358 );
359 }
360
361 #[test]
362 fn error_duplicate_label_display() {
363 let err = AsmError::DuplicateLabel {
364 label: "loop".into(),
365 span: Span::new(20, 1, 200, 4),
366 first_span: Span::new(5, 1, 50, 4),
367 };
368 assert_eq!(
369 format!("{}", err),
370 "20:1: duplicate label 'loop' (first defined at 5:1)"
371 );
372 }
373
374 #[test]
375 fn error_relaxation_limit_display() {
376 let err = AsmError::RelaxationLimit { max: 20 };
377 assert_eq!(
378 format!("{}", err),
379 "assembly exceeded maximum of 20 relaxation passes (possible oscillation)"
380 );
381 }
382
383 #[test]
384 fn error_branch_out_of_range_display() {
385 let err = AsmError::BranchOutOfRange {
386 label: "far_away".into(),
387 disp: 500000,
388 max: 127,
389 span: Span::new(1, 1, 0, 10),
390 };
391 assert_eq!(
392 format!("{}", err),
393 "1:1: branch target 'far_away' out of range (displacement=500000, max=±127)"
394 );
395 }
396
397 #[test]
398 fn error_multiple_display() {
399 let err = AsmError::Multiple {
400 errors: vec![
401 AsmError::Syntax {
402 msg: "err1".into(),
403 span: Span::new(1, 1, 0, 1),
404 },
405 AsmError::Syntax {
406 msg: "err2".into(),
407 span: Span::new(2, 1, 5, 1),
408 },
409 ],
410 };
411 let s = format!("{}", err);
412 assert!(s.contains("err1"));
413 assert!(s.contains("err2"));
414 }
415
416 #[test]
417 fn error_resource_limit_exceeded_display() {
418 let err = AsmError::ResourceLimitExceeded {
419 resource: "statements".into(),
420 limit: 1_000_000,
421 };
422 assert_eq!(
423 format!("{}", err),
424 "resource limit exceeded: statements (limit: 1000000)"
425 );
426 }
427}