diff --git a/pos_rune.go b/pos_rune.go new file mode 100644 index 0000000..2d90b34 --- /dev/null +++ b/pos_rune.go @@ -0,0 +1,6 @@ +package bufr + +type posRune struct { + Rune rune + Pos Position +} diff --git a/position.go b/position.go new file mode 100644 index 0000000..44d38ad --- /dev/null +++ b/position.go @@ -0,0 +1,17 @@ +package bufr + +type Position struct { + Index int + Line int + Column int +} + +func (p *Position) Advance(rn rune) { + p.Index++ + if rn == '\n' { + p.Line++ + p.Column = 0 + } else { + p.Column++ + } +} diff --git a/reader.go b/reader.go index c5bfd58..342b710 100644 --- a/reader.go +++ b/reader.go @@ -9,22 +9,36 @@ import ( ) type Reader struct { - buf *dstruct.Stack[rune] + buf *dstruct.Stack[posRune] src *bufio.Reader + pos *Position } func NewReader(r io.Reader) *Reader { return &Reader{ - buf: new(dstruct.Stack[rune]), + buf: new(dstruct.Stack[posRune]), src: bufio.NewReader(r), + pos: &Position{}, } } +func (r *Reader) psrn(rn rune) posRune { + return posRune{ + Rune: rn, + Pos: *r.pos, + } +} + +func (r *Reader) Pos() (index, line, column int) { + return r.pos.Index, r.pos.Line, r.pos.Column +} + // Rune returns the next rune in r func (r *Reader) Rune() (rune, error) { rn, _, err := r.src.ReadRune() if err == nil { - r.buf.Push(rn) + r.buf.Push(r.psrn(rn)) + r.pos.Advance(rn) } return rn, err } @@ -38,9 +52,12 @@ func (r *Reader) UnreadRune() error { } if err := r.src.UnreadRune(); err == nil { - r.buf.Pop() + rn := r.buf.Pop() + *r.pos = rn.Pos } else { - r.src = prependRune(r.buf.Pop(), r.src) + rn := r.buf.Pop() + *r.pos = rn.Pos + r.src = prependRune(rn.Rune, r.src) } return nil diff --git a/reader_test.go b/reader_test.go new file mode 100644 index 0000000..5a2de01 --- /dev/null +++ b/reader_test.go @@ -0,0 +1,24 @@ +package bufr + +import ( + "fmt" + "strings" + "testing" +) + +func TestPos(t *testing.T) { + r := NewReader(strings.NewReader("hello world\nsecond line")) + + unread := false + for rn, err := r.Rune(); err == nil; rn, err = r.Rune() { + index, line, col := r.Pos() + fmt.Println(string(rn), index, line, col) + + if !unread && rn == '\n' { + for i := 0; i < 5; i++ { + r.UnreadRune() + } + unread = true + } + } +}