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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::error::Error;
use std::fmt;

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ParseError<L,T,E> {
    /// Generated by the parser when it encounters a token (or EOF) it did not
    /// expect.
    InvalidToken {
        location: L
    },

    /// Generated by the parser when it encounters a token (or EOF) it did not
    /// expect.
    UnrecognizedToken {
        /// If this is `Some`, then an unexpected token of type `T`
        /// was observed, with a span given by the two `L` values. If
        /// this is `None`, then EOF was observed when it was not
        /// expected.
        token: Option<(L, T, L)>,

        /// The set of expected tokens: these names are taken from the
        /// grammar and hence may not necessarily be suitable for
        /// presenting to the user.
        expected: Vec<String>
    },

    /// Generated by the parser when it encounters additional,
    /// unexpected tokens.
    ExtraToken {
        token: (L, T, L),
    },

    /// Custom error type.
    User {
        error: E,
    },
}

impl<L, T, E> ParseError<L, T, E> {
    fn map_intern<FL,LL,FT,TT,FE,EE>(self, loc_op: FL, tok_op: FT, err_op: FE) -> ParseError<LL,TT,EE>
    where FL: Fn(L) -> LL,
          FT: Fn(T) -> TT,
          FE: Fn(E) -> EE
    {
        let maptok = |(s,t,e) : (L,T,L)| (loc_op(s), tok_op(t), loc_op(e));
        match self {
            ParseError::InvalidToken { location } => ParseError::InvalidToken { location: loc_op(location) },
            ParseError::UnrecognizedToken { token, expected } => ParseError::UnrecognizedToken { token: token.map(maptok), expected: expected },
            ParseError::ExtraToken { token } => ParseError::ExtraToken { token: maptok(token) },
            ParseError::User { error } => ParseError::User { error: err_op(error) }
        }
    }

    pub fn map_location<F,LL>(self, op: F) -> ParseError<LL, T, E> where F: Fn(L) -> LL {
        self.map_intern(op, |x|x, |x|x)
    }

    pub fn map_token<F,TT>(self, op: F) -> ParseError<L, TT, E> where F: Fn(T) -> TT {
        self.map_intern(|x|x, op, |x|x)
    }

    pub fn map_error<F,EE>(self, op: F) -> ParseError<L, T, EE> where F: Fn(E) -> EE {
        self.map_intern(|x|x, |x|x, op)
    }
}

impl<L, T, E> fmt::Display for ParseError<L, T, E>
where L: fmt::Display,
      T: fmt::Display,
      E: fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ParseError::*;
        match *self {
            InvalidToken { ref location } =>
                write!(f, "Invalid token at {}", location),
            UnrecognizedToken { ref token, ref expected } => {
                match *token {
                    Some((ref start, ref token, ref end)) =>
                        try!(write!(f, "Unrecognized token `{}` found at {}:{}", token, start, end)),
                    None =>
                        try!(write!(f, "Unrecognized EOF")),
                }
                if !expected.is_empty() {
                    try!(writeln!(f, ""));
                    for (i, e) in expected.iter().enumerate() {
                        let sep = match i {
                            0 => "Expected one of",
                            _ if i < expected.len() - 1 => ",",
                            // Last expected message to be written
                            _ => " or",
                        };
                        try!(write!(f, "{} {}", sep, e));
                    }
                }
                Ok(())
            }
            ExtraToken { token: (ref start, ref token, ref end) } => {
                write!(f, "Extra token {} found at {}:{}", token, start, end)
            }
            User { ref error } =>
                write!(f, "{}", error)
        }
    }
}

impl<L, T, E> Error for ParseError<L, T, E>
where L: fmt::Debug + fmt::Display,
      T: fmt::Debug + fmt::Display,
      E: fmt::Debug + fmt::Display
{
    fn description(&self) -> &str {
        "parse error"
    }
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ErrorRecovery<L, T, E> {
    pub error: ParseError<L, T, E>,
    pub dropped_tokens: Vec<(L, T, L)>,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test() {
        let err = ParseError::UnrecognizedToken::<i32, &str, &str> {
            token: Some((1, "t0", 2)),
            expected: vec!["t1", "t2", "t3"]
                .into_iter()
                .map(|s| s.to_string())
                .collect()
        };
        assert_eq!(format!("{}", err), "Unrecognized token `t0` found at 1:2\n\
                                        Expected one of t1, t2 or t3");
    }
}