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}