1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*
 * mini-haskell: light-weight Haskell for fun
 * Copyright (C) 2020  Xie Ruifeng
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

//! error reporting for the mini-Haskell compiler.

use num_bigint::BigInt;
use crate::lexeme::LexemeType;
use crate::scanner::{LexError, Location, Range};

/// An exhaustive list of compiler errors.
#[derive(Debug)]
pub enum Error {
    /// An invalid UTF-8 sequence.
    InvalidUTF8(Vec<u8>),
    /// A failure during the input process.
    InputFailure(std::io::Error),
    /// A Unicode character not accepted by the Haskell language.
    InvalidChar(char),
    /// An error during the tokenization process.
    InvalidToken(LexError),
    /// A lexeme ended prematurely, e.g. EOF in a block comment.
    IncompleteLexeme(LexemeType),
    /// A float literal is too large (or small) to represent.
    ///
    /// **Note**:
    ///
    /// - maximum value for IEEE 754 64-bit double is approximately 1.8e308;
    /// - to prevent loss of precision, maximum value to store in IEEE 754 64-bit double is 2^53;
    /// - `Rational` with an exponent 4096 takes approximately 13.3KiB to store;
    /// - large float literals may eventually exhaust the usable memory of the compiler;
    /// - `Rational` is probably not a good representation for large floats;
    FloatOutOfBound(BigInt),
    /// A character/string literal contains a Unicode character out of bound.
    CharOutOfBound(BigInt),
}

/// A diagnostic message (body).
#[derive(Debug)]
pub enum DiagnosticMessage {
    /// Critical errors.
    Error(Error),
}

/// A diagnostic, with a source location, and an optional source range.
#[derive(Debug)]
pub struct Diagnostic {
    location: Location,
    range: Option<Range>,
    message: DiagnosticMessage,
}

impl Diagnostic {
    /// Create a new diagnostics.
    pub fn new(location: Location, message: DiagnosticMessage) -> Diagnostic {
        Diagnostic { location, message, range: None }
    }

    /// Add a source range to the report.
    pub fn within_range(self, range: Range) -> Self {
        Self { range: Some(range), ..self }
    }

    /// Add a source range from a `[begin, end)` pair to the report.
    pub fn within(self, begin: Location, end: Location) -> Self {
        Self { range: Some(Range { begin, end }), ..self }
    }

    /// Report to the diagnostics engine.
    pub fn report(self, engine: &mut DiagnosticsEngine) {
        engine.push(self)
    }
}

/// The diagnostics engine.
pub type DiagnosticsEngine = Vec<Diagnostic>;