|
|
|
use std::{cmp, fmt};
|
|
|
|
|
|
|
|
/// The representation of an s-expression parse error.
|
|
|
|
pub struct Error {
|
|
|
|
/// The error message.
|
|
|
|
pub message: &'static str,
|
|
|
|
/// Position in the source string where the error was detected
|
|
|
|
pub pos: SourcePosition,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Position in the input string
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
|
|
pub struct SourcePosition {
|
|
|
|
/// 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<Error>;
|
|
|
|
|
|
|
|
/// Helps clean up type signatures, but shouldn't be exposed to the outside
|
|
|
|
/// world.
|
|
|
|
pub(crate) type ERes<T> = Result<T, Err>;
|
|
|
|
|
|
|
|
impl fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
|
|
write!(f, "{}:{}: {}", self.pos.line, self.pos.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) -> SourcePosition {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SourcePosition {
|
|
|
|
line,
|
|
|
|
column: cmp::max(col, 0) as usize,
|
|
|
|
index: pos,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cold]
|
|
|
|
fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err {
|
|
|
|
Box::new(Error {
|
|
|
|
message,
|
|
|
|
pos: get_line_and_column(s, *pos)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Build an error with span information
|
|
|
|
pub(crate) fn err<T>(message: &'static str, s: &str, pos: &usize) -> ERes<T> {
|
|
|
|
Err(err_impl(message, s, pos))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Build a span
|
|
|
|
pub(crate) fn spos(s: &str, pos: &usize) -> Option<Box<SourcePosition>> {
|
|
|
|
if *pos >= s.len() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(Box::new(get_line_and_column(s, *pos)))
|
|
|
|
}
|
|
|
|
}
|