diff --git a/safe_reader.go b/safe_reader.go new file mode 100644 index 0000000..ea1de89 --- /dev/null +++ b/safe_reader.go @@ -0,0 +1,120 @@ +package anyreader + +import ( + "git.milar.in/milarin/ds" + "git.milar.in/milarin/slices" +) + +type SafeReader[T any] struct { + buf ds.Stack[T] + indices ds.Stack[uint64] + index uint64 + src func() T +} + +func NewSafeReaderFromSlice[T any](s []T) *SafeReader[T] { + return NewSafeReaderFromFunc(sliceToSafeFunc(s)) +} + +func NewSafeReaderFromFunc[T any](src func() T) *SafeReader[T] { + return &SafeReader[T]{ + src: src, + buf: ds.NewArrayStack[T](), + index: 0, + indices: ds.NewArrayStack[uint64](), + } +} + +func (r *SafeReader[T]) Read() T { + v := r.src() + r.buf.Push(v) + r.index++ + return v +} + +func (r *SafeReader[T]) Unread() { + if r.buf.Empty() { + return + } + + v := r.buf.Pop() + r.index-- + + returned := false + oldSrc := r.src + r.src = func() T { + if returned { + return oldSrc() + } + + returned = true + return v + } +} + +func (r *SafeReader[T]) UnreadN(n int) { + for i := 0; i < n; i++ { + r.Unread() + } +} + +func (r *SafeReader[T]) Peek() T { + value := r.Read() + r.Unread() + return value +} + +func (r *SafeReader[T]) ReadWhile(f ...func(T) bool) []T { + res := make([]T, 0, 10) + for value := r.Read(); findFirstTrue(value, f); value = r.Read() { + res = append(res, value) + } + return res +} + +func (r *SafeReader[T]) ReadUntil(f ...func(T) bool) []T { + return r.ReadWhile(func(v T) bool { return !findFirstTrue(v, f) }) +} + +func (r *SafeReader[T]) SkipUntil(f ...func(T) bool) { + r.ReadUntil(f...) + r.Unread() +} + +func (r *SafeReader[T]) SkipWhile(f ...func(T) bool) { + r.ReadWhile(f...) + r.Unread() +} + +func (r *SafeReader[T]) Expect(f ...func(T) bool) bool { + return findFirstTrue(r.Read(), f) +} + +func (r *SafeReader[T]) Push() { + r.indices.Push(r.index) +} + +func (r *SafeReader[T]) Pop() []T { + if r.indices.Empty() { + return []T{} + } + + lastIndex := r.indices.Pop() + currentIndex := r.index + if lastIndex < currentIndex { + values := make([]T, 0, int(currentIndex-lastIndex)) + for i := 0; i < int(currentIndex-lastIndex); i++ { + r.Unread() + values = append(values, r.Peek()) + } + return slices.Reverse(values) + } else if lastIndex > currentIndex { + values := make([]T, 0, int(lastIndex-currentIndex)) + for i := 0; i < int(lastIndex-currentIndex); i++ { + values = append(values, r.Read()) + } + return values + } + + return []T{} +} diff --git a/utils.go b/utils.go index 22458a8..f5e5410 100644 --- a/utils.go +++ b/utils.go @@ -14,6 +14,20 @@ func sliceToFunc[T any](s []T) func() (T, error) { } } +func sliceToSafeFunc[T any](s []T) func() T { + i := 0 + return func() T { + c := i + + if c >= len(s) { + return *new(T) + } + + i++ + return s[c] + } +} + func findFirstTrue[T any](value T, functions []func(T) bool) bool { for _, f := range functions { if f(value) {