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
91
92
93
94
95
96
97
98
99
100
101
102
103
use lalrpop_util::lexer::Token;
use lalrpop_util::ParseError;
use line_col::LineColLookup;

use crate::parser::Position;

pub enum RlError {
    File {
        filename: String,
        message: String,
    },
    Parse {
        message: String,
        position: Option<Position>,
        expected: Vec<String>,
    },
    Duplicate {
        name: String,
        first: Option<Position>,
        second: Option<Position>,
    },
    Resolve {
        element: String,
        position: Option<Position>,
    },
    Other(String),
}

impl RlError {
    pub fn new_parse(
        file: &str,
        lookup: &LineColLookup,
        error: ParseError<usize, Token, &str>,
    ) -> Self {
        match error {
            ParseError::InvalidToken { location } => Self::Parse {
                message: "invalid token".into(),
                position: Some(Position::new(file, lookup, location)),
                expected: Vec::new(),
            },
            ParseError::UnrecognizedEOF { location, expected } => Self::Parse {
                message: "unreconized EOF".into(),
                position: Some(Position::new(file, lookup, location)),
                expected,
            },
            ParseError::UnrecognizedToken { token, expected } => Self::Parse {
                message: format!("unreconized token '{}'", token.1),
                position: Some(Position::new(file, lookup, token.0)),
                expected,
            },
            ParseError::ExtraToken { token } => Self::Parse {
                message: format!("extra token '{}'", token.1),
                position: Some(Position::new(file, lookup, token.0)),
                expected: Vec::new(),
            },
            ParseError::User { error } => Self::Parse {
                message: format!("parse error '{}'", error),
                position: None,
                expected: Vec::new(),
            },
        }
    }
}

impl std::fmt::Display for RlError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            RlError::File { filename, message } => {
                write!(f, "cannot read file {} {}", filename, message)
            }
            RlError::Parse {
                message,
                position,
                expected,
            } => match position {
                Some(position) => write!(
                    f,
                    "parse error '{}' at {}, expecting: {:?}",
                    message, position, expected
                ),
                None => write!(f, "parse error '{}', expecting: {:?}", message, expected),
            },
            RlError::Other(msg) => write!(f, "error: {}", msg),
            RlError::Resolve { element, position } => {
                if let Some(position) = position {
                    write!(f, "unresolved {} at {}", element, position)
                } else {
                    write!(f, "unresolved {}", element)
                }
            }
            RlError::Duplicate {
                name,
                first,
                second,
            } => match (first, second) {
                (None, None) => write!(f, "duplicate '{}'", name),
                (None, Some(p)) => write!(f, "duplicate '{}' at {}", name, p),
                (Some(p), None) => write!(f, "duplicate '{}' at {}", name, p),
                (Some(p1), Some(p2)) => write!(f, "duplicate '{}' at {} and {}", name, p1, p2),
            },
        }
    }
}