use super::{IntoTokens, TokenLocation, Tokens};
pub struct SliceTokens<'a, Item> {
slice: &'a [Item],
cursor: usize,
}
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
pub struct SliceTokensLocation(usize);
impl TokenLocation for SliceTokensLocation {
fn offset(&self) -> usize {
self.0
}
}
impl<'a, Item> SliceTokens<'a, Item> {
pub fn consumed(&self) -> &'a [Item] {
&self.slice[..self.cursor]
}
pub fn remaining(&self) -> &'a [Item] {
&self.slice[self.cursor..]
}
}
impl<'a, Item> From<SliceTokens<'a, Item>> for &'a [Item] {
fn from(toks: SliceTokens<'a, Item>) -> Self {
toks.slice
}
}
impl<'a, Item> Tokens for SliceTokens<'a, Item> {
type Item = &'a Item;
type Location = SliceTokensLocation;
fn next(&mut self) -> Option<Self::Item> {
let res = self.slice.get(self.cursor);
self.cursor += 1;
res
}
fn location(&self) -> Self::Location {
SliceTokensLocation(self.cursor)
}
fn set_location(&mut self, location: Self::Location) {
self.cursor = location.0;
}
fn is_at_location(&self, location: &Self::Location) -> bool {
self.cursor == location.0
}
}
impl<'a, Item> IntoTokens<&'a Item> for SliceTokens<'a, Item> {
type Tokens = Self;
fn into_tokens(self) -> Self {
self
}
}
impl<'a, Item> IntoTokens<&'a Item> for &'a [Item] {
type Tokens = SliceTokens<'a, Item>;
fn into_tokens(self) -> Self::Tokens {
SliceTokens {
slice: self,
cursor: 0,
}
}
}
pub struct StrTokens<'a> {
str: &'a str,
cursor: usize,
}
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
pub struct StrTokensLocation(usize);
impl TokenLocation for StrTokensLocation {
fn offset(&self) -> usize {
self.0
}
}
impl<'a> StrTokens<'a> {
pub fn consumed(&self) -> &'a str {
&self.str[..self.cursor]
}
pub fn remaining(&self) -> &'a str {
&self.str[self.cursor..]
}
}
impl<'a> From<StrTokens<'a>> for &'a str {
fn from(toks: StrTokens<'a>) -> Self {
toks.str
}
}
impl<'a> Tokens for StrTokens<'a> {
type Item = char;
type Location = StrTokensLocation;
fn next(&mut self) -> Option<Self::Item> {
if self.cursor == self.str.len() {
return None;
}
let mut next_char_boundary = self.cursor + 1;
while !self.str.is_char_boundary(next_char_boundary) {
next_char_boundary += 1;
}
let next_char = unsafe { self.str.get_unchecked(self.cursor..next_char_boundary) }
.chars()
.next()
.unwrap();
self.cursor = next_char_boundary;
Some(next_char)
}
fn location(&self) -> Self::Location {
StrTokensLocation(self.cursor)
}
fn set_location(&mut self, location: Self::Location) {
self.cursor = location.0;
}
fn is_at_location(&self, location: &Self::Location) -> bool {
self.cursor == location.0
}
fn parse<Out, Buf>(&mut self) -> Result<Out, <Out as core::str::FromStr>::Err>
where
Out: core::str::FromStr,
Buf: FromIterator<Self::Item> + core::ops::Deref<Target = str>,
{
let res = self.remaining().parse()?;
self.cursor = self.str.len();
Ok(res)
}
fn parse_slice<Out, Buf>(
&mut self,
from: Self::Location,
to: Self::Location,
) -> Result<Out, <Out as core::str::FromStr>::Err>
where
Out: core::str::FromStr,
Buf: FromIterator<Self::Item> + core::ops::Deref<Target = str>,
{
self.str[from.0..to.0].parse()
}
fn parse_take<Out, Buf>(&mut self, n: usize) -> Result<Out, <Out as core::str::FromStr>::Err>
where
Out: core::str::FromStr,
Buf: FromIterator<Self::Item> + core::ops::Deref<Target = str>,
{
let from = self.location();
self.take(n).consume();
let to = self.location();
let res = self.str[from.0..to.0].parse();
if res.is_err() {
self.set_location(from);
}
res
}
fn parse_take_while<Out, Buf, F>(
&mut self,
take_while: F,
) -> Result<Out, <Out as core::str::FromStr>::Err>
where
Out: core::str::FromStr,
Buf: FromIterator<Self::Item> + core::ops::Deref<Target = str>,
F: FnMut(&Self::Item) -> bool,
{
let from = self.location();
self.take_while(take_while).consume();
let to = self.location();
let res = self.str[from.0..to.0].parse();
if res.is_err() {
self.set_location(from);
}
res
}
}
impl<'a> IntoTokens<char> for StrTokens<'a> {
type Tokens = Self;
fn into_tokens(self) -> Self {
self
}
}
impl<'a> IntoTokens<char> for &'a str {
type Tokens = StrTokens<'a>;
fn into_tokens(self) -> Self::Tokens {
StrTokens {
str: self,
cursor: 0,
}
}
}
#[derive(Clone)]
pub struct IterTokens<I> {
iter: I,
cursor: usize,
}
#[derive(Clone)]
pub struct IterTokensLocation<I>(IterTokens<I>);
impl<I> core::fmt::Debug for IterTokensLocation<I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "IterTokensLocation(cursor = {})", self.0.cursor)
}
}
impl<I> PartialEq for IterTokensLocation<I> {
fn eq(&self, other: &Self) -> bool {
self.0.cursor == other.0.cursor
}
}
impl<I> TokenLocation for IterTokensLocation<I> {
fn offset(&self) -> usize {
self.0.cursor
}
}
impl<I> IterTokens<I> {
pub fn new(iter: I) -> Self {
IterTokens { iter, cursor: 0 }
}
pub fn into_inner(self) -> I {
self.iter
}
}
impl<I> Tokens for IterTokens<I>
where
I: Iterator + Clone,
{
type Item = I::Item;
type Location = IterTokensLocation<I>;
fn next(&mut self) -> Option<Self::Item> {
self.cursor += 1;
self.iter.next()
}
fn location(&self) -> Self::Location {
IterTokensLocation(self.clone())
}
fn set_location(&mut self, location: Self::Location) {
*self = location.0;
}
fn is_at_location(&self, location: &Self::Location) -> bool {
self.cursor == location.0.cursor
}
fn offset(&self) -> usize {
self.cursor
}
}
impl<I> IntoTokens<I::Item> for IterTokens<I>
where
I: Iterator + Clone,
{
type Tokens = Self;
fn into_tokens(self) -> Self {
self
}
}
pub struct WithContext<T, C> {
tokens: T,
context: C,
}
pub struct WithContextMut<T, C> {
tokens: T,
context: C,
}
macro_rules! with_context_impls {
($name:ident $( $($mut:tt)+ )?) => {
impl <T, C> $name<T, C> {
pub(crate) fn new(tokens: T, context: C) -> Self {
Self { tokens, context }
}
pub fn into_parts(self) -> (T, C) {
(self.tokens, self.context)
}
pub fn context(&self) -> &C {
&self.context
}
pub fn context_mut(&mut self) -> &mut C {
&mut self.context
}
}
impl <T, C> Tokens for $name<$( $($mut)+ )? T, C>
where T: Tokens {
type Item = T::Item;
type Location = T::Location;
fn next(&mut self) -> Option<Self::Item> {
self.tokens.next()
}
fn location(&self) -> Self::Location {
self.tokens.location()
}
fn set_location(&mut self, location: Self::Location) {
self.tokens.set_location(location)
}
fn is_at_location(&self, location: &Self::Location) -> bool {
self.tokens.is_at_location(location)
}
fn parse<Out, Buf>(&mut self) -> Result<Out, <Out as core::str::FromStr>::Err>
where
Out: core::str::FromStr,
Buf: FromIterator<Self::Item> + core::ops::Deref<Target = str>,
{
self.tokens.parse::<Out, Buf>()
}
fn parse_slice<Out, Buf>(
&mut self,
from: Self::Location,
to: Self::Location,
) -> Result<Out, <Out as core::str::FromStr>::Err>
where
Out: core::str::FromStr,
Buf: FromIterator<Self::Item> + core::ops::Deref<Target = str>,
{
self.tokens.parse_slice::<Out, Buf>(from, to)
}
fn parse_take<Out, Buf>(&mut self, n: usize) -> Result<Out, <Out as core::str::FromStr>::Err>
where
Out: core::str::FromStr,
Buf: FromIterator<Self::Item> + core::ops::Deref<Target = str>,
{
self.tokens.parse_take::<Out, Buf>(n)
}
fn parse_take_while<Out, Buf, F>(
&mut self,
take_while: F,
) -> Result<Out, <Out as core::str::FromStr>::Err>
where
Out: core::str::FromStr,
Buf: FromIterator<Self::Item> + core::ops::Deref<Target = str>,
F: FnMut(&Self::Item) -> bool,
{
self.tokens.parse_take_while::<Out, Buf, F>(take_while)
}
}
}
}
with_context_impls!(WithContext);
with_context_impls!(WithContextMut &mut);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exotic_character_bounds() {
let mut tokens = "🗻∈🌏".into_tokens();
assert_eq!(tokens.next(), Some('🗻'));
assert_eq!(tokens.next(), Some('∈'));
assert_eq!(tokens.next(), Some('🌏'));
}
#[test]
fn iterator_tokens_sanity_check() {
let chars = "hello \n\t world".chars();
let mut tokens = IterTokens::new(chars);
let loc = tokens.location();
assert!(tokens.tokens("hello".chars()));
tokens.set_location(loc.clone());
assert!(tokens.tokens("hello".chars()));
tokens.skip_while(|c| c.is_whitespace());
assert!(tokens.tokens("world".chars()));
tokens.set_location(loc);
assert!(tokens.tokens("hello".chars()));
}
#[test]
fn str_tokens_parse_optimisations_work() {
struct BadBuffer;
impl core::iter::FromIterator<char> for BadBuffer {
fn from_iter<T: IntoIterator<Item = char>>(_: T) -> Self {
panic!("FromIterator impl shouldn't be used")
}
}
impl core::ops::Deref for BadBuffer {
type Target = str;
fn deref(&self) -> &Self::Target {
panic!("Deref impl shouldn't be used")
}
}
let mut tokens = "123abc".into_tokens();
let from = tokens.location();
tokens.take_while(|t| t.is_numeric()).consume();
let to = tokens.location();
let n = tokens
.slice(from, to)
.parse::<u16, BadBuffer>()
.expect("parse worked (1)");
assert_eq!(n, 123);
assert_eq!(tokens.remaining(), "abc");
let mut tokens = "123abc".into_tokens();
let n = tokens
.take(3)
.parse::<u16, BadBuffer>()
.expect("parse worked (2)");
assert_eq!(n, 123);
assert_eq!(tokens.remaining(), "abc");
let mut tokens = "123abc".into_tokens();
let n = tokens
.take_while(|t| t.is_numeric())
.parse::<u16, BadBuffer>()
.expect("parse worked (3)");
assert_eq!(n, 123);
assert_eq!(tokens.remaining(), "abc");
let mut tokens = "123ab+=".into_tokens();
let n = tokens
.take(6)
.take(5)
.take_while(|t| t.is_alphanumeric())
.take_while(|t| t.is_numeric())
.take(2)
.parse::<u16, BadBuffer>()
.expect("parse worked (4)");
assert_eq!(n, 12);
assert_eq!(tokens.remaining(), "3ab+=");
}
}