mipsasm_rsp/
error.rs

1use alloc::{
2    fmt::{self, Write},
3    format,
4    string::{String, ToString},
5};
6use core::cmp;
7
8#[macro_export]
9macro_rules! error {
10    ($self:ident, MultipleLabelDefinition, $label:expr, $first:expr) => {
11        ParserError::MultipleLabelDefinition {
12            line: Line::new(
13                $self.line_num,
14                $self.input.get($self.line_num - 1).unwrap().to_string(),
15            ),
16            label: $label.to_string(),
17            first: Line::new($first + 1, $self.input.get($first).unwrap().to_string()),
18        }
19    };
20    ($self:ident, InvalidLabel, $label:expr) => {
21        ParserError::InvalidLabel {
22            line: Line::new(
23                $self.line_num,
24                $self.input.get($self.line_num - 1).unwrap().to_string(),
25            ),
26            label: $label.to_string(),
27        }
28    };
29    ($self:ident, InvalidInstruction) => {
30        ParserError::InvalidInstruction {
31            line: Line::new(
32                $self.line_num,
33                $self.input.get($self.line_num - 1).unwrap().to_string(),
34            ),
35        }
36    };
37    ($self:ident, InvalidOperandCount, $ops:expr, $expected:expr, $found:expr) => {
38        ParserError::InvalidOperandCount {
39            line: Line::new(
40                $self.line_num,
41                $self.input.get($self.line_num - 1).unwrap().to_string(),
42            ),
43            ops: $ops.to_string(),
44            expected: $expected,
45            found: $found,
46        }
47    };
48    ($self:ident, InvalidOpcode, $opcode:expr) => {
49        ParserError::InvalidOpcode {
50            line: Line::new(
51                $self.line_num,
52                $self.input.get($self.line_num - 1).unwrap().to_string(),
53            ),
54            opcode: $opcode.to_string(),
55        }
56    };
57    ($self:ident, InvalidRegister, $register:expr) => {
58        ParserError::InvalidRegister {
59            line: Line::new(
60                $self.line_num,
61                $self.input.get($self.line_num - 1).unwrap().to_string(),
62            ),
63            register: $register.to_string(),
64        }
65    };
66    ($self:ident, InvalidTargetAddress, $target:expr) => {
67        ParserError::InvalidTargetAddress {
68            line: Line::new(
69                $self.line_num,
70                $self.input.get($self.line_num - 1).unwrap().to_string(),
71            ),
72            address: $target.to_string(),
73        }
74    };
75    ($self:ident, InvalidImmediate, $immediate:expr) => {
76        ParserError::InvalidImmediate {
77            line: Line::new(
78                $self.line_num,
79                $self.input.get($self.line_num - 1).unwrap().to_string(),
80            ),
81            immediate: $immediate.to_string(),
82        }
83    };
84    ($self:ident, InvalidFloatCond, $cond:expr) => {
85        ParserError::InvalidFloatCond {
86            line: Line::new(
87                $self.line_num,
88                $self.input.get($self.line_num - 1).unwrap().to_string(),
89            ),
90            cond: $cond.to_string(),
91        }
92    };
93    ($self:ident, BranchOutOfBounds, $line:expr, $target:expr, $bounds:expr) => {
94        ParserError::BranchOutOfBounds {
95            line: Line::new(
96                $self.line_num,
97                $self.input.get($self.line_num - 1).unwrap().to_string(),
98            ),
99            branch: $target,
100            bounds: $bounds,
101        }
102    };
103    ($self:ident, LocalLabelOutOfScope, $line_num:expr, $label:expr) => {
104        ParserError::LocalLabelOutOfScope {
105            line: Line::new(
106                $line_num,
107                $self.input.get($line_num - 1).unwrap().to_string(),
108            ),
109            label: $label.to_string(),
110        }
111    };
112    ($self:ident, UndefinedLabel, $line_num:expr, $label:expr) => {
113        ParserError::UndefinedLabel {
114            line: Line::new(
115                $line_num,
116                $self.input.get($line_num - 1).unwrap().to_string(),
117            ),
118            label: $label.to_string(),
119        }
120    };
121}
122
123#[macro_export]
124macro_rules! warning {
125    ($self:ident, InvalidInstructionInDelaySlot) => {
126        ParserWarning::InvalidInstructionInDelaySlot {
127            delay_slot_inst: Line::new(
128                $self.line_num,
129                $self.input.get($self.line_num - 1).unwrap().to_string(),
130            ),
131            line: Line::new(
132                $self.line_num - 1,
133                $self.input.get($self.line_num - 2).unwrap().to_string(),
134            ),
135        }
136    };
137    ($self:ident, UnalignedBranch, $target:expr) => {
138        ParserWarning::UnalignedBranch {
139            line: Line::new(
140                $self.line_num,
141                $self.input.get($self.line_num - 1).unwrap().to_string(),
142            ),
143            offset: $target,
144        }
145    };
146    ($self:ident, UnalignedJump, $target:expr) => {
147        ParserWarning::UnalignedJump {
148            line: Line::new(
149                $self.line_num,
150                $self.input.get($self.line_num - 1).unwrap().to_string(),
151            ),
152            target: $target,
153        }
154    };
155}
156
157#[derive(Debug)]
158pub struct Line {
159    num: usize,
160    content: String,
161}
162
163impl Line {
164    pub fn new(num: usize, content: String) -> Self {
165        Self { num, content }
166    }
167}
168
169#[derive(Debug)]
170pub enum ParserError {
171    MultipleLabelDefinition {
172        line: Line,
173        label: String,
174        first: Line,
175    },
176    InvalidLabel {
177        line: Line,
178        label: String,
179    },
180    InvalidInstruction {
181        line: Line,
182    },
183    InvalidOperandCount {
184        line: Line,
185        expected: usize,
186        found: usize,
187        ops: String,
188    },
189    InvalidOpcode {
190        line: Line,
191        opcode: String,
192    },
193    InvalidRegister {
194        line: Line,
195        register: String,
196    },
197    InvalidTargetAddress {
198        line: Line,
199        address: String,
200    },
201    InvalidImmediate {
202        line: Line,
203        immediate: String,
204    },
205    InvalidFloatCond {
206        line: Line,
207        cond: String,
208    },
209    BranchOutOfBounds {
210        line: Line,
211        branch: String,
212        bounds: (u32, u32),
213    },
214    LocalLabelOutOfScope {
215        line: Line,
216        label: String,
217    },
218    UndefinedLabel {
219        line: Line,
220        label: String,
221    },
222}
223
224impl fmt::Display for ParserError {
225    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
226        match self {
227            Self::MultipleLabelDefinition {
228                line: Line { num, content },
229                label,
230                first:
231                    Line {
232                        num: first_num,
233                        content: first_content,
234                    },
235            } => {
236                let margin = cmp::max(num.to_string().len(), first_num.to_string().len());
237                writeln!(
238                    f,
239                    "\x1b[91merror\x1b[0m: label `{}` defined multiple times",
240                    label
241                )?;
242                writeln!(
243                    f,
244                    "{}",
245                    fmt_line(
246                        *first_num,
247                        first_content,
248                        margin,
249                        true,
250                        "first defined here",
251                        true,
252                        label
253                    )
254                )?;
255                writeln!(f, "\x1b[94m...\x1b[0m")?;
256                writeln!(
257                    f,
258                    "{}",
259                    fmt_line(*num, content, margin, false, "redefined here", false, label)
260                )
261            }
262            Self::InvalidLabel {
263                line: Line { num, content },
264                label,
265            } => {
266                let margin = num.to_string().len();
267                writeln!(
268                    f,
269                    "\x1b[91merror\x1b[0m: label `{}` must start with a letter",
270                    label
271                )?;
272                writeln!(
273                    f,
274                    "{}",
275                    fmt_line(*num, content, margin, false, "defined here", true, label)
276                )
277            }
278            Self::InvalidInstruction {
279                line: Line { num, content },
280            } => {
281                let margin = num.to_string().len();
282                writeln!(
283                    f,
284                    "\x1b[91merror\x1b[0m: invalid instruction `{}`",
285                    content.trim()
286                )?;
287                writeln!(
288                    f,
289                    "{}",
290                    fmt_line(*num, content, margin, false, "", true, content.trim())
291                )
292            }
293            Self::InvalidOperandCount {
294                line: Line { num, content },
295                ops,
296                expected,
297                found,
298            } => {
299                let margin = num.to_string().len();
300                writeln!(
301                    f,
302                    "\x1b[91merror\x1b[0m: invalid number of operands `{}`",
303                    content
304                )?;
305                writeln!(
306                    f,
307                    "{}",
308                    fmt_line(
309                        *num,
310                        content,
311                        margin,
312                        false,
313                        format!("expected {} operands, found {}", expected, found).as_str(),
314                        true,
315                        ops
316                    )
317                )
318            }
319            Self::InvalidOpcode {
320                line: Line { num, content },
321                opcode,
322            } => {
323                let margin = num.to_string().len();
324                writeln!(f, "\x1b[91merror\x1b[0m: invalid opcode `{}`", opcode)?;
325                writeln!(
326                    f,
327                    "{}",
328                    fmt_line(*num, content, margin, false, "", true, opcode)
329                )
330            }
331            Self::InvalidRegister {
332                line: Line { num, content },
333                register,
334            } => {
335                let margin = num.to_string().len();
336                writeln!(f, "\x1b[91merror\x1b[0m: invalid register `{}`", register)?;
337                writeln!(
338                    f,
339                    "{}",
340                    fmt_line(*num, content, margin, false, "", true, register)
341                )
342            }
343            Self::InvalidTargetAddress {
344                line: Line { num, content },
345                address,
346            } => {
347                let margin = num.to_string().len();
348                writeln!(
349                    f,
350                    "\x1b[91merror\x1b[0m: invalid target address `{}`",
351                    address
352                )?;
353                writeln!(
354                    f,
355                    "{}",
356                    fmt_line(*num, content, margin, false, "", true, address)
357                )
358            }
359            Self::InvalidImmediate {
360                line: Line { num, content },
361                immediate,
362            } => {
363                let margin = num.to_string().len();
364                writeln!(f, "\x1b[91merror\x1b[0m: invalid immediate `{}`", immediate)?;
365                writeln!(
366                    f,
367                    "{}",
368                    fmt_line(*num, content, margin, false, "", true, immediate)
369                )
370            }
371            Self::InvalidFloatCond {
372                line: Line { num, content },
373                cond,
374            } => {
375                let margin = num.to_string().len();
376                writeln!(
377                    f,
378                    "\x1b[91merror\x1b[0m: invalid float compare condition `{}`",
379                    cond
380                )?;
381                writeln!(
382                    f,
383                    "{}",
384                    fmt_line(*num, content, margin, false, "", true, cond)
385                )
386            }
387            Self::BranchOutOfBounds {
388                line: Line { num, content },
389                branch,
390                bounds,
391            } => {
392                let margin = num.to_string().len();
393                writeln!(f, "\x1b[91merror\x1b[0m: branch `{}` out of bounds", branch)?;
394                writeln!(
395                    f,
396                    "{}",
397                    fmt_line(
398                        *num,
399                        content,
400                        margin,
401                        false,
402                        &format!(
403                            "should be between 0x{:08x?} and 0x{:08x?}",
404                            bounds.0, bounds.1
405                        ),
406                        true,
407                        branch
408                    )
409                )
410            }
411            Self::LocalLabelOutOfScope {
412                line: Line { num, content },
413                label,
414            } => {
415                let margin = num.to_string().len();
416                writeln!(
417                    f,
418                    "\x1b[91merror\x1b[0m: local label `{}` used outside of its scope",
419                    label
420                )?;
421                writeln!(
422                    f,
423                    "{}",
424                    fmt_line(*num, content, margin, false, "", true, label)
425                )
426            }
427            Self::UndefinedLabel {
428                line: Line { num, content },
429                label,
430            } => {
431                let margin = num.to_string().len();
432                writeln!(f, "\x1b[91merror\x1b[0m: label `{}` is not defined", label)?;
433                writeln!(
434                    f,
435                    "{}",
436                    fmt_line(*num, content, margin, false, "used here", true, label)
437                )
438            }
439        }
440    }
441}
442
443fn fmt_line(
444    num: usize,
445    content: &str,
446    margin: usize,
447    err_underline: bool,
448    msg: &str,
449    first_space: bool,
450    underline: &str,
451) -> String {
452    let mut s = String::new();
453    let underline_start = content
454        .find(underline)
455        .unwrap_or_else(|| panic!("{}", content));
456    if first_space {
457        writeln!(s, "\x1b[94m{:>margin$} |\x1b[0m", "").unwrap();
458    }
459    writeln!(s, "\x1b[94m{:>margin$} |\x1b[0m {}", num, content).unwrap();
460    if err_underline {
461        writeln!(
462            s,
463            "\x1b[94m{:>margin$} | {: <start$}{:-<len$} {msg}\x1b[0m",
464            "",
465            "",
466            "",
467            len = underline.len(),
468            start = underline_start
469        )
470        .unwrap();
471    } else {
472        writeln!(
473            s,
474            "\x1b[94m{:>margin$} | {: <start$}\x1b[91m{:^<len$} {msg}\x1b[0m",
475            "",
476            "",
477            "",
478            len = underline.len(),
479            start = underline_start
480        )
481        .unwrap();
482    }
483    write!(s, "\x1b[94m{:>margin$} |\x1b[0m", "").unwrap();
484    s
485}