diff --git a/errors.go b/errors.go index 7152e3a..1322c3d 100644 --- a/errors.go +++ b/errors.go @@ -6,4 +6,5 @@ import ( var ( ErrNothingToUnread = adverr.NewErrTmpl("ErrNothingToUnread", "Unreading failed because there wasn't any Read yet") + ErrPopFailed = adverr.NewErrTmpl("ErrPopFailed", "stack cannot be popped. It was never pushed") ) diff --git a/go.mod b/go.mod index b1bfac8..f4b54ef 100644 --- a/go.mod +++ b/go.mod @@ -5,4 +5,7 @@ go 1.19 require ( git.milar.in/milarin/adverr v1.1.0 git.milar.in/milarin/ds v0.0.2 + git.milar.in/milarin/slices v0.0.8 ) + +require git.milar.in/milarin/gmath v0.0.3 // indirect diff --git a/go.sum b/go.sum index 010c479..cabd037 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ git.milar.in/milarin/adverr v1.1.0 h1:jD9WnOvs40lfMhvqQ7cllOaRJNBMWr1f07/s9jAadp0= git.milar.in/milarin/adverr v1.1.0/go.mod h1:joU9sBb7ySyNv4SpTXB0Z4o1mjXsArBw4N27wjgzj9E= -git.milar.in/milarin/ds v0.0.0-20230120204927-7dc44b9cd222 h1:r7gcMqltPqk9U2/+LzEN5dooZ5wiQ8C7SI/nK6T0KjA= -git.milar.in/milarin/ds v0.0.0-20230120204927-7dc44b9cd222/go.mod h1:HJK7QERcRvV9j7xzEocrKUtW+1q4JB1Ly4Bj54chfwI= -git.milar.in/milarin/ds v0.0.1 h1:ov4Rp+QiB3xtmV0a4eC+LluxWEOvbykgW+wCchYEp9o= -git.milar.in/milarin/ds v0.0.1/go.mod h1:HJK7QERcRvV9j7xzEocrKUtW+1q4JB1Ly4Bj54chfwI= git.milar.in/milarin/ds v0.0.2 h1:vCA3mDxZUNfvHpzrdz7SeBUKiPn74NTopo915IUG7I0= git.milar.in/milarin/ds v0.0.2/go.mod h1:HJK7QERcRvV9j7xzEocrKUtW+1q4JB1Ly4Bj54chfwI= +git.milar.in/milarin/gmath v0.0.3 h1:ii6rKNItS55O/wtIFhD1cTN2BMwDZjTBmiOocKURvxM= +git.milar.in/milarin/gmath v0.0.3/go.mod h1:HDLftG5RLpiNGKiIWh+O2G1PYkNzyLDADO8Cd/1abiE= +git.milar.in/milarin/slices v0.0.8 h1:qN9TE3tkArdTixMKSnwvNPcApwAjxpLVwA5a9k1rm2s= +git.milar.in/milarin/slices v0.0.8/go.mod h1:qMhdtMnfWswc1rHpwgNw33lB84aNEkdBn5BDiYA+G3k= diff --git a/reader.go b/reader.go index d8ece28..7f71f13 100644 --- a/reader.go +++ b/reader.go @@ -6,19 +6,24 @@ import ( "strings" "git.milar.in/milarin/ds" + "git.milar.in/milarin/slices" ) type Reader struct { - buf ds.Stack[posRune] - src *bufio.Reader - pos *Position + 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}, + buf: ds.NewArrayStack[posRune](), + src: bufio.NewReader(r), + pos: &Position{Index: 0, Line: 1, Column: 1}, + index: 0, + indices: ds.NewArrayStack[uint64](), } } @@ -43,6 +48,7 @@ func (r *Reader) Rune() (rune, error) { if err == nil { r.buf.Push(r.psrn(rn)) r.pos.Advance(rn) + r.index++ } return rn, err } @@ -99,6 +105,8 @@ func (r *Reader) UnreadRune() error { r.src = prependRune(rn.Rune, r.src) } + r.index-- + return nil } @@ -250,8 +258,44 @@ func (r *Reader) ExpectOneOfString(str ...string) (string, bool, error) { return "", false, nil } -// Commit clears the internal buffer and therefore removes all data which were already read. -// After calling Commit any unreads will return ErrNothingToUnread until another read occured. -func (r *Reader) Commit() { - r.buf.Clear() +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 }