diff options
| author | Mike Buland <mike@xagasoft.com> | 2026-08-19 09:27:44 -0700 |
|---|---|---|
| committer | Mike Buland <mike@xagasoft.com> | 2026-08-19 09:27:44 -0700 |
| commit | ba96bd8a4a1583edd3677dd613c89504d9ce88cf (patch) | |
| tree | 13b96f53970fb521a697ee1d544624ee329f518c | |
| parent | e1a688efb16f96a6dca3678c67ff38b7cabfa8c6 (diff) | |
| download | deepcrow-ba96bd8a4a1583edd3677dd613c89504d9ce88cf.tar.gz deepcrow-ba96bd8a4a1583edd3677dd613c89504d9ce88cf.tar.bz2 deepcrow-ba96bd8a4a1583edd3677dd613c89504d9ce88cf.tar.xz deepcrow-ba96bd8a4a1583edd3677dd613c89504d9ce88cf.zip | |
Derive?
| -rw-r--r-- | Cargo.lock | 3 | ||||
| -rw-r--r-- | lookahead/src/lib.rs | 34 |
2 files changed, 37 insertions, 0 deletions
| @@ -5,6 +5,9 @@ version = 4 | |||
| 5 | [[package]] | 5 | [[package]] |
| 6 | name = "crimtag" | 6 | name = "crimtag" |
| 7 | version = "0.1.0" | 7 | version = "0.1.0" |
| 8 | dependencies = [ | ||
| 9 | "derive", | ||
| 10 | ] | ||
| 8 | 11 | ||
| 9 | [[package]] | 12 | [[package]] |
| 10 | name = "derive" | 13 | name = "derive" |
diff --git a/lookahead/src/lib.rs b/lookahead/src/lib.rs index 08bf20e..e4f9533 100644 --- a/lookahead/src/lib.rs +++ b/lookahead/src/lib.rs | |||
| @@ -1,3 +1,20 @@ | |||
| 1 | //! Provides a simple iterator adaptor that buffers data from the | ||
| 2 | //! underlying iterator when needed. It will only read ahead when peek requests | ||
| 3 | //! are made, but it will only request each element from the source once. That | ||
| 4 | //! is, no matter how many positions of lookahead you allocate, they won't be | ||
| 5 | //! filled unless you need them, once you have seen them they will be buffered | ||
| 6 | //! until iterated to. | ||
| 7 | //! | ||
| 8 | //! This is a fundamental, low level component of lexical analyzers and of | ||
| 9 | //! parsers in general as well as many other systems. | ||
| 10 | |||
| 11 | /// LookAhead uses a statically allocated ring buffer to store it's data in, an | ||
| 12 | /// internal array of Options to make moving the data in and out easier. | ||
| 13 | /// | ||
| 14 | /// C: How many positions are in the buffer, this should be 1+lookahead length, | ||
| 15 | /// that is C=2 means you get a current value and one lookahead token. | ||
| 16 | /// T: The type of the underlying items | ||
| 17 | /// U: The type of the underlying iterator, must implement Iterator with Type=T | ||
| 1 | pub struct LookAhead<const C: usize, T, U> | 18 | pub struct LookAhead<const C: usize, T, U> |
| 2 | where | 19 | where |
| 3 | U: Iterator<Item=T> + Sized | 20 | U: Iterator<Item=T> + Sized |
| @@ -12,6 +29,9 @@ impl<const C: usize, T, U> LookAhead<C,T,U> | |||
| 12 | where | 29 | where |
| 13 | U: Iterator<Item=T> + Sized | 30 | U: Iterator<Item=T> + Sized |
| 14 | { | 31 | { |
| 32 | /// Create a new LookAhead Iterator wrapping the provided iterator. | ||
| 33 | /// | ||
| 34 | /// This does nothing to the base iterator, it does not begin reading yet. | ||
| 15 | pub fn new(iter: U) -> Self { | 35 | pub fn new(iter: U) -> Self { |
| 16 | Self { | 36 | Self { |
| 17 | iter, | 37 | iter, |
| @@ -21,12 +41,22 @@ where | |||
| 21 | } | 41 | } |
| 22 | } | 42 | } |
| 23 | 43 | ||
| 44 | /// Look ahead into the buffered look-ahead. If you exceed the size of the | ||
| 45 | /// internal buffer (C) then this will panic. This will fill the buffer to | ||
| 46 | /// the point requested if it is not already full. This will not advance | ||
| 47 | /// the iterator, so multiple calls to peek for the same position without | ||
| 48 | /// calling next() will provide the same value and will not affect the state | ||
| 49 | /// of the interior iterator. | ||
| 24 | pub fn peek(&mut self, offset: usize) -> Option<&T> { | 50 | pub fn peek(&mut self, offset: usize) -> Option<&T> { |
| 25 | assert!(offset<C); | 51 | assert!(offset<C); |
| 26 | self.fill_to( offset ); | 52 | self.fill_to( offset ); |
| 27 | self.data[(self.cur+offset)%C].as_ref() | 53 | self.data[(self.cur+offset)%C].as_ref() |
| 28 | } | 54 | } |
| 29 | 55 | ||
| 56 | /// Fills the internal buffer to the requested level by calling next() on | ||
| 57 | /// the underlying iterator. This will call the underlying iterator for | ||
| 58 | /// each position to fill, even if the next() function has returned None. | ||
| 59 | /// It will simply fill the lookahead buffer with None values. | ||
| 30 | fn fill_to(&mut self, qty: usize) { | 60 | fn fill_to(&mut self, qty: usize) { |
| 31 | for i in self.fill..=qty { | 61 | for i in self.fill..=qty { |
| 32 | if let Some(x) = self.iter.next() { | 62 | if let Some(x) = self.iter.next() { |
| @@ -43,6 +73,10 @@ where | |||
| 43 | { | 73 | { |
| 44 | type Item=T; | 74 | type Item=T; |
| 45 | 75 | ||
| 76 | /// Follows the standard rules for an iterator. This will only call the | ||
| 77 | /// underlying iterator's next function if our lookahead buffer is empty. | ||
| 78 | /// Otherwise this is a pass-through. It does nothing special in the case | ||
| 79 | /// of a None response from the underlying iterator. | ||
| 46 | fn next(&mut self) -> Option<T> { | 80 | fn next(&mut self) -> Option<T> { |
| 47 | if self.fill == 0 { | 81 | if self.fill == 0 { |
| 48 | self.iter.next() | 82 | self.iter.next() |
