cpclib_asm/
error.rs

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) // expected index
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
34#[allow(missing_docs)]
35pub enum AssemblerError {
36    /// Dirty trick to not play with memory
37    AlreadyRenderedError(String),
38
39    /// Parse of a located listing failed, but the error is in fact stored within the located listing object...
40    LocatedListingError(std::sync::Arc<LocatedListing>),
41
42    //#[fail(display = "Several errors arised: {:?}", errors)]
43    MultipleErrors {
44        errors: Vec<AssemblerError>
45    },
46
47    //#[fail(display = "{} cannot be empty.", 0)]
48    EmptyBinaryFile(String),
49
50    //#[fail(display = "Amsdos error: {}", error)]
51    AmsdosError {
52        error: AmsdosError
53    },
54
55    //#[fail(display = "Assembling bug: {}", msg)]
56    BugInAssembler {
57        file: &'static str,
58        line: u32,
59        msg: String
60    },
61
62    //#[fail(display = "Parser bug: {}. Context: {:?}", error, context)]
63    BugInParser {
64        error: String,
65        context: ParserContext
66    },
67
68    // TODO add more information
69    //#[fail(display = "Syntax error:\n{}", error)]
70    SyntaxError {
71        error: Z80ParserError
72    },
73
74    IncludedFileError {
75        span: Z80Span,
76        error: Box<AssemblerError>
77    },
78
79    //#[fail(display = "Basic error: {}", error)]
80    BasicError {
81        error: BasicError
82    },
83
84    DisassemblerError {
85        msg: String
86    },
87
88    // TODO add more information
89    // #[fail(display = "Assembling error: {}", msg)]
90    AssemblingError {
91        msg: String
92    },
93
94    // #[fail(display = "Invalid argument: {}", msg)]
95    InvalidArgument {
96        msg: String
97    },
98
99    Fail {
100        msg: String
101    },
102
103    //  #[fail(display = "Assertion failed -- {} [{}]: {}", test, guidance, msg)]
104    AssertionFailed {
105        test: String,
106        msg: String,
107        guidance: String
108    },
109
110    //  #[fail(display = "Symbol `{}` already present on the symbol table", symbol)]
111    SymbolAlreadyExists {
112        symbol: String
113    },
114
115    CounterAlreadyExists {
116        symbol: String
117    },
118
119    IncoherentCode {
120        msg: String
121    },
122
123    //    #[fail(
124    //        display = "There is no macro named `{}`. Closest one is: {:?}",
125    //        symbol, closest
126    //    )]
127    UnknownMacro {
128        symbol: SmolStr,
129        closest: Option<SmolStr>
130    },
131
132    //    #[fail(display = "Error when applying macro {}. {}", name, root)]
133    MacroError {
134        name: SmolStr,
135        location: Option<Source>,
136        root: Box<AssemblerError>
137    },
138
139    //   #[fail(
140    //       display = "Macro `{}` expect {} arguments; {} are provided.",
141    //       symbol, nb_arguments, nb_paramers
142    //   )]
143    WrongNumberOfParameters {
144        symbol: String,
145        nb_paramers: usize,
146        nb_arguments: usize
147    },
148
149    //  #[fail(display = "Unknown symbol: {}. Closest one is: {:?}", symbol, closest)]
150    UnknownSymbol {
151        symbol: SmolStr,
152        closest: Option<SmolStr>
153    },
154
155    InvalidSymbol(SmolStr),
156
157    //   #[fail(display = "Symbol {} is not a {}", symbol, isnot)]
158    WrongSymbolType {
159        symbol: SmolStr,
160        isnot: SmolStr
161    },
162
163    // TODO add symbol type
164    AlreadyDefinedSymbol {
165        symbol: SmolStr,
166        kind: SmolStr,
167        here: Option<Source>
168    },
169
170    //   #[fail(display = "IO error: {}", msg)]
171    IOError {
172        msg: String
173    },
174
175    //  #[fail(display = "Current assembling address is unknown.")]
176    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    //  #[fail(display = "Unable to resolve expression {}.", expression)]
192    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    /// Several errors has been generated without span information.
209    /// RelocatedError allows them to be approximately located
210    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    /// Returns true only for errors already located
317    pub fn is_located(&self) -> bool {
318        match self {
319            AssemblerError::RelocatedError{..} |
320            AssemblerError::RelocatedWarning{..} /*|
321            AssemblerError::SyntaxError{..} */ => true, // we need to exclude syntax error to add the location from the assembler when using macros
322            _ => 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                            //  Z80ParserErrorKind::Nom(ErrorKind::Eof) => true,
406                            _ => true
407                        }
408                    })
409                    .map(|e| {
410                        match e.1 {
411                            Z80ParserErrorKind::Context(_)
412                            | Z80ParserErrorKind::Winnow
413                            | Z80ParserErrorKind::Char(_) => {
414                                // Get the real are build the context
415                                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                                // Add filename to database if needed
428                                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            // By construction contains only error with no span information
679            AssemblerError::RelocatedError { error, span } => {
680                if complete {
681                    // Relocated error format may vary among errors
682                    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 // fake value
898        )
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    // TODO do it in a cleaner way. Here it is an ugly path !!!
927    let end = if title.starts_with("Override ") {
928        // XXX Handle the case of memory overriding that can use lots of instructions
929        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 // fake value
936        )
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 // fake value
982        )
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
1001/// The parser is unable to provide the end of the error.
1002/// This function tries to get it
1003fn 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    // remove whitespace from selection
1057    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    // phf is not currently usable
1070
1071    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}