1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::fmt::Display;
4use std::ops::Deref;
5use std::sync::LazyLock;
6
7use codespan_reporting::diagnostic::{Diagnostic, Label, Severity};
8use codespan_reporting::files::SimpleFiles;
9use codespan_reporting::term::termcolor::Buffer;
10use codespan_reporting::term::{self, Chars, DisplayStyle};
11use cpclib_basic::BasicError;
12use cpclib_common::itertools::Itertools;
13use cpclib_common::smol_str::SmolStr;
14use cpclib_disc::amsdos::AmsdosError;
15use cpclib_sna::SnapshotError;
16use cpclib_tokens::symbols::{PhysicalAddress, Source, Symbol, SymbolError};
17use cpclib_tokens::{BinaryOperation, ExpressionTypeError, tokens};
18
19use crate::Z80Span;
20use crate::assembler::AssemblingPass;
21use crate::parser::ParserContext;
22use crate::preamble::{LocatedListing, SourceString, Z80ParserError, Z80ParserErrorKind};
23
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum ExpressionError {
26 LeftError(BinaryOperation, Box<AssemblerError>),
27 RightError(BinaryOperation, Box<AssemblerError>),
28 LeftAndRightError(BinaryOperation, Box<AssemblerError>, Box<AssemblerError>),
29 OwnError(Box<AssemblerError>),
30 InvalidSize(usize, usize) }
32
33#[derive(Debug, Clone, PartialEq, Eq)]
34#[allow(missing_docs)]
35pub enum AssemblerError {
36 AlreadyRenderedError(String),
38
39 LocatedListingError(std::sync::Arc<LocatedListing>),
41
42 MultipleErrors {
44 errors: Vec<AssemblerError>
45 },
46
47 EmptyBinaryFile(String),
49
50 AmsdosError {
52 error: AmsdosError
53 },
54
55 BugInAssembler {
57 file: &'static str,
58 line: u32,
59 msg: String
60 },
61
62 BugInParser {
64 error: String,
65 context: ParserContext
66 },
67
68 SyntaxError {
71 error: Z80ParserError
72 },
73
74 IncludedFileError {
75 span: Z80Span,
76 error: Box<AssemblerError>
77 },
78
79 BasicError {
81 error: BasicError
82 },
83
84 DisassemblerError {
85 msg: String
86 },
87
88 AssemblingError {
91 msg: String
92 },
93
94 InvalidArgument {
96 msg: String
97 },
98
99 Fail {
100 msg: String
101 },
102
103 AssertionFailed {
105 test: String,
106 msg: String,
107 guidance: String
108 },
109
110 SymbolAlreadyExists {
112 symbol: String
113 },
114
115 CounterAlreadyExists {
116 symbol: String
117 },
118
119 IncoherentCode {
120 msg: String
121 },
122
123 UnknownMacro {
128 symbol: SmolStr,
129 closest: Option<SmolStr>
130 },
131
132 MacroError {
134 name: SmolStr,
135 location: Option<Source>,
136 root: Box<AssemblerError>
137 },
138
139 WrongNumberOfParameters {
144 symbol: String,
145 nb_paramers: usize,
146 nb_arguments: usize
147 },
148
149 UnknownSymbol {
151 symbol: SmolStr,
152 closest: Option<SmolStr>
153 },
154
155 InvalidSymbol(SmolStr),
156
157 WrongSymbolType {
159 symbol: SmolStr,
160 isnot: SmolStr
161 },
162
163 AlreadyDefinedSymbol {
165 symbol: SmolStr,
166 kind: SmolStr,
167 here: Option<Source>
168 },
169
170 IOError {
172 msg: String
173 },
174
175 UnknownAssemblingAddress,
177 ReadOnlySymbol(Symbol),
178 RunAlreadySpecified,
179 NoActiveCounter,
180 NoDataToCrunch,
181 NotAllowed,
182
183 OutputExceedsLimits(PhysicalAddress, usize),
184 OutputAlreadyExceedsLimits(usize),
185 OutputProtected {
186 area: std::ops::RangeInclusive<u16>,
187 address: u16
188 },
189 OverrideMemory(PhysicalAddress, usize),
190
191 ExpressionUnresolvable {
193 expression: tokens::Expr
194 },
195
196 ExpressionError(ExpressionError),
197
198 RelativeAddressUncomputable {
199 address: i32,
200 pass: AssemblingPass,
201 error: Box<AssemblerError>
202 },
203
204 CrunchedSectionError {
205 error: Box<AssemblerError>
206 },
207
208 RelocatedError {
211 error: Box<AssemblerError>,
212 span: Z80Span
213 },
214 RelocatedWarning {
215 warning: Box<AssemblerError>,
216 span: Z80Span
217 },
218 RelocatedInfo {
219 info: Box<AssemblerError>,
220 span: Z80Span
221 },
222
223 ForIssue {
224 error: Box<AssemblerError>,
225 span: Option<Z80Span>
226 },
227
228 RepeatIssue {
229 error: Box<AssemblerError>,
230 span: Option<Z80Span>,
231 repetition: i32
232 },
233
234 WhileIssue {
235 error: Box<AssemblerError>,
236 span: Option<Z80Span>
237 },
238
239 MMRError {
240 value: i32
241 },
242
243 SnapshotError {
244 error: SnapshotError
245 },
246
247 FunctionWithoutReturn(String),
248 FunctionWithEmptyBody(String),
249 FunctionUnknown(String),
250 FunctionWithWrongNumberOfArguments(String, usize, usize),
251 FunctionError(String, Box<AssemblerError>),
252
253 ExpressionTypeError(ExpressionTypeError)
254}
255
256impl From<ExpressionTypeError> for AssemblerError {
257 fn from(e: ExpressionTypeError) -> Self {
258 Self::ExpressionTypeError(e)
259 }
260}
261
262impl From<&ExpressionTypeError> for AssemblerError {
263 fn from(e: &ExpressionTypeError) -> Self {
264 Self::ExpressionTypeError(e.clone())
265 }
266}
267
268impl From<Z80ParserError> for AssemblerError {
269 fn from(err: Z80ParserError) -> Self {
270 AssemblerError::SyntaxError { error: err }
271 }
272}
273
274impl From<std::io::Error> for AssemblerError {
275 fn from(err: std::io::Error) -> Self {
276 AssemblerError::IOError {
277 msg: err.to_string()
278 }
279 }
280}
281
282impl From<BasicError> for AssemblerError {
283 fn from(msg: BasicError) -> Self {
284 AssemblerError::BasicError { error: msg }
285 }
286}
287
288impl From<SnapshotError> for AssemblerError {
289 fn from(msg: SnapshotError) -> Self {
290 AssemblerError::SnapshotError { error: msg }
291 }
292}
293
294impl From<SymbolError> for AssemblerError {
295 fn from(err: SymbolError) -> Self {
296 match err {
297 SymbolError::UnknownAssemblingAddress => AssemblerError::UnknownAssemblingAddress,
298 SymbolError::CannotModify(symb) => AssemblerError::ReadOnlySymbol(symb),
299 SymbolError::WrongSymbol(err) => AssemblerError::InvalidSymbol(err.value().into()),
300 SymbolError::NoNamespaceActive => {
301 AssemblerError::AssemblingError {
302 msg: "There is no namespace active".to_owned()
303 }
304 },
305 }
306 }
307}
308
309impl From<AmsdosError> for AssemblerError {
310 fn from(err: AmsdosError) -> Self {
311 AssemblerError::AmsdosError { error: err }
312 }
313}
314
315impl AssemblerError {
316 pub fn is_located(&self) -> bool {
318 match self {
319 AssemblerError::RelocatedError{..} |
320 AssemblerError::RelocatedWarning{..} => true, _ => false
323 }
324 }
325
326 pub fn is_override_memory(&self) -> bool {
327 match self {
328 AssemblerError::OverrideMemory(..) => true,
329 AssemblerError::RelocatedError { error, .. }
330 | AssemblerError::RelocatedWarning { warning: error, .. } => error.is_override_memory(),
331 _ => false
332 }
333 }
334
335 pub fn locate(self, span: Z80Span) -> Self {
336 if self.is_located() {
337 self
338 }
339 else {
340 AssemblerError::RelocatedError {
341 span,
342 error: Box::new(self)
343 }
344 }
345 }
346
347 pub fn locate_warning(self, span: Z80Span) -> Self {
348 if self.is_located() {
349 self
350 }
351 else {
352 AssemblerError::RelocatedWarning {
353 span,
354 warning: Box::new(self)
355 }
356 }
357 }
358}
359
360pub(crate) const LD_WRONG_SOURCE: &str = "LD: error in the source";
361pub(crate) const LD_WRONG_DESTINATION: &str = "LD: error in the destination";
362
363pub(crate) const JP_WRONG_PARAM: &str = "JP: error in the destination";
364pub(crate) const JR_WRONG_PARAM: &str = "JR: error in the destination";
365pub(crate) const CALL_WRONG_PARAM: &str = "CALL: error in the destination";
366
367pub(crate) const SNASET_WRONG_LABEL: &str = "SNASET: error in the option naming";
368pub(crate) const SNASET_MISSING_COMMA: &str = "SNASET: missing comma";
369
370impl Display for AssemblerError {
371 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
372 self.format(f, true)
373 }
374}
375
376impl AssemblerError {
377 pub fn is_already_rendered(&self) -> bool {
378 match self {
379 AssemblerError::AlreadyRenderedError(_) => true,
380 _ => false
381 }
382 }
383
384 pub fn render(self) -> Self {
385 match &self {
386 Self::AlreadyRenderedError(_) => self,
387 _ => Self::AlreadyRenderedError(self.to_string())
388 }
389 }
390
391 pub fn format(&self, f: &mut std::fmt::Formatter<'_>, complete: bool) -> std::fmt::Result {
392 match self {
393 AssemblerError::SyntaxError { error } => {
394 let mut source_files = SimpleFiles::new();
395 let mut fname_to_id = std::collections::BTreeMap::new();
396
397 let str = error
398 .errors()
399 .iter()
400 .filter(|e| {
401 match e.1 {
402 Z80ParserErrorKind::Context(ctx) => {
403 !ctx.to_string().starts_with("[DBG]")
404 },
405 _ => true
407 }
408 })
409 .map(|e| {
410 match e.1 {
411 Z80ParserErrorKind::Context(_)
412 | Z80ParserErrorKind::Winnow
413 | Z80ParserErrorKind::Char(_) => {
414 let ctx: std::borrow::Cow<str> = match e.1 {
416 Z80ParserErrorKind::Context(ctx) => Cow::Owned(ctx.to_string()),
417 Z80ParserErrorKind::Winnow => "Unknown error".into(),
418 Z80ParserErrorKind::Char(c) => {
419 format!("Error with char '{}'", c).into()
420 },
421 _ => unreachable!()
422 };
423 let ctx = ctx.deref();
424
425 let span = &e.0;
426
427 let filename =
429 e.0.state
430 .filename()
431 .map(|p| p.as_os_str().to_str().unwrap());
432
433 let filename = filename.unwrap_or_else(|| {
434 e.0.state.context_name().unwrap_or("no file")
435 });
436 let filename = Box::new(filename.to_owned());
437
438 let source = e.0.state.complete_source();
439 let file_id = match fname_to_id.get(filename.deref()) {
440 Some(&id) => id,
441 None => {
442 let id =
443 source_files.add(filename.deref().to_owned(), source);
444 fname_to_id.insert(filename.deref().to_owned(), id);
445 id
446 }
447 };
448
449 let offset = Z80Span::from(**span).offset_from_start();
450 let sample_range = std::ops::Range {
451 start: offset,
452 end: guess_error_end(
453 source_files.get(file_id).unwrap().source(),
454 offset,
455 ctx
456 )
457 };
458 let mut diagnostic = Diagnostic::error()
459 .with_message("Syntax error")
460 .with_labels(vec![
461 Label::new(
462 codespan_reporting::diagnostic::LabelStyle::Primary,
463 file_id,
464 sample_range
465 )
466 .with_message(ctx),
467 ]);
468
469 if let Some(notes) = get_additional_notes(ctx) {
470 diagnostic = diagnostic.with_notes(notes);
471 }
472
473 let mut writer = buffer();
474 let config = config();
475 term::emit(&mut writer, &config, &source_files, &diagnostic)
476 .unwrap();
477
478 std::str::from_utf8(writer.as_slice()).unwrap().to_owned()
479 },
480
481 _ => unreachable!("{:?}", e.1)
482 }
483 })
484 .unique()
485 .join("\n");
486 write!(f, "{}", str)
487 },
488
489 AssemblerError::IncludedFileError { span, error } => {
490 match error.as_ref() {
491 AssemblerError::IOError { msg } => {
492 let msg = build_simple_error_message_with_message(
493 "Error for imported file",
494 msg,
495 span
496 );
497 write!(f, "{}", msg)
498 },
499 _ => {
500 let msg = build_simple_error_message(
501 "Error in imported file",
502 span,
503 Severity::Error
504 );
505 write!(f, "{}", msg)?;
506 error.fmt(f)
507 }
508 }
509 },
510
511 AssemblerError::OverrideMemory(address, count) => {
512 write!(f, "Override {} bytes at {}", *count, address)
513 },
514 AssemblerError::DisassemblerError { msg } => write!(f, "Disassembler error: {}", msg),
515
516 AssemblerError::ExpressionError(e) => {
517 let msg = match e {
518 ExpressionError::LeftError(oper, error) => {
519 format!("on left operand of {}: {}.", oper, error)
520 },
521 ExpressionError::RightError(oper, error) => {
522 format!("on right operand of {}: {}.", oper, error)
523 },
524 ExpressionError::LeftAndRightError(oper, error1, error2) => {
525 format!(
526 "on left and right operand of {}: {} / {}",
527 oper, error1, error2
528 )
529 },
530 ExpressionError::OwnError(error) => {
531 format!("{}", error)
532 },
533 ExpressionError::InvalidSize(expected, index) => {
534 format!("{} index incompatible with size {}", index, expected)
535 }
536 };
537 write!(f, "Expression error {}", msg)
538 },
539 AssemblerError::CounterAlreadyExists { symbol } => {
540 write!(f, "A counter named `{}` already exists", symbol)
541 },
542 AssemblerError::SymbolAlreadyExists { symbol } => {
543 write!(f, "A symbol named `{}` already exists", symbol)
544 },
545 AssemblerError::IncoherentCode { msg } => write!(f, "Incoherent code: {}", msg),
546 AssemblerError::NoActiveCounter => write!(f, "No active counter"),
547 AssemblerError::OutputExceedsLimits(address, limit) => {
548 write!(f, "Code at {} exceeds limits of 0x{:X}", address, limit)
549 },
550 AssemblerError::OutputAlreadyExceedsLimits(limit) => {
551 write!(f, "Code already exceeds limits of 0x{:X}", limit)
552 },
553 AssemblerError::RunAlreadySpecified => write!(f, "RUN has already been specified"),
554 AssemblerError::AlreadyDefinedSymbol { symbol, kind, here } => {
555 if let Some(here) = here {
556 write!(
557 f,
558 "Symbol \"{}\" already defined as a {} in {}",
559 symbol, kind, here
560 )
561 }
562 else {
563 write!(f, "Symbol \"{}\" already defined as a {}", symbol, kind)
564 }
565 },
566
567 AssemblerError::MultipleErrors { errors } => {
568 for e in errors.iter().map(|e| e.to_string()).unique() {
569 writeln!(f, "{}", e)?;
570 }
571 Ok(())
572 },
573
574 AssemblerError::UnknownSymbol { symbol, closest } => {
575 write!(
576 f,
577 "Unknown symbol: {}.{}",
578 symbol,
579 closest
580 .as_ref()
581 .map(|v| format!(" Closest one is: `{v}`"))
582 .unwrap_or_default()
583 )
584 },
585
586 AssemblerError::ExpressionTypeError(e) => write!(f, "{}", e),
587
588 AssemblerError::EmptyBinaryFile(_) => todo!(),
589 AssemblerError::AmsdosError { error: e } => {
590 write!(f, "AMSDOS error: {}", e)
591 },
592 AssemblerError::BugInAssembler { file, line, msg } => {
593 write!(f, "BUG in assembler {}:{} {}", file, line, msg)
594 },
595 AssemblerError::BugInParser {
596 error: _,
597 context: _
598 } => todo!(),
599
600 AssemblerError::BasicError { error } => write!(f, "{}", error),
601 AssemblerError::AssemblingError { msg } => write!(f, "{}", msg),
602 AssemblerError::InvalidArgument { msg } => write!(f, "Invalid argument: {}", msg),
603 AssemblerError::AssertionFailed {
604 test,
605 msg,
606 guidance
607 } => {
608 write!(f, "Assert error: {}\n{}\n{}", test, msg, guidance)
609 },
610
611 AssemblerError::UnknownMacro { symbol, closest } => {
612 write!(
613 f,
614 "MACRO {} does not exist. Try {}",
615 symbol,
616 closest.as_ref().unwrap_or(&SmolStr::new_inline(""))
617 )
618 },
619 AssemblerError::FunctionWithoutReturn(name) => {
620 write!(f, "Function {} has no RETURN directive", name)
621 },
622 AssemblerError::FunctionWithEmptyBody(name) => {
623 write!(f, "Function {} has no body", name)
624 },
625 AssemblerError::FunctionUnknown(name) => {
626 write!(f, "Function {} unknown", name)
627 },
628 AssemblerError::FunctionError(name, e) => {
629 write!(f, "Function {} error: {}", name, e)
630 },
631 AssemblerError::FunctionWithWrongNumberOfArguments(name, expected, received) => {
632 write!(
633 f,
634 "Function {} called with {} parameters instead of {}",
635 name, received, expected
636 )
637 },
638 AssemblerError::WrongNumberOfParameters {
639 symbol: _,
640 nb_paramers: _,
641 nb_arguments: _
642 } => todo!(),
643 AssemblerError::MacroError {
644 name,
645 location,
646 root
647 } => {
648 if let Some(location) = location {
649 write!(
650 f,
651 "Error in macro call {} (defined in {})\n{}",
652 name, location, root
653 )
654 }
655 else {
656 write!(f, "Error in macro call: {}\n{}", name, root)
657 }
658 },
659 AssemblerError::WrongSymbolType {
660 symbol: s,
661 isnot: n
662 } => {
663 write!(f, "Wrong symbol type: {} is not {}", s, n)
664 },
665 AssemblerError::IOError { msg } => {
666 write!(f, "IO Error: {}", msg)
667 },
668 AssemblerError::UnknownAssemblingAddress => todo!(),
669 AssemblerError::ExpressionUnresolvable { expression: _ } => todo!(),
670 AssemblerError::RelativeAddressUncomputable {
671 address: _,
672 pass: _,
673 error
674 } => {
675 write!(f, "Unable to compute relative address {}", error)
676 },
677
678 AssemblerError::RelocatedError { error, span } => {
680 if complete {
681 match error.deref() {
683 AssemblerError::RelocatedError { error, span: _ } => {
684 write!(f, "{}", error)
685 },
686
687 AssemblerError::UnknownSymbol { symbol, closest } => {
688 let msg = match closest {
689 Some(closest) => {
690 build_simple_error_message_with_notes(
691 &format!("Unknown symbol: {}", symbol),
692 vec![format!("Closest one is: {}", closest)],
693 span
694 )
695 },
696 None => {
697 build_simple_error_message(
698 &format!("Unknown symbol: {}", symbol),
699 span,
700 Severity::Error
701 )
702 },
703 };
704
705 write!(f, "{}", msg)
706 },
707
708 AssemblerError::UnknownMacro { symbol, closest } => {
709 let msg = match closest {
710 Some(closest) => {
711 build_simple_error_message_with_notes(
712 &format!("Unknown macro: {}", symbol),
713 vec![format!("Closest one is: {}", closest)],
714 span
715 )
716 },
717 None => {
718 build_simple_error_message(
719 &format!("Unknown macro: {}", symbol),
720 span,
721 Severity::Error
722 )
723 },
724 };
725
726 write!(f, "{}", msg)
727 },
728
729 AssemblerError::MacroError {
730 name,
731 location,
732 root
733 } => {
734 let msg = if let Some(location) = location {
735 format!("Error in macro call {} (defined in {})", name, location)
736 }
737 else {
738 format!("Error in macro call {}", name)
739 };
740
741 let msg = build_simple_error_message(&msg, span, Severity::Error);
742 write!(f, "{}\n{}", msg, root)
743 },
744
745 AssemblerError::BasicError { error } => {
746 let msg =
747 build_simple_error_message("BASIC error", span, Severity::Error);
748 write!(f, "{}\n{}", msg, error)
749 },
750
751 AssemblerError::OutputProtected { area, address } => {
752 let msg = build_simple_error_message_with_message(
753 "Forbidden output",
754 &format!(
755 "Tentative to write in 0x{:X} in a protected area [0x{:X}:0x{:X}]",
756 address,
757 area.start(),
758 area.end()
759 ),
760 span
761 );
762 write!(f, "{}", msg)
763 },
764
765 AssemblerError::CrunchedSectionError { error } => {
766 let msg = build_simple_error_message(
767 "Impossible to crunch section",
768 span,
769 Severity::Error
770 );
771 write!(f, "{}", msg)?;
772 write!(f, "{}", error)
773 },
774
775 _ => {
776 let msg = build_simple_error_message(
777 &format!("{}", error),
778 span,
779 Severity::Error
780 );
781 write!(f, "{}", msg)
782 }
783 }
784 }
785 else {
786 write!(f, "{}", error)
787 }
788 },
789 AssemblerError::ReadOnlySymbol(symb) => {
790 write!(f, "{} cannot be modified", symb.value())
791 },
792
793 AssemblerError::RepeatIssue {
794 error,
795 span,
796 repetition
797 } => {
798 if span.is_some() {
799 let msg = build_simple_error_message(
800 &format!("REPEAT: error in loop {}", repetition),
801 span.as_ref().unwrap(),
802 Severity::Error
803 );
804 write!(f, "{}\n{}", msg, error)
805 }
806 else {
807 write!(f, "Repeat issue\n{}", error)
808 }
809 },
810
811 AssemblerError::ForIssue { error, span } => {
812 if span.is_some() {
813 let msg = build_simple_error_message(
814 "FOR: error in loop",
815 span.as_ref().unwrap(),
816 Severity::Error
817 );
818 write!(f, "{}\n{}", msg, error)
819 }
820 else {
821 write!(f, "FOR issue\n{}", error)
822 }
823 },
824
825 AssemblerError::WhileIssue { error, span } => {
826 if span.is_some() {
827 let msg = build_simple_error_message(
828 "WHILE: error in loop",
829 span.as_ref().unwrap(),
830 Severity::Error
831 );
832 write!(f, "{}\n{}", msg, error)
833 }
834 else {
835 write!(f, "WHILE issue\n{}", error)
836 }
837 },
838
839 AssemblerError::OutputProtected { area, address } => {
840 write!(
841 f,
842 "Tentative to write in 0x{:X} in a protected area [0x{:X}:0x{:X}]",
843 address,
844 area.start(),
845 area.end()
846 )
847 },
848 AssemblerError::InvalidSymbol(msg) => {
849 write!(f, "Invalid symbol \"{}\"", msg)
850 },
851 AssemblerError::NoDataToCrunch => {
852 write!(f, "There is no bytes to crunch")
853 },
854 AssemblerError::MMRError { value } => {
855 write!(
856 f,
857 "{} is invalid. We expect values from 0xC0 to 0xc7.",
858 value
859 )
860 },
861 AssemblerError::RelocatedWarning { warning, span } => {
862 let msg =
863 build_simple_error_message(&format!("{}", warning), span, Severity::Warning);
864 write!(f, "{}", msg)
865 },
866 AssemblerError::RelocatedInfo { info, span } => {
867 let msg = build_simple_error_message(&format!("{}", info), span, Severity::Note);
868 write!(f, "{}", msg)
869 },
870 AssemblerError::SnapshotError { error } => write!(f, "Snapshot error. {:#?}", error),
871 AssemblerError::CrunchedSectionError { error } => {
872 write!(f, "Error when crunching code {}", error)
873 },
874 AssemblerError::NotAllowed => write!(f, "Instruction not allowed in this context."),
875 AssemblerError::Fail { msg } => write!(f, "FAIL: {}", msg),
876 AssemblerError::LocatedListingError(arc) => {
877 write!(f, "{}", arc.as_ref().cpclib_error_unchecked())
878 },
879 AssemblerError::AlreadyRenderedError(e) => write!(f, "{}", e)
880 }
881 }
882}
883
884fn build_simple_error_message_with_message(title: &str, message: &str, span: &Z80Span) -> String {
885 let filename = build_filename(span);
886 let source = span.state.complete_source();
887 let offset = span.offset_from_start();
888
889 let mut source_files = SimpleFiles::new();
890 let file = source_files.add(filename, source);
891
892 let sample_range = std::ops::Range {
893 start: offset,
894 end: guess_error_end(
895 source_files.get(file).unwrap().source(),
896 offset,
897 JP_WRONG_PARAM )
899 };
900
901 let diagnostic = Diagnostic::error().with_message(title).with_labels(vec![
902 Label::new(
903 codespan_reporting::diagnostic::LabelStyle::Primary,
904 file,
905 sample_range
906 )
907 .with_message(message),
908 ]);
909
910 let mut writer = buffer();
911 let config = config();
912 term::emit(&mut writer, &config, &source_files, &diagnostic).unwrap();
913
914 std::str::from_utf8(writer.as_slice()).unwrap().to_owned()
915}
916
917#[inline]
918pub fn build_simple_error_message(title: &str, span: &Z80Span, severity: Severity) -> String {
919 let filename = build_filename(span);
920 let source = span.state.complete_source();
921 let offset = span.offset_from_start();
922
923 let mut source_files = SimpleFiles::new();
924 let file = source_files.add(filename, source);
925
926 let end = if title.starts_with("Override ") {
928 span.as_str().chars().count() + offset + 1
930 }
931 else {
932 guess_error_end(
933 source_files.get(file).unwrap().source(),
934 offset,
935 JP_WRONG_PARAM )
937 };
938
939 let sample_range = std::ops::Range { start: offset, end };
940
941 let diagnostic = Diagnostic::new(severity)
942 .with_message(title)
943 .with_labels(vec![Label::new(
944 codespan_reporting::diagnostic::LabelStyle::Primary,
945 file,
946 sample_range
947 )]);
948
949 let mut writer = buffer();
950 let mut config = config();
951 if severity == Severity::Note {
952 config.display_style = DisplayStyle::Short;
953 }
954 term::emit(&mut writer, &config, &source_files, &diagnostic).unwrap();
955
956 std::str::from_utf8(writer.as_slice()).unwrap().to_owned()
957}
958
959#[inline]
960pub fn build_filename(span: &Z80Span) -> Box<String> {
961 Box::new(span.filename().to_owned())
962}
963
964fn build_simple_error_message_with_notes(
965 title: &str,
966 notes: Vec<String>,
967 span: &Z80Span
968) -> String {
969 let filename = build_filename(span);
970 let source = span.state.complete_source();
971 let offset = span.offset_from_start();
972
973 let mut source_files = SimpleFiles::new();
974 let file = source_files.add(filename, source);
975
976 let sample_range = std::ops::Range {
977 start: offset,
978 end: guess_error_end(
979 source_files.get(file).unwrap().source(),
980 offset,
981 JP_WRONG_PARAM )
983 };
984
985 let diagnostic = Diagnostic::error()
986 .with_message(title)
987 .with_labels(vec![Label::new(
988 codespan_reporting::diagnostic::LabelStyle::Primary,
989 file,
990 sample_range
991 )])
992 .with_notes(notes);
993
994 let mut writer = buffer();
995 let config = config();
996 term::emit(&mut writer, &config, &source_files, &diagnostic).unwrap();
997
998 std::str::from_utf8(writer.as_slice()).unwrap().to_owned()
999}
1000
1001fn guess_error_end(code: &str, offset: usize, ctx: &str) -> usize {
1004 enum EndKind {
1005 CommaOrEnd,
1006 End
1007 }
1008
1009 impl EndKind {
1010 fn guess(&self, code: &str, mut offset: usize) -> usize {
1011 match self {
1012 EndKind::End => {
1013 for current in code[offset..].chars() {
1014 if current == ':'
1015 || current == '\n'
1016 || current == ':'
1017 || current == ';'
1018 || offset == code.len()
1019 {
1020 break;
1021 }
1022 offset += 1;
1023 }
1024 offset
1025 },
1026
1027 EndKind::CommaOrEnd => {
1028 for current in code[offset..].chars() {
1029 if current == ','
1030 || current == ':'
1031 || current == '\n'
1032 || current == ':'
1033 || current == ';'
1034 || offset == code.len()
1035 {
1036 break;
1037 }
1038 offset += 1;
1039 }
1040 offset
1041 }
1042 }
1043 }
1044 }
1045 static GUESSER_LUT: LazyLock<HashMap<&'static str, EndKind>> = LazyLock::new(|| {
1046 let mut hash = HashMap::new();
1047
1048 hash.insert(LD_WRONG_DESTINATION, EndKind::CommaOrEnd);
1049
1050 hash
1051 });
1052
1053 let guesser = GUESSER_LUT.get(ctx).unwrap_or(&EndKind::End);
1054
1055 let mut end = guesser.guess(code, offset);
1056 for previous in code[offset..end].chars().rev() {
1058 if previous.is_whitespace() {
1059 end -= 1;
1060 }
1061 else {
1062 break;
1063 }
1064 }
1065 end
1066}
1067
1068fn get_additional_notes(ctx: &str) -> Option<Vec<String>> {
1069 static NOTES_LUT: LazyLock<HashMap<&'static str, Vec<String>>> = LazyLock::new(|| {
1072 let mut hash = HashMap::new();
1073
1074 hash.insert(
1075 LD_WRONG_DESTINATION,
1076 vec![
1077 "Possible destinations are:".to_owned(),
1078 " - 16 bits registers: AF, HL, BC, DE, IX, IY".to_owned(),
1079 " - 8 bits registers: A, B, C, D, E, H, L, IXH, IXL, IYH, IYL, I".to_owned(),
1080 " - addresses: (address), (hl), (de), (bc), (IX+delta), (IY+delta)".to_owned(),
1081 ]
1082 );
1083
1084 hash
1085 });
1086
1087 NOTES_LUT.get(ctx).cloned()
1088}
1089
1090fn buffer() -> Buffer {
1091 if cfg!(feature = "colored_errors") {
1092 Buffer::ansi()
1093 }
1094 else {
1095 Buffer::no_color()
1096 }
1097}
1098
1099fn config() -> codespan_reporting::term::Config {
1100 if cfg!(feature = "colored_errors") {
1101 codespan_reporting::term::Config::default()
1102 }
1103 else {
1104 let mut conf = codespan_reporting::term::Config::default();
1105 conf.chars = Chars::ascii();
1106 conf
1107 }
1108}
1109
1110pub struct SimplerAssemblerError<'e>(pub(crate) &'e AssemblerError);
1111
1112impl Display for SimplerAssemblerError<'_> {
1113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1114 self.0.format(f, false)
1115 }
1116}