use crate::parser::ast::Span;
#[derive(Debug, Clone)]
pub struct PositionTracker<'a> {
source: &'a str,
offset: usize,
line: u32,
column: u32,
line_start: usize,
}
impl<'a> PositionTracker<'a> {
#[must_use]
pub const fn new(source: &'a str) -> Self {
Self {
source,
offset: 0,
line: 1,
column: 1,
line_start: 0,
}
}
#[must_use]
pub const fn new_at(source: &'a str, offset: usize, line: u32, column: u32) -> Self {
Self {
source,
offset,
line,
column,
line_start: offset.saturating_sub((column - 1) as usize),
}
}
#[must_use]
pub const fn offset(&self) -> usize {
self.offset
}
#[must_use]
pub const fn line(&self) -> u32 {
self.line
}
#[must_use]
pub const fn column(&self) -> u32 {
self.column
}
pub fn advance(&mut self, bytes: usize) {
let end = (self.offset + bytes).min(self.source.len());
while self.offset < end {
if self.source.as_bytes().get(self.offset) == Some(&b'\n') {
self.offset += 1;
self.line += 1;
self.column = 1;
self.line_start = self.offset;
} else {
self.offset += 1;
self.column += 1;
}
}
}
pub fn advance_to(&mut self, target_offset: usize) {
if target_offset > self.offset {
self.advance(target_offset - self.offset);
}
}
pub fn skip_whitespace(&mut self) {
while let Some(&ch) = self.source.as_bytes().get(self.offset) {
if ch == b' ' || ch == b'\t' || ch == b'\r' {
self.advance(1);
} else {
break;
}
}
}
pub fn skip_line(&mut self) {
while let Some(&ch) = self.source.as_bytes().get(self.offset) {
self.advance(1);
if ch == b'\n' {
break;
}
}
}
#[must_use]
pub fn remaining(&self) -> &'a str {
&self.source[self.offset..]
}
#[must_use]
pub const fn is_at_end(&self) -> bool {
self.offset >= self.source.len()
}
#[must_use]
pub const fn span_from(&self, start: &PositionTracker) -> Span {
Span::new(start.offset, self.offset, start.line, start.column)
}
#[must_use]
pub const fn span_for(&self, length: usize) -> Span {
Span::new(self.offset, self.offset + length, self.line, self.column)
}
#[must_use]
pub const fn checkpoint(&self) -> Self {
PositionTracker {
source: self.source,
offset: self.offset,
line: self.line,
column: self.column,
line_start: self.line_start,
}
}
}