use std::{cmp, fmt}; /// The representation of an s-expression parse error. pub struct Error { /// The error message. pub message: &'static str, /// The line number on which the error occurred. pub line: usize, /// The column number on which the error occurred. pub column: usize, /// The index in the given string which caused the error. pub index: usize, } /// Since errors are the uncommon case, they're boxed. This keeps the size of /// structs down, which helps performance in the common case. /// /// For example, an `ERes<()>` becomes 8 bytes, instead of the 24 bytes it would /// be if `Err` were unboxed. type Err = Box; /// Helps clean up type signatures, but shouldn't be exposed to the outside /// world. pub(crate) type ERes = Result; impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{}:{}: {}", self.line, self.column, self.message) } } impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{}", self) } } impl std::error::Error for Error {} pub(crate) fn get_line_and_column(s: &str, pos: usize) -> (usize, usize) { let mut line: usize = 1; let mut col: isize = -1; for c in s.chars().take(pos + 1) { if c == '\n' { line += 1; col = -1; } else { col += 1; } } (line, cmp::max(col, 0) as usize) } #[cold] fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err { let (line, column) = get_line_and_column(s, *pos); Box::new(Error { message: message, line: line, column: column, index: *pos, }) } /// Build an error with span information pub(crate) fn err(message: &'static str, s: &str, pos: &usize) -> ERes { Err(err_impl(message, s, pos)) }