Croissant Runtime
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
crsn/crsn/crsn-sexp/src/error.rs

108 lines
2.9 KiB

use std::{cmp, fmt};
use std::fmt::{Formatter, Debug};
/// 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(PartialEq, Clone, Default)]
pub struct SourcePosition {
/// The line number on which the error occurred.
pub line: u32,
/// The column number on which the error occurred.
pub column: u32,
/// The index in the given string which caused the error.
pub index: u32,
}
impl fmt::Display for SourcePosition {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}:{}", self.line, self.column)
}
}
impl Debug for SourcePosition {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_struct("SourcePosition")
.field("line", &self.line)
.field("column", &self.column)
.field("index", &self.index)
.finish()
} else {
// shorter version
write!(f, "Pos({}:{})", self.line, self.column)
}
}
}
/// 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: line as u32,
column: cmp::max(col, 0) as u32,
index: pos as u32,
}
}
#[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) -> SourcePosition {
if pos >= s.len() {
Default::default()
} else {
get_line_and_column(s, pos)
}
}