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
use std::fmt;

/// Error type used by the tokenizer and the parser internally.
#[derive(Debug)]
pub enum Error {
    UnexpectedEndOfProgram,
    UnexpectedToken {
        start: usize,
        end: usize,
    },
}

/// Error type returned by `parser::parse`. This error will include
/// owned `String` of the source code where the error occured, so
/// that a meaningful error can be printed out.
pub enum ParseError {
    UnexpectedEndOfProgram,
    UnexpectedToken {
        source: String,
        start: usize,
        end: usize,
    },
}

impl fmt::Debug for ParseError {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

impl fmt::Display for ParseError {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParseError::UnexpectedEndOfProgram => {
                try!(write!(f, "Unexpected end of program"))
            },

            ParseError::UnexpectedToken {
                ref source,
                start,
                end
            } => {
                let (lineno, line) = source[..start]
                                       .lines()
                                       .enumerate()
                                       .last()
                                       .unwrap_or((0, ""));

                let colno = line.chars().count();
                let token_len = source[start..end].chars().count();

                try!(writeln!(f, "Unexpected token at {}:{}\n", lineno + 1, colno + 1));

                let iter = source
                            .lines()
                            .enumerate()
                            .skip_while(|&(index, _)| index < lineno.saturating_sub(2))
                            .take_while(|&(index, _)| index < lineno + 3);

                let width = log10(lineno + 3);

                for (index, line) in iter {
                    if index == lineno {
                        try!(writeln!(f, "> {0:1$} | {2}", index+1, width, line));

                        for _ in 0..width {
                            try!(write!(f, " "));
                        }

                        try!(write!(f, "   | "));

                        for _ in 0..colno {
                            try!(write!(f, " "));
                        }

                        for _ in 0..token_len {
                            try!(write!(f, "^"));
                        }

                        try!(write!(f, "\n"));
                    } else {
                        try!(writeln!(f, "{0:1$} | {2}", index+1, width+2, line));
                    }
                }

            },
        }

        Ok(())
    }
}

fn log10(mut num: usize) -> usize {
    let mut log = 0;

    while num > 0 {
        log += 1;
        num /= 10;
    }

    log
}

pub type Result<T> = ::std::result::Result<T, Error>;

pub type ParseResult<T> = ::std::result::Result<T, ParseError>;