Added a Lookahead iterator.
This adds a Lookahead iterator so that while parsing it is easier to peek ahead however much the parser needs. Basic parsers may not need any, but a lot of parsers have two token lookahead. I've even seen some with three.
This commit is contained in:
parent
cb882ceb84
commit
34a579332d
209
src/iter.rs
Normal file
209
src/iter.rs
Normal file
@ -0,0 +1,209 @@
|
||||
//! An iterator adapter for arbitrary lookahead functionality.
|
||||
//!
|
||||
//! This module provides [`Lookahead`], an adapter for any iterator that allows
|
||||
//! you to peek ahead by any number of elements, without consuming them.
|
||||
//!
|
||||
//! ## Example
|
||||
//! ```
|
||||
//! use rune::LookaheadExt;
|
||||
//!
|
||||
//! let mut it = vec![10, 20, 30].into_iter().lookahead();
|
||||
//!
|
||||
//! assert_eq!(it.peek(0), Some(&10));
|
||||
//! assert_eq!(it.peek(1), Some(&20));
|
||||
//! assert_eq!(it.next(), Some(10));
|
||||
//! assert_eq!(it.peek(0), Some(&20));
|
||||
//! ```
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::iter::{Fuse, FusedIterator};
|
||||
|
||||
|
||||
|
||||
/// An iterator adapter that allows arbitrary lookahead peeking.
|
||||
///
|
||||
/// This struct wraps an iterator and buffers items so that any future
|
||||
/// item can be accessed by index without consuming them. Similar to
|
||||
/// [`std::iter::Peekable`], but supports peeking any number of steps ahead.
|
||||
pub struct Lookahead<I>
|
||||
where I: Iterator
|
||||
{
|
||||
iter: Fuse<I>,
|
||||
buffer: VecDeque<I::Item>
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl<I> Lookahead<I> where I: Iterator
|
||||
{
|
||||
/// Creates a new [`Lookahead`] from the given iterator.
|
||||
///
|
||||
/// This constructor is typically used indirectly via the
|
||||
/// [`LookaheadExt::lookahead()`] method or [`lookahead()`] free function.
|
||||
#[must_use]
|
||||
pub fn new(iter: I) -> Self
|
||||
{
|
||||
Lookahead { iter: iter.fuse(),
|
||||
buffer: VecDeque::new() }
|
||||
}
|
||||
|
||||
/// Returns a reference to the `n`th upcoming item, if it exists.
|
||||
///
|
||||
/// `peek(0)` is the same as peeking at the next item.
|
||||
///
|
||||
/// This does **not consume** any items from the iterator.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rune::LookaheadExt;
|
||||
///
|
||||
/// let mut it = vec![1, 2, 3].into_iter().lookahead();
|
||||
///
|
||||
/// assert_eq!(it.peek(1), Some(&2));
|
||||
/// assert_eq!(it.next(), Some(1));
|
||||
/// ```
|
||||
pub fn peek(&mut self, n: usize) -> Option<&I::Item>
|
||||
{
|
||||
while self.buffer.len() <= n
|
||||
{
|
||||
if let Some(item) = self.iter.next()
|
||||
{
|
||||
self.buffer.push_back(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.buffer.get(n)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the `n`th upcoming item, if it exists.
|
||||
///
|
||||
/// This allows in-place modification of peeked items before consumption.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rune::LookaheadExt;
|
||||
///
|
||||
/// let mut it = vec![1, 2, 3].into_iter().lookahead();
|
||||
/// if let Some(x) = it.peek_mut(1)
|
||||
/// {
|
||||
/// *x *= 10;
|
||||
/// }
|
||||
/// assert_eq!(it.next(), Some(1));
|
||||
/// assert_eq!(it.next(), Some(20));
|
||||
/// ```
|
||||
pub fn peek_mut(&mut self, n: usize) -> Option<&mut I::Item>
|
||||
{
|
||||
while self.buffer.len() <= n
|
||||
{
|
||||
if let Some(item) = self.iter.next()
|
||||
{
|
||||
self.buffer.push_back(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.buffer.get_mut(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for Lookahead<I> where I: Iterator
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
/// Retrieves the next item, consuming it.
|
||||
///
|
||||
/// If any items were previously peeked and buffered, they are returned
|
||||
/// first before accessing the underlying iterator.
|
||||
fn next(&mut self) -> Option<Self::Item>
|
||||
{
|
||||
if let Some(front) = self.buffer.pop_front()
|
||||
{
|
||||
Some(front)
|
||||
}
|
||||
else
|
||||
{
|
||||
self.iter.next()
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a size hint accounting for both buffered and remaining elements.
|
||||
fn size_hint(&self) -> (usize, Option<usize>)
|
||||
{
|
||||
let (low, high) = self.iter.size_hint();
|
||||
let buffered = self.buffer.len();
|
||||
(low.saturating_add(buffered), high.and_then(|h| h.checked_add(buffered)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Clone for Lookahead<I>
|
||||
where I: Iterator + Clone,
|
||||
I::Item: Clone
|
||||
{
|
||||
fn clone(&self) -> Self
|
||||
{
|
||||
Lookahead { iter: self.iter.clone(),
|
||||
buffer: self.buffer.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> fmt::Debug for Lookahead<I>
|
||||
where I: Iterator + fmt::Debug,
|
||||
I::Item: fmt::Debug
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
f.debug_struct("Lookahead")
|
||||
.field("iter", &self.iter)
|
||||
.field("buffer", &self.buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> FusedIterator for Lookahead<I> where I: Iterator + FusedIterator {}
|
||||
|
||||
|
||||
|
||||
/// Extension trait to provide `.lookahead()` on all iterators.
|
||||
///
|
||||
/// This lets you easily call `.lookahead()` on any iterator to
|
||||
/// create a [`Lookahead`] instance.
|
||||
pub trait LookaheadExt: Iterator + Sized
|
||||
{
|
||||
/// Wraps the iterator in a [`Lookahead`] adapter.
|
||||
fn lookahead(self) -> Lookahead<Self>;
|
||||
}
|
||||
|
||||
impl<I: Iterator> LookaheadExt for I
|
||||
{
|
||||
fn lookahead(self) -> Lookahead<Self>
|
||||
{
|
||||
Lookahead::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Creates a [`Lookahead`] from any iterable.
|
||||
///
|
||||
/// This is a convenience function for use in functional-style code or
|
||||
/// when not using the extension trait.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rune::lookahead;
|
||||
///
|
||||
/// let mut it = lookahead(vec![1, 2, 3]);
|
||||
///
|
||||
/// assert_eq!(it.peek(2), Some(&3));
|
||||
/// ```
|
||||
pub fn lookahead<I>(iterable: I) -> Lookahead<I::IntoIter>
|
||||
where I: IntoIterator
|
||||
{
|
||||
Lookahead::new(iterable.into_iter())
|
||||
}
|
@ -9,6 +9,7 @@ pub mod library;
|
||||
|
||||
mod ast;
|
||||
mod error;
|
||||
mod iter;
|
||||
mod lexer;
|
||||
mod position;
|
||||
mod token;
|
||||
@ -17,6 +18,7 @@ mod token;
|
||||
|
||||
pub use crate::ast::*;
|
||||
pub use crate::error::*;
|
||||
pub use crate::iter::*;
|
||||
pub use crate::lexer::*;
|
||||
pub use crate::position::*;
|
||||
pub use crate::token::*;
|
||||
|
43
tests/iter.rs
Normal file
43
tests/iter.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use rune::*;
|
||||
|
||||
#[test]
|
||||
fn peek_works()
|
||||
{
|
||||
let mut it = vec![1, 2, 3].into_iter().lookahead();
|
||||
assert_eq!(it.peek(0), Some(&1));
|
||||
assert_eq!(it.peek(1), Some(&2));
|
||||
assert_eq!(it.peek(2), Some(&3));
|
||||
assert_eq!(it.peek(3), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn peek_mut_modifies_item()
|
||||
{
|
||||
let mut it = vec![10, 20, 30].into_iter().lookahead();
|
||||
if let Some(x) = it.peek_mut(1)
|
||||
{
|
||||
*x += 100;
|
||||
}
|
||||
assert_eq!(it.next(), Some(10));
|
||||
assert_eq!(it.next(), Some(120));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterates_correctly()
|
||||
{
|
||||
let mut it = vec![1, 2, 3].into_iter().lookahead();
|
||||
assert_eq!(it.next(), Some(1));
|
||||
assert_eq!(it.next(), Some(2));
|
||||
assert_eq!(it.next(), Some(3));
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_hint_accounts_for_buffer()
|
||||
{
|
||||
let mut it = vec![1, 2, 3, 4].into_iter().lookahead();
|
||||
it.peek(2);
|
||||
let (low, high) = it.size_hint();
|
||||
assert!(low >= 4);
|
||||
assert_eq!(high, Some(4));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user