ass_core/parser/position_tracker/
tracker.rs1use crate::parser::ast::Span;
7
8#[derive(Debug, Clone)]
10pub struct PositionTracker<'a> {
11 source: &'a str,
13 offset: usize,
15 line: u32,
17 column: u32,
19 line_start: usize,
21}
22
23impl<'a> PositionTracker<'a> {
24 #[must_use]
26 pub const fn new(source: &'a str) -> Self {
27 Self {
28 source,
29 offset: 0,
30 line: 1,
31 column: 1,
32 line_start: 0,
33 }
34 }
35
36 #[must_use]
38 pub const fn new_at(source: &'a str, offset: usize, line: u32, column: u32) -> Self {
39 Self {
40 source,
41 offset,
42 line,
43 column,
44 line_start: offset.saturating_sub((column - 1) as usize),
45 }
46 }
47
48 #[must_use]
50 pub const fn offset(&self) -> usize {
51 self.offset
52 }
53
54 #[must_use]
56 pub const fn line(&self) -> u32 {
57 self.line
58 }
59
60 #[must_use]
62 pub const fn column(&self) -> u32 {
63 self.column
64 }
65
66 pub fn advance(&mut self, bytes: usize) {
68 let end = (self.offset + bytes).min(self.source.len());
69
70 while self.offset < end {
71 if self.source.as_bytes().get(self.offset) == Some(&b'\n') {
72 self.offset += 1;
73 self.line += 1;
74 self.column = 1;
75 self.line_start = self.offset;
76 } else {
77 self.offset += 1;
78 self.column += 1;
79 }
80 }
81 }
82
83 pub fn advance_to(&mut self, target_offset: usize) {
85 if target_offset > self.offset {
86 self.advance(target_offset - self.offset);
87 }
88 }
89
90 pub fn skip_whitespace(&mut self) {
92 while let Some(&ch) = self.source.as_bytes().get(self.offset) {
93 if ch == b' ' || ch == b'\t' || ch == b'\r' {
94 self.advance(1);
95 } else {
96 break;
97 }
98 }
99 }
100
101 pub fn skip_line(&mut self) {
103 while let Some(&ch) = self.source.as_bytes().get(self.offset) {
104 self.advance(1);
105 if ch == b'\n' {
106 break;
107 }
108 }
109 }
110
111 #[must_use]
113 pub fn remaining(&self) -> &'a str {
114 &self.source[self.offset..]
115 }
116
117 #[must_use]
119 pub const fn is_at_end(&self) -> bool {
120 self.offset >= self.source.len()
121 }
122
123 #[must_use]
125 pub const fn span_from(&self, start: &PositionTracker) -> Span {
126 Span::new(start.offset, self.offset, start.line, start.column)
127 }
128
129 #[must_use]
131 pub const fn span_for(&self, length: usize) -> Span {
132 Span::new(self.offset, self.offset + length, self.line, self.column)
133 }
134
135 #[must_use]
137 pub const fn checkpoint(&self) -> Self {
138 PositionTracker {
139 source: self.source,
140 offset: self.offset,
141 line: self.line,
142 column: self.column,
143 line_start: self.line_start,
144 }
145 }
146}