Skip to main content

melodium_lang/
error.rs

1//! Provides Mélodium script error management.
2//!
3//! The main type of this module is [ScriptError], which handles most of the management, combined with kind of errors detailed with [ScriptErrorKind].
4
5use crate::text::Kind;
6use crate::text::PositionnedString;
7use crate::text::Word;
8
9use melodium_common::descriptor::Status;
10use melodium_engine::LogicError;
11use std::error::Error;
12use std::fmt::{Display, Formatter};
13use std::sync::Arc;
14
15/// Handles and describe a Mélodium script error.
16///
17/// Most of the properties are deeply related with [Word](super::text::word::Word).
18///
19/// # Note
20/// All positions (`absolute_position`, `line_position`) are expected to be bytes indexes, not chars.
21#[derive(Debug, Clone)]
22pub struct ScriptError {
23    /// Identifier of error.
24    pub id: u32,
25    /// Kind of error.
26    pub kind: ScriptErrorKind,
27}
28
29/// Kind of script error that might happens.
30#[derive(Debug, Clone)]
31pub enum ScriptErrorKind {
32    /// The error is related to a specific word that disable script to work.
33    Word {
34        word: Word,
35        expected: &'static [Kind],
36    },
37    /// The error is about an unexcpected end of script.
38    EndOfScript,
39    DescriptionElementExpected {
40        word: Word,
41        element: Word,
42        expected: &'static [&'static str],
43    },
44    DeclarationExpected {
45        word: Word,
46        expected: &'static [&'static str],
47    },
48    InvalidRoot {
49        text: PositionnedString,
50        root: String,
51    },
52    AlreadyUsedName {
53        text: PositionnedString,
54    },
55    InvalidType {
56        text: PositionnedString,
57    },
58    InvalidStructure {
59        text: PositionnedString,
60    },
61    UnimportedElement {
62        text: PositionnedString,
63    },
64    AlreadyDeclared {
65        text: PositionnedString,
66    },
67    AlreadyAssigned {
68        text: PositionnedString,
69    },
70    MissingType {
71        text: PositionnedString,
72    },
73    MissingValue {
74        text: PositionnedString,
75    },
76    DefaultForbidden {
77        text: PositionnedString,
78    },
79    DefaultForbiddenForGenerics {
80        text: PositionnedString,
81        generic: String,
82    },
83    ConstDeclarationOnly {
84        text: PositionnedString,
85    },
86    FlowForbidden {
87        text: PositionnedString,
88    },
89    StructureForbidden {
90        text: PositionnedString,
91    },
92    TypeForbidden {
93        text: PositionnedString,
94    },
95    ConnectionMustTransmit {
96        from: PositionnedString,
97        to: PositionnedString,
98    },
99    TreatmentNotFound {
100        text: PositionnedString,
101    },
102    NameRequired {
103        text: PositionnedString,
104    },
105    UndeclaredModel {
106        text: PositionnedString,
107    },
108    UndeclaredParameter {
109        text: PositionnedString,
110    },
111    UndeclaredContext {
112        text: PositionnedString,
113    },
114    UndeclaredData {
115        text: PositionnedString,
116    },
117    ReferenceUnset {
118        debug_reference: String,
119    },
120    InvalidBoolean {
121        text: PositionnedString,
122    },
123    InvalidNumber {
124        text: PositionnedString,
125    },
126    InvalidString {
127        text: PositionnedString,
128    },
129    InvalidCharacter {
130        text: PositionnedString,
131    },
132    InvalidByte {
133        text: PositionnedString,
134    },
135    ExecutiveRestitutionFailed {
136        text: PositionnedString,
137        message: String,
138    },
139    MissingFunctionParameter {
140        text: PositionnedString,
141        index: usize,
142    },
143    MissingFunctionGeneric {
144        text: PositionnedString,
145        index: usize,
146    },
147    MissingTreatmentGeneric {
148        text: PositionnedString,
149    },
150    InvalidGeneric {
151        text: PositionnedString,
152    },
153    InvalidTrait {
154        text: PositionnedString,
155    },
156    UnexistingDependency {
157        text: PositionnedString,
158        root: String,
159    },
160    /// The error comes from logic.
161    Logic {
162        error: LogicError,
163    },
164    /// No descriptor associated
165    NoDescriptor {
166        name: PositionnedString,
167    },
168}
169
170impl Display for ScriptErrorKind {
171    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
172        match self {
173            ScriptErrorKind::Word { word, expected } => write!(
174                f,
175                "found '{}' at line {} position {}, expecting {}",
176                word.text, word.position.line_number, word.position.line_position, expected.iter().map(|k| k.to_string()).collect::<Vec<_>>().join(", ")
177            ),
178            ScriptErrorKind::EndOfScript => write!(f, "reached unexpected end of script"),
179            ScriptErrorKind::DescriptionElementExpected {
180                word,
181                element,
182                expected,
183            } => write!(
184                f,
185                "for '{}' at line {} position {}, '{}' is not an expected element; {} are admitted",
186                element.text,
187                word.position.line_number,
188                word.position.line_position,
189                word.text,
190                expected
191                    .iter()
192                    .map(|s| format!("'{s}'"))
193                    .collect::<Vec<_>>()
194                    .join(", ")
195            ),
196            ScriptErrorKind::DeclarationExpected {
197                word,
198                expected,
199            } => write!(
200                f,
201                "found '{}' at line {} position {} while a declaration is expected; {} are admitted",
202                word.text,
203                word.position.line_number,
204                word.position.line_position,
205                expected
206                    .iter()
207                    .map(|s| format!("'{s}'"))
208                    .collect::<Vec<_>>()
209                    .join(", ")
210            ),
211            ScriptErrorKind::InvalidRoot { text, root } => write!(f, "at line {} position {} '{root}' is not a valid root", text.position.line_number, text.position.line_position),
212            ScriptErrorKind::AlreadyUsedName { text } => write!(f, "at line {} position {} '{}' is already used as name", text.position.line_number, text.position.line_position, text.string),
213            ScriptErrorKind::InvalidType { text } => write!(f, "at line {} position {} '{}' is not a valid type", text.position.line_number, text.position.line_position, text.string),
214            ScriptErrorKind::InvalidStructure { text } => write!(f, "at line {} position {} '{}' is not a valid structure", text.position.line_number, text.position.line_position, text.string),
215            ScriptErrorKind::UnimportedElement { text } => write!(f, "at line {} position {} element '{}' is not imported nor locally available", text.position.line_number, text.position.line_position, text.string),
216            ScriptErrorKind::AlreadyDeclared { text } => write!(f, "at line {} position {} '{}' is already declared", text.position.line_number, text.position.line_position, text.string),
217            ScriptErrorKind::AlreadyAssigned { text } => write!(f, "at line {} position {} '{}' is already assigned", text.position.line_number, text.position.line_position, text.string),
218            ScriptErrorKind::MissingType { text } => write!(f, "at line {} position {} type for '{}' is missing", text.position.line_number, text.position.line_position, text.string),
219            ScriptErrorKind::MissingValue { text } => write!(f, "at line {} position {} value for '{}' is missing", text.position.line_number, text.position.line_position, text.string),
220            ScriptErrorKind::DefaultForbidden { text } => write!(f, "at line {} position {} '{}' cannot have default value", text.position.line_number, text.position.line_position, text.string),
221            ScriptErrorKind::DefaultForbiddenForGenerics { text , generic} => write!(f, "at line {} position {} '{}' cannot have default value because '{}' is treated as generic", text.position.line_number, text.position.line_position, text.string, generic),
222            ScriptErrorKind::ConstDeclarationOnly { text } => write!(f, "at line {} position {} '{}' cannot be otherwise than 'const'", text.position.line_number, text.position.line_position, text.string),
223            ScriptErrorKind::FlowForbidden { text } => write!(f, "at line {} position {} '{}' cannot have flow specification", text.position.line_number, text.position.line_position, text.string),
224            ScriptErrorKind::StructureForbidden { text } => write!(f, "at line {} position {} '{}' cannot have structure specification", text.position.line_number, text.position.line_position, text.string),
225            ScriptErrorKind::TypeForbidden { text } => write!(f, "at line {} position {} '{}' cannot have type specification", text.position.line_number, text.position.line_position, text.string),
226            ScriptErrorKind::ConnectionMustTransmit { from, to } => write!(f, "at line {} position {}, connection from '{}' to '{}' must transmit data", from.position.line_number, from.position.line_position, from.string, to.string),
227            ScriptErrorKind::TreatmentNotFound { text } => write!(f, "at line {} position {} cannot find treatment '{}'", text.position.line_number, text.position.line_position, text.string),
228            ScriptErrorKind::NameRequired { text } => write!(f, "at line {} position {} a name is required for assignation to '{}'", text.position.line_number, text.position.line_position, text.string),
229            ScriptErrorKind::UndeclaredModel { text } => write!(f, "at line {} position {} no model declared as '{}'", text.position.line_number, text.position.line_position, text.string),
230            ScriptErrorKind::UndeclaredParameter { text } => write!(f, "at line {} position {} no parameter declared as '{}'", text.position.line_number, text.position.line_position, text.string),
231            ScriptErrorKind::UndeclaredContext { text } => write!(f, "at line {} position {} no context declared as '{}'", text.position.line_number, text.position.line_position, text.string),
232            ScriptErrorKind::UndeclaredData { text } => write!(f, "at line {} position {} no data type declared as '{}'", text.position.line_number, text.position.line_position, text.string),
233            ScriptErrorKind::ReferenceUnset { debug_reference } => write!(f, "reference is not set, this is an internal error: '{debug_reference}'"),
234            ScriptErrorKind::InvalidBoolean { text } => write!(f, "at line {} position {} '{}' is not a boolean value", text.position.line_number, text.position.line_position, text.string),
235            ScriptErrorKind::InvalidNumber { text } => write!(f, "at line {} position {} '{}' is not a numeric value", text.position.line_number, text.position.line_position, text.string),
236            ScriptErrorKind::InvalidString { text } => write!(f, "at line {} position {} '{}' is not a correctly formatted string", text.position.line_number, text.position.line_position, text.string),
237            ScriptErrorKind::InvalidCharacter { text } => write!(f, "at line {} position {} '{}' is not a valid character", text.position.line_number, text.position.line_position, text.string),
238            ScriptErrorKind::InvalidByte { text } => write!(f, "at line {} position {} '{}' is not a valid byte", text.position.line_number, text.position.line_position, text.string),
239            ScriptErrorKind::ExecutiveRestitutionFailed { text, message } => write!(f, "at line {} position {} error occured with value '{}' because: {}", text.position.line_number, text.position.line_position, text.string, message),
240            ScriptErrorKind::MissingFunctionParameter { text, index } => write!(f, "at line {} position {} for function '{}' parameter is missing at position {index}", text.position.line_number, text.position.line_position, text.string),
241            ScriptErrorKind::MissingFunctionGeneric { text, index } => write!(f, "at line {} position {} for function '{}' generic is missing at position {index}", text.position.line_number, text.position.line_position, text.string),
242            ScriptErrorKind::MissingTreatmentGeneric { text } => write!(f, "at line {} position {} for treatment '{}' generic is missing", text.position.line_number, text.position.line_position, text.string),
243            ScriptErrorKind::InvalidGeneric { text } => write!(f, "at line {} position {} '{}' is not a valid generic name", text.position.line_number, text.position.line_position, text.string),
244            ScriptErrorKind::InvalidTrait { text } => write!(f, "at line {} position {} '{}' is not a valid trait", text.position.line_number, text.position.line_position, text.string),
245            ScriptErrorKind::UnexistingDependency { text, root } => write!(f, "at line {} position {} '{root}' is not a dependency", text.position.line_number, text.position.line_position),
246            ScriptErrorKind::Logic { error } => {
247                if let Some(ps) = error
248                    .design_reference
249                    .as_ref().and_then(|ptr| Arc::clone(ptr).downcast_arc::<PositionnedString>().ok())
250                {
251                    write!(
252                        f,
253                        "at line {} position {} '{}': {}",
254                        ps.position.line_number, ps.position.line_position, ps.string, error
255                    )
256                } else {
257                    write!(f, "{}", error)
258                }
259            }
260            ScriptErrorKind::NoDescriptor { name } => write!(
261                f,
262                "no descriptor available for '{}' line {} position {}, this is an internal error",
263                name.string, name.position.line_number, name.position.line_position
264            ),
265        }
266    }
267}
268
269impl ScriptError {
270    /// Creates a new error of Word kind.
271    ///
272    /// The ScriptError created that way will be of [ScriptErrorKind::Word] kind.
273    /// Each parameter matches the properties of ScriptError.
274    pub fn word(id: u32, word: Word, expected: &'static [Kind]) -> Self {
275        Self {
276            id,
277            kind: ScriptErrorKind::Word { word, expected },
278        }
279    }
280
281    /// Creates a new error of EndOfScript kind.
282    ///
283    /// The ScriptError created that way will be of [ScriptErrorKind::EndOfScript] kind.
284    pub fn end_of_script(id: u32) -> Self {
285        Self {
286            id,
287            kind: ScriptErrorKind::EndOfScript,
288        }
289    }
290
291    pub fn description_element_expected(
292        id: u32,
293        word: Word,
294        element: Word,
295        expected: &'static [&'static str],
296    ) -> Self {
297        Self {
298            id,
299            kind: ScriptErrorKind::DescriptionElementExpected {
300                word,
301                element,
302                expected,
303            },
304        }
305    }
306
307    pub fn declaration_expected(id: u32, word: Word, expected: &'static [&'static str]) -> Self {
308        Self {
309            id,
310            kind: ScriptErrorKind::DeclarationExpected { word, expected },
311        }
312    }
313
314    pub fn invalid_root(id: u32, text: PositionnedString, root: String) -> Self {
315        Self {
316            id,
317            kind: ScriptErrorKind::InvalidRoot { text, root },
318        }
319    }
320
321    pub fn already_used_name(id: u32, text: PositionnedString) -> Self {
322        Self {
323            id,
324            kind: ScriptErrorKind::AlreadyUsedName { text },
325        }
326    }
327
328    pub fn invalid_type(id: u32, text: PositionnedString) -> Self {
329        Self {
330            id,
331            kind: ScriptErrorKind::InvalidType { text },
332        }
333    }
334
335    pub fn invalid_structure(id: u32, text: PositionnedString) -> Self {
336        Self {
337            id,
338            kind: ScriptErrorKind::InvalidStructure { text },
339        }
340    }
341
342    pub fn unimported_element(id: u32, text: PositionnedString) -> Self {
343        Self {
344            id,
345            kind: ScriptErrorKind::UnimportedElement { text },
346        }
347    }
348
349    pub fn already_declared(id: u32, text: PositionnedString) -> Self {
350        Self {
351            id,
352            kind: ScriptErrorKind::AlreadyDeclared { text },
353        }
354    }
355
356    pub fn already_assigned(id: u32, text: PositionnedString) -> Self {
357        Self {
358            id,
359            kind: ScriptErrorKind::AlreadyAssigned { text },
360        }
361    }
362
363    pub fn missing_type(id: u32, text: PositionnedString) -> Self {
364        Self {
365            id,
366            kind: ScriptErrorKind::MissingType { text },
367        }
368    }
369
370    pub fn missing_value(id: u32, text: PositionnedString) -> Self {
371        Self {
372            id,
373            kind: ScriptErrorKind::MissingValue { text },
374        }
375    }
376
377    pub fn default_forbidden(id: u32, text: PositionnedString) -> Self {
378        Self {
379            id,
380            kind: ScriptErrorKind::DefaultForbidden { text },
381        }
382    }
383
384    pub fn default_forbidden_for_generics(
385        id: u32,
386        text: PositionnedString,
387        generic: String,
388    ) -> Self {
389        Self {
390            id,
391            kind: ScriptErrorKind::DefaultForbiddenForGenerics { text, generic },
392        }
393    }
394
395    pub fn const_declaration_only(id: u32, text: PositionnedString) -> Self {
396        Self {
397            id,
398            kind: ScriptErrorKind::ConstDeclarationOnly { text },
399        }
400    }
401
402    pub fn flow_forbidden(id: u32, text: PositionnedString) -> Self {
403        Self {
404            id,
405            kind: ScriptErrorKind::FlowForbidden { text },
406        }
407    }
408
409    pub fn structure_forbidden(id: u32, text: PositionnedString) -> Self {
410        Self {
411            id,
412            kind: ScriptErrorKind::StructureForbidden { text },
413        }
414    }
415
416    pub fn type_forbidden(id: u32, text: PositionnedString) -> Self {
417        Self {
418            id,
419            kind: ScriptErrorKind::TypeForbidden { text },
420        }
421    }
422
423    pub fn connection_must_transmit_data(
424        id: u32,
425        from: PositionnedString,
426        to: PositionnedString,
427    ) -> Self {
428        Self {
429            id,
430            kind: ScriptErrorKind::ConnectionMustTransmit { from, to },
431        }
432    }
433
434    pub fn treatment_not_found(id: u32, text: PositionnedString) -> Self {
435        Self {
436            id,
437            kind: ScriptErrorKind::TreatmentNotFound { text },
438        }
439    }
440
441    pub fn name_required(id: u32, text: PositionnedString) -> Self {
442        Self {
443            id,
444            kind: ScriptErrorKind::NameRequired { text },
445        }
446    }
447
448    pub fn undeclared_model(id: u32, text: PositionnedString) -> Self {
449        Self {
450            id,
451            kind: ScriptErrorKind::UndeclaredModel { text },
452        }
453    }
454
455    pub fn undeclared_parameter(id: u32, text: PositionnedString) -> Self {
456        Self {
457            id,
458            kind: ScriptErrorKind::UndeclaredParameter { text },
459        }
460    }
461
462    pub fn undeclared_context(id: u32, text: PositionnedString) -> Self {
463        Self {
464            id,
465            kind: ScriptErrorKind::UndeclaredContext { text },
466        }
467    }
468
469    pub fn undeclared_data(id: u32, text: PositionnedString) -> Self {
470        Self {
471            id,
472            kind: ScriptErrorKind::UndeclaredData { text },
473        }
474    }
475
476    pub fn reference_unset(id: u32, debug_reference: String) -> Self {
477        Self {
478            id,
479            kind: ScriptErrorKind::ReferenceUnset { debug_reference },
480        }
481    }
482
483    pub fn invalid_boolean(id: u32, text: PositionnedString) -> Self {
484        Self {
485            id,
486            kind: ScriptErrorKind::InvalidBoolean { text },
487        }
488    }
489
490    pub fn invalid_number(id: u32, text: PositionnedString) -> Self {
491        Self {
492            id,
493            kind: ScriptErrorKind::InvalidNumber { text },
494        }
495    }
496
497    pub fn invalid_string(id: u32, text: PositionnedString) -> Self {
498        Self {
499            id,
500            kind: ScriptErrorKind::InvalidString { text },
501        }
502    }
503
504    pub fn invalid_character(id: u32, text: PositionnedString) -> Self {
505        Self {
506            id,
507            kind: ScriptErrorKind::InvalidCharacter { text },
508        }
509    }
510
511    pub fn invalid_byte(id: u32, text: PositionnedString) -> Self {
512        Self {
513            id,
514            kind: ScriptErrorKind::InvalidByte { text },
515        }
516    }
517
518    pub fn executive_restitution_failed(id: u32, text: PositionnedString, message: String) -> Self {
519        Self {
520            id,
521            kind: ScriptErrorKind::ExecutiveRestitutionFailed { text, message },
522        }
523    }
524
525    pub fn missing_function_parameter(id: u32, text: PositionnedString, index: usize) -> Self {
526        Self {
527            id,
528            kind: ScriptErrorKind::MissingFunctionParameter { text, index },
529        }
530    }
531
532    pub fn missing_function_generic(id: u32, text: PositionnedString, index: usize) -> Self {
533        Self {
534            id,
535            kind: ScriptErrorKind::MissingFunctionGeneric { text, index },
536        }
537    }
538
539    pub fn missing_treatment_generic(id: u32, text: PositionnedString) -> Self {
540        Self {
541            id,
542            kind: ScriptErrorKind::MissingTreatmentGeneric { text },
543        }
544    }
545
546    pub fn invalid_generic(id: u32, text: PositionnedString) -> Self {
547        Self {
548            id,
549            kind: ScriptErrorKind::InvalidGeneric { text },
550        }
551    }
552
553    pub fn invalid_trait(id: u32, text: PositionnedString) -> Self {
554        Self {
555            id,
556            kind: ScriptErrorKind::InvalidTrait { text },
557        }
558    }
559
560    pub fn unexisting_dependency(id: u32, text: PositionnedString, root: String) -> Self {
561        Self {
562            id,
563            kind: ScriptErrorKind::UnexistingDependency { text, root },
564        }
565    }
566
567    pub fn logic(id: u32, error: LogicError) -> Self {
568        Self {
569            id,
570            kind: ScriptErrorKind::Logic { error },
571        }
572    }
573
574    pub fn no_descriptor(id: u32, name: PositionnedString) -> Self {
575        Self {
576            id,
577            kind: ScriptErrorKind::NoDescriptor { name },
578        }
579    }
580}
581
582impl Display for ScriptError {
583    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
584        write!(f, "S{:04}: {}", self.id, self.kind)
585    }
586}
587
588impl From<LogicError> for ScriptError {
589    fn from(le: LogicError) -> Self {
590        ScriptError::logic(0, le)
591    }
592}
593
594impl Error for ScriptError {
595    fn source(&self) -> Option<&(dyn Error + 'static)> {
596        // Generic error, underlying cause isn't tracked.
597        None
598    }
599}
600
601pub type ScriptErrors = Vec<ScriptError>;
602pub type ScriptResult<T> = Status<T, ScriptError, ScriptError>;
603
604impl From<ScriptError> for ScriptErrors {
605    fn from(value: ScriptError) -> Self {
606        vec![value]
607    }
608}