package bufr import ( "bufio" "io" "strings" "git.milar.in/milarin/ds" "git.milar.in/milarin/slices" ) type Reader struct { buf ds.Stack[posRune] indices ds.Stack[uint64] index uint64 src *bufio.Reader pos *Position } func New(r io.Reader) *Reader { return &Reader{ buf: ds.NewArrayStack[posRune](), src: bufio.NewReader(r), pos: &Position{Index: 0, Line: 1, Column: 1}, index: 0, indices: ds.NewArrayStack[uint64](), } } func NewFromString(str string) *Reader { return New(strings.NewReader(str)) } func (r *Reader) psrn(rn rune) posRune { return posRune{ Rune: rn, Pos: *r.pos, } } func (r *Reader) Pos() Position { return *r.pos } // Rune returns the next rune in r func (r *Reader) Rune() (rune, error) { rn, _, err := r.src.ReadRune() if err == nil { r.buf.Push(r.psrn(rn)) r.pos.Advance(rn) r.index++ } return rn, err } func (r *Reader) Buffered() int { return r.src.Buffered() } // PeekRune returns the next rune in r without advancing reader position. // The next read will return the same rune again. func (r *Reader) PeekRune() (rune, error) { rn, err := r.Rune() if err != nil { return 0, err } if err := r.UnreadRune(); err != nil { return 0, err } return rn, nil } // String returns the next len runes in r as a string. // If an error occurs, both the already read string and the error will be returned func (r *Reader) String(len int) (string, error) { str := "" for i := 0; i < len; i++ { rn, err := r.Rune() if err != nil { return str, err } str += string(rn) } return str, nil } // UnreadRune unreads the last rune. // The next read will include the unread rune. // It returns ErrNothingToUnread if there wasn't any read yet func (r *Reader) UnreadRune() error { if r.buf.Empty() { return ErrNothingToUnread.New() } if err := r.src.UnreadRune(); err == nil { rn := r.buf.Pop() *r.pos = rn.Pos } else { rn := r.buf.Pop() *r.pos = rn.Pos r.src = prependRune(rn.Rune, r.src) } r.index-- return nil } // UnreadString calls UnreadRune for each rune in str + one addtional rune for the separator rune // The actual runes in str are irrelevant. // Only the rune count of str determines the amount of UnreadRune calls. // The first error occured will be returned immediately. func (r *Reader) UnreadString(str string) error { for range str { err := r.UnreadRune() if err != nil { return err } } return r.UnreadRune() } // UnreadRunes calls UnreadRune n times func (r *Reader) UnreadRunes(n int) error { for i := 0; i < n; i++ { err := r.UnreadRune() if err != nil { return err } } return nil } // StringWhile reads runes and calls all functions for each one. // It returns all runes as a string for which any function returned true. // It stops when all functions returned false or an error occured. // The rune for which that function returned false will not be unread. func (r *Reader) StringWhile(f ...RuneFunc) (string, error) { s := new(strings.Builder) var rn rune var err error for rn, err = r.Rune(); err == nil && findFirstTrue(rn, f); rn, err = r.Rune() { s.WriteRune(rn) } return s.String(), err } // PeekStringWhile acts as StringWhile but does not advance reader position func (r *Reader) PeekStringWhile(f ...RuneFunc) (string, error) { str, err := r.StringWhile(f...) if err != nil { return "", err } if err := r.UnreadString(str); err != nil { return "", err } return str, nil } // StringUntil reads runes and calls all functions for each one. // It returns all runes as a string for which all functions returned true. // It stops when any function returns false or an error occured. // The rune for which that function returned false will not be unread. func (r *Reader) StringUntil(f ...RuneFunc) (string, error) { return r.StringWhile(func(rn rune) bool { return !findFirstTrue(rn, f) }) } // PeekStringUntil acts as StringUntil but does not advance reader position func (r *Reader) PeekStringUntil(f ...RuneFunc) (string, error) { str, err := r.StringUntil(f...) if err != nil { return "", err } if err := r.UnreadString(str); err != nil { return "", err } return str, nil } // SkipUntil acts as StringUntil but discards the string // The rune for which that function returned false will be unread. func (r *Reader) SkipUntil(f ...RuneFunc) error { _, err := r.StringUntil(f...) if err != nil { return err } return r.UnreadRune() } // SkipWhile acts as StringWhile but discards the string. // The rune for which that function returned false will be unread. func (r *Reader) SkipWhile(f ...RuneFunc) error { _, err := r.StringWhile(f...) if err != nil { return err } return r.UnreadRune() } // ExpectRune returns true if any function returns true for the next rune read from r func (r *Reader) ExpectRune(f ...RuneFunc) (bool, error) { rn, err := r.Rune() if err != nil { return false, err } return findFirstTrue(rn, f), nil } // ExpectString calls ExpectRune for each rune in str successively. // If the expected string was not found, all read runes will be unread func (r *Reader) ExpectString(str string) (bool, error) { read := 0 for _, rn := range str { ok, err := r.ExpectRune(Is(rn)) if err != nil { return false, err } read++ if !ok { if err := r.UnreadRunes(read); err != nil { return false, err } return false, nil } } return true, nil } // ExpectOneOfString calls ExpectString for each string successively // and returns the string which first matched. // The boolean value is true if any string was found. // The returned string will not be unread. func (r *Reader) ExpectOneOfString(str ...string) (string, bool, error) { for _, s := range str { ok, err := r.ExpectString(s) if err != nil { return "", false, err } if ok { return s, true, nil } } return "", false, nil } func (r *Reader) Push() { r.indices.Push(r.index) } func (r *Reader) Pop() ([]rune, error) { if r.indices.Empty() { return nil, ErrPopFailed.New() } lastIndex := r.indices.Pop() currentIndex := r.index if lastIndex < currentIndex { values := make([]rune, 0, int(currentIndex-lastIndex)) for i := 0; i < int(currentIndex-lastIndex); i++ { err := r.UnreadRune() if err != nil { return nil, err } value, err := r.PeekRune() if err != nil { return nil, err } values = append(values, value) } return slices.Reverse(values), nil } else if lastIndex > currentIndex { values := make([]rune, 0, int(lastIndex-currentIndex)) for i := 0; i < int(lastIndex-currentIndex); i++ { value, err := r.Rune() if err != nil { return nil, err } values = append(values, value) } return values, nil } return []rune{}, nil }