#![forbid(unsafe_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
pub fn new(start: usize, end: usize) -> Self {
Self { start, end }
}
pub fn len(&self) -> usize {
self.end.saturating_sub(self.start)
}
pub fn is_empty(&self) -> bool {
self.end <= self.start
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Spanned<T> {
pub value: T,
pub span: Span,
}
impl<T> Spanned<T> {
pub fn new(value: T, span: Span) -> Self {
Self { value, span }
}
}
#[cfg(feature = "native-parser")]
pub(crate) struct LineTable {
line_starts: Vec<usize>,
}
#[cfg(feature = "native-parser")]
impl LineTable {
pub fn build(src: &str) -> Self {
let mut line_starts = vec![0usize];
for (i, b) in src.bytes().enumerate() {
if b == b'\n' {
line_starts.push(i + 1);
}
}
Self { line_starts }
}
pub fn offset_to_line_col_0based(&self, offset: usize) -> (u32, u32) {
let line = self
.line_starts
.partition_point(|&s| s <= offset)
.saturating_sub(1);
let col = offset.saturating_sub(self.line_starts.get(line).copied().unwrap_or(0));
(line as u32, col as u32)
}
pub fn proto_span(&self, start: usize, end: usize) -> Vec<i32> {
let (sl, sc) = self.offset_to_line_col_0based(start);
let (el, ec) = self.offset_to_line_col_0based(end);
if sl == el {
vec![sl as i32, sc as i32, ec as i32]
} else {
vec![sl as i32, sc as i32, el as i32, ec as i32]
}
}
}
#[cfg(feature = "native-parser")]
pub(crate) fn offset_to_line_col(src: &str, offset: usize) -> (u32, u32) {
let mut line = 1u32;
let mut col = 1u32;
for (i, b) in src.bytes().enumerate() {
if i >= offset {
break;
}
if b == b'\n' {
line += 1;
col = 1;
} else {
col += 1;
}
}
(line, col)
}