You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
9.3 KiB
Rust
346 lines
9.3 KiB
Rust
use std::ops::{Add, AddAssign, Not, Sub, SubAssign};
|
|
|
|
/// Helper utility for parsers operating on slices
|
|
pub struct SliceCursor<'a, T> {
|
|
data: &'a [T],
|
|
pos: Position,
|
|
chop: usize,
|
|
}
|
|
|
|
/// Helper for the [`SliceCursor`] helper.
|
|
#[derive(Copy, Clone)]
|
|
struct Position {
|
|
/// Always within -1 and `end` (both inclusive).
|
|
pos: isize,
|
|
/// Length of the slice (i.e. first out-of-bound index).
|
|
end: usize,
|
|
}
|
|
|
|
impl<'a, T> Clone for SliceCursor<'a, T> {
|
|
fn clone(&self) -> Self {
|
|
Self {
|
|
data: self.data,
|
|
pos: self.pos,
|
|
chop: self.chop,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T> SliceCursor<'a, T> {
|
|
pub fn new(data: &'a [T]) -> Self {
|
|
assert!(data.len() <= isize::MAX as usize);
|
|
|
|
Self {
|
|
data,
|
|
pos: Position::new(data.len()),
|
|
chop: 0,
|
|
}
|
|
}
|
|
|
|
/// Return the item at the current position, if any.
|
|
pub fn current(&self) -> Option<&'a T> {
|
|
self.pos.index().map(|index| &self.data[index])
|
|
}
|
|
|
|
/// Advance the cursor to the next item and return that item.
|
|
pub fn next(&mut self) -> Option<&'a T> {
|
|
self.pos.advance().map(|index| &self.data[index])
|
|
}
|
|
|
|
/// Return the next item without advancing the cursor.
|
|
pub fn peek(&self) -> Option<&'a T> {
|
|
self.pos.next_index().map(|index| &self.data[index])
|
|
}
|
|
|
|
/// Reverse the cursor to the previous item and return that item.
|
|
pub fn prev(&mut self) -> Option<&'a T> {
|
|
self.pos.reverse().map(|index| &self.data[index])
|
|
}
|
|
|
|
/// Peek for the next item and advance the cursor if `predicate` is true.
|
|
pub fn next_if<F>(&mut self, predicate: F) -> Option<&'a T>
|
|
where
|
|
F: FnOnce(&'a T) -> bool,
|
|
{
|
|
let next = self.peek()?;
|
|
if predicate(next) {
|
|
self.pos.advance();
|
|
Some(next)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Advance to the last item for which `predicate` is true and return a
|
|
/// slice from the current position up to and including that last item.
|
|
///
|
|
/// Besides the fact that it will not modify the chop position,
|
|
/// this operation is functionally equivalent to:
|
|
///
|
|
/// ```
|
|
/// cursor.chop();
|
|
/// while let Some(c) = cursor.peek() {
|
|
/// if predicate(c) {
|
|
/// cursor.next();
|
|
/// } else {
|
|
/// break;
|
|
/// }
|
|
/// }
|
|
/// let result = cursor.chop();
|
|
/// ```
|
|
pub fn next_while<F>(&mut self, mut predicate: F) -> &'a [T]
|
|
where
|
|
F: FnMut(&'a T) -> bool,
|
|
{
|
|
let start = self.pos.next_index_or_end();
|
|
let len = self.data[start..]
|
|
.iter()
|
|
.enumerate()
|
|
.find_map(|(i, v)| predicate(v).not().then_some(i))
|
|
.unwrap_or(self.data.len() - start);
|
|
let end = start + len;
|
|
self.pos += len;
|
|
&self.data[start..end]
|
|
}
|
|
|
|
/// Save the cursor's state and perform an arbitrary operation on it.
|
|
/// If the operation failed (i.e. yielded `None`), restore the cursor's
|
|
/// state. Passes on the return value of `op`.
|
|
///
|
|
/// `op` SHOULD NOT redefine the cursor unless you want buggy code.
|
|
/// This is because the original slice is only restored if `op` succeeded.
|
|
pub fn attempt<F, U>(&mut self, op: F) -> Option<U>
|
|
where
|
|
F: FnOnce(&mut Self) -> Option<U>,
|
|
{
|
|
let backup = self.save();
|
|
let result = op(self);
|
|
if result.is_none() {
|
|
self.restore(backup);
|
|
}
|
|
result
|
|
}
|
|
|
|
/// Return a slice over all elements since the last time this method was called.
|
|
/// If the cursor went backwards, the slice is empty.
|
|
pub fn chop(&mut self) -> &'a [T] {
|
|
let start = self.chop;
|
|
let end = self.pos.next_index_or_end();
|
|
|
|
let slice = if start < end {
|
|
&self.data[start..end]
|
|
} else {
|
|
&[]
|
|
};
|
|
|
|
self.chop = end;
|
|
slice
|
|
}
|
|
|
|
/// Return how many items remain in the cursor.
|
|
/// This indicates how many times it is possible
|
|
/// to call [`Self::next()`] before it returns `None`.
|
|
pub fn remaining(&self) -> usize {
|
|
self.data.len() - self.pos.next_index_or_end()
|
|
}
|
|
|
|
fn save(&self) -> Self {
|
|
self.clone()
|
|
}
|
|
|
|
fn restore(&mut self, backup: Self) {
|
|
self.data = backup.data;
|
|
self.chop = backup.chop;
|
|
self.pos = backup.pos;
|
|
}
|
|
}
|
|
|
|
impl Position {
|
|
pub fn new(end: usize) -> Position {
|
|
assert!(end <= isize::MAX as usize);
|
|
Position { pos: -1, end }
|
|
}
|
|
|
|
pub fn advance(&mut self) -> Option<usize> {
|
|
*self += 1;
|
|
self.index()
|
|
}
|
|
|
|
pub fn reverse(&mut self) -> Option<usize> {
|
|
*self -= 1;
|
|
self.index()
|
|
}
|
|
|
|
pub fn jump_to(&mut self, index: usize) {
|
|
assert!(index <= self.end);
|
|
self.pos = index as isize;
|
|
}
|
|
|
|
pub fn index(&self) -> Option<usize> {
|
|
(0..self.end as isize)
|
|
.contains(&self.pos)
|
|
.then_some(self.pos as usize)
|
|
}
|
|
|
|
pub fn next_index(&self) -> Option<usize> {
|
|
let next_pos = assert_usize(self.pos + 1);
|
|
(next_pos < self.end).then_some(next_pos)
|
|
}
|
|
|
|
pub fn next_index_or_end(&self) -> usize {
|
|
self.next_index().unwrap_or(self.end)
|
|
}
|
|
}
|
|
|
|
impl Add<usize> for Position {
|
|
type Output = Position;
|
|
fn add(self, rhs: usize) -> Position {
|
|
let rhs = assert_isize(rhs);
|
|
let result = self.pos.saturating_add(rhs);
|
|
Position {
|
|
pos: result.min(self.end as isize),
|
|
end: self.end,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AddAssign<usize> for Position {
|
|
fn add_assign(&mut self, rhs: usize) {
|
|
let rhs = assert_isize(rhs);
|
|
self.pos = self.pos.saturating_add(rhs).min(self.end as isize);
|
|
}
|
|
}
|
|
|
|
impl Sub<usize> for Position {
|
|
type Output = Position;
|
|
fn sub(self, rhs: usize) -> Position {
|
|
let rhs = assert_isize(rhs);
|
|
let result = self.pos.saturating_sub(rhs);
|
|
Position {
|
|
pos: result.max(-1),
|
|
end: self.end,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SubAssign<usize> for Position {
|
|
fn sub_assign(&mut self, rhs: usize) {
|
|
let rhs = assert_isize(rhs);
|
|
self.pos = self.pos.saturating_sub(rhs).max(-1);
|
|
}
|
|
}
|
|
|
|
fn assert_usize(i: isize) -> usize {
|
|
assert!(i >= 0);
|
|
i as usize
|
|
}
|
|
|
|
fn assert_isize(u: usize) -> isize {
|
|
assert!(u <= isize::MAX as usize);
|
|
u as isize
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::util::slice::SliceCursor;
|
|
|
|
#[test]
|
|
fn next_and_prev() {
|
|
let data: Vec<u8> = (0..10).collect();
|
|
let mut cursor = SliceCursor::new(&data);
|
|
assert_eq!(cursor.remaining(), 10);
|
|
|
|
for (i, v) in data.iter().enumerate() {
|
|
assert_eq!(cursor.remaining(), data.len() - i);
|
|
assert_eq!(cursor.next(), Some(v));
|
|
}
|
|
assert_eq!(cursor.remaining(), 0);
|
|
assert!(cursor.next().is_none());
|
|
assert_eq!(cursor.remaining(), 0);
|
|
|
|
for (i, v) in data.iter().rev().enumerate() {
|
|
assert_eq!(cursor.prev(), Some(v));
|
|
assert_eq!(cursor.remaining(), i);
|
|
}
|
|
assert_eq!(cursor.prev(), None);
|
|
assert_eq!(cursor.remaining(), 10);
|
|
}
|
|
|
|
#[test]
|
|
fn chop() {
|
|
let data: Vec<u8> = (0..10).collect();
|
|
let mut cursor = SliceCursor::new(&data);
|
|
|
|
assert_eq!(cursor.chop(), &data[0..0]);
|
|
|
|
cursor.next();
|
|
assert_eq!(cursor.chop(), &data[0..1]);
|
|
|
|
for _ in 0..3 {
|
|
cursor.next();
|
|
}
|
|
assert_eq!(cursor.chop(), &data[1..4]);
|
|
|
|
cursor.prev();
|
|
assert_eq!(cursor.chop(), &data[3..3]);
|
|
|
|
while cursor.next().is_some() {}
|
|
assert_eq!(cursor.chop(), &data[3..10]);
|
|
|
|
for i in (0u8..10).rev() {
|
|
assert_eq!(cursor.prev(), Some(&i));
|
|
}
|
|
assert!(cursor.prev().is_none());
|
|
assert_eq!(cursor.chop(), &data[0..0]);
|
|
|
|
while cursor.next().is_some() {}
|
|
assert_eq!(cursor.chop(), &data[..]);
|
|
assert_eq!(cursor.chop(), &data[10..10]);
|
|
}
|
|
|
|
#[test]
|
|
fn predicates() {
|
|
let data: Vec<u8> = (0..10).collect();
|
|
let mut cursor = SliceCursor::new(&data);
|
|
|
|
assert_eq!(cursor.next_if(|c| *c == 0), Some(&data[0]));
|
|
assert_eq!(cursor.next_if(|c| *c == 0), None);
|
|
|
|
assert_eq!(cursor.next_while(|c| *c < 5), &data[1..5]);
|
|
assert_eq!(cursor.current(), Some(&4));
|
|
assert_eq!(cursor.chop(), &data[0..5]);
|
|
}
|
|
|
|
#[test]
|
|
fn attempt() {
|
|
let data: Vec<u8> = (0..10).collect();
|
|
let mut cursor = SliceCursor::new(&data);
|
|
|
|
let result = cursor.attempt(|cursor| cursor.next().copied().filter(|c| *c == 0));
|
|
assert_eq!(result, Some(0));
|
|
assert_eq!(cursor.remaining(), 9);
|
|
assert_eq!(cursor.current(), Some(&0));
|
|
|
|
let result = cursor.attempt(|cursor| cursor.next().copied().filter(|c| *c == 0));
|
|
assert_eq!(result, None);
|
|
assert_eq!(cursor.remaining(), 9);
|
|
assert_eq!(cursor.current(), Some(&0));
|
|
|
|
let data2: Vec<u8> = (10..20).collect();
|
|
|
|
let _: Option<()> = cursor.attempt(|cursor| {
|
|
*cursor = SliceCursor::new(&data2);
|
|
cursor.next();
|
|
None
|
|
});
|
|
assert_eq!(cursor.current(), Some(&0));
|
|
|
|
cursor.attempt(|cursor| {
|
|
*cursor = SliceCursor::new(&data2);
|
|
cursor.next();
|
|
Some(())
|
|
});
|
|
assert_eq!(cursor.current(), Some(&10));
|
|
}
|
|
}
|