use std::ops::Deref;
use memchr::{memchr, memchr2, memchr3, memmem};
use crate::ext::CharExt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Tape<'a, T> {
raw: &'a [T],
pub pos: usize,
}
impl<'a, T> Deref for Tape<'a, T> {
type Target = &'a [T];
fn deref(&self) -> &Self::Target {
&self.raw
}
}
impl<'a, T> Tape<'a, T> {
#[inline]
#[must_use]
pub const fn new(raw: &'a [T]) -> Self {
Self { raw, pos: 0 }
}
#[inline]
#[must_use]
pub fn rest(self) -> &'a [T] {
&self.raw[self.pos..self.raw.len()]
}
#[inline(always)]
pub const fn adv(&mut self) {
self.pos += 1;
}
#[inline(always)]
pub const fn dec(&mut self) {
self.pos -= 1;
}
pub const fn is_exhausted(&mut self) -> bool {
self.pos >= self.raw.len()
}
}
impl<'a, T: Copy + PartialEq> Tape<'a, T> {
#[inline(always)]
#[must_use]
#[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Option<T> {
let elem = self.raw.get(self.pos);
self.pos += 1;
elem.copied()
}
#[must_use]
#[inline(always)]
pub const fn cur(self) -> Option<T> {
if self.pos < self.raw.len() {
Some(self.raw[self.pos])
} else {
None
}
}
#[must_use]
#[inline(always)]
pub const fn peek(self) -> Option<T> {
let pos = self.pos + 1;
if pos < self.raw.len() {
Some(self.raw[pos])
} else {
None
}
}
#[must_use]
#[inline(always)]
pub const fn peek_back(self) -> Option<T> {
let pos = self.pos - 1;
if pos < self.raw.len() {
Some(self.raw[pos])
} else {
None
}
}
#[must_use]
#[inline]
pub fn poll<F>(self, mut pred: F) -> Option<usize>
where
F: FnMut(T, usize) -> bool,
{
(self.pos..self.raw.len()).find(|&pos| pred(self.raw[pos], pos))
}
#[must_use]
#[inline]
pub fn poll_back<F>(self, mut pred: F) -> Option<usize>
where
F: FnMut(T, usize) -> bool,
{
(self.pos..self.raw.len())
.rev()
.find(|&pos| pred(self.raw[pos], pos))
}
#[inline]
pub fn consume<F>(&mut self, mut pred: F) -> &'a [T]
where
F: FnMut(T, usize) -> bool,
{
match self.poll(|elem, pos| !pred(elem, pos)) {
None => &self.raw[0..0],
Some(pos) => {
let res = &self.raw[self.pos..pos];
self.pos = pos;
res
}
}
}
#[inline]
pub fn put_back<F>(&mut self, mut pred: F) -> &'a [T]
where
F: FnMut(T, usize) -> bool,
{
match self.poll_back(|elem, pos| !pred(elem, pos)) {
None => &self.raw[0..0],
Some(pos) => {
let res = &self.raw[self.pos..pos];
self.pos = pos;
res
}
}
}
#[inline]
pub fn seek<F>(&mut self, pred: F) -> bool
where
F: FnMut(T, usize) -> bool,
{
match self.poll(pred) {
None => false,
Some(pos) => {
self.pos = pos;
true
}
}
}
#[inline]
pub fn seek_back<F>(&mut self, pred: F) -> bool
where
F: FnMut(T, usize) -> bool,
{
match self.poll_back(pred) {
None => false,
Some(pos) => {
self.pos = pos;
true
}
}
}
#[must_use]
#[inline]
pub fn is_at(self, query: &[T]) -> bool {
self.raw[self.pos..].starts_with(query)
}
}
impl<'a> Tape<'a, u8> {
#[must_use]
#[inline]
pub fn is_left_clear(self, pos: usize) -> bool {
pos == 0 || self.raw.get(pos - 1).is_none_or(|elem| elem.is_simple_ws())
}
#[must_use]
#[inline]
pub fn is_right_clear(self, pos: usize) -> bool {
self.raw.get(pos + 1).is_none_or(|elem| elem.is_simple_ws())
}
#[must_use]
#[inline]
pub fn is_any_clear(self, start: usize) -> bool {
!self.is_left_clear(start) || self.is_right_clear(self.pos)
}
#[must_use]
#[inline]
pub fn poll_in_pgraph<F>(self, spacing: u8, mut pred: F) -> Option<usize>
where
F: FnMut(u8, usize) -> bool,
{
let mut nl_count = 0;
for (i, &elem) in self.raw.iter().enumerate() {
if elem == b'\n' {
nl_count += 1;
if nl_count >= spacing {
return None;
}
} else {
nl_count = 0;
}
if pred(elem, i) {
return Some(i);
}
}
None
}
#[inline]
pub fn consume_in_pgraph<F>(&mut self, spacing: u8, mut pred: F) -> &'a [u8]
where
F: FnMut(u8, usize) -> bool,
{
match self.poll_in_pgraph(spacing, |elem, pos| !pred(elem, pos)) {
None => &self.raw[0..0],
Some(pos) => {
let res = &self.raw[self.pos..pos];
self.pos = pos;
res
}
}
}
#[inline]
pub fn seek_ch(&mut self, query: u8) -> bool {
if let Some(offset) = memchr(query, &self.raw[self.pos..]) {
self.pos += offset;
return true;
}
false
}
#[inline]
pub fn seek_ch2(&mut self, ch0: u8, ch1: u8) -> bool {
if let Some(offset) = memchr2(ch0, ch1, &self.raw[self.pos..]) {
self.pos += offset;
return true;
}
false
}
#[inline]
pub fn seek_ch3(&mut self, ch0: u8, ch1: u8, ch2: u8) -> bool {
if let Some(offset) = memchr3(ch0, ch1, ch2, &self.raw[self.pos..]) {
self.pos += offset;
return true;
}
false
}
#[inline]
pub fn seek_at(&mut self, query: &'a [u8]) -> bool {
if let Some(offset) = memmem::find(&self.raw[self.pos..], query) {
self.pos += offset;
return true;
}
false
}
#[inline]
pub fn seek_at_in_pgraph(&mut self, spacing: u8, query: &'a [u8]) -> bool {
self.seek_in_pgraph(spacing, |_, pos| self.raw[pos..].starts_with(query))
}
#[inline]
pub fn seek_ch_in_pgraph(&mut self, spacing: u8, query: u8) -> bool {
self.seek_in_pgraph(spacing, |_, pos| self.raw[pos] == query)
}
#[inline]
pub fn seek_in_pgraph<F>(&mut self, spacing: u8, pred: F) -> bool
where
F: FnMut(u8, usize) -> bool,
{
match self.poll_in_pgraph(spacing, pred) {
None => false,
Some(pos) => {
self.pos = pos;
true
}
}
}
#[must_use]
#[inline]
pub fn is_cur_prefix(self) -> bool {
self.is_prefix(self.pos)
}
#[must_use]
#[inline]
pub fn is_prefix(self, pos: usize) -> bool {
for i in (0..pos).rev() {
let c = self.raw[i]; if c == b'\n' {
return true;
}
if !c.is_simple_ws() {
return false;
}
}
true
}
#[must_use]
#[inline]
pub fn count_indent(self) -> u8 {
let ws = &self.raw[self.poll_back(|elem, _| elem == b'\n').unwrap_or(0)..self.pos];
let (tabs, spaces) = ws.iter().fold((0, 0), |(t, s), &elem| match elem {
b'\t' => (t + 1, s),
b' ' => (t, s + 1),
_ => (t, s),
});
tabs + (spaces / 4)
}
}