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
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> 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");
    }
}