Skip to content

Instantly share code, notes, and snippets.

@clipperhouse
Created May 21, 2018 22:29
Show Gist options
  • Save clipperhouse/add34daff16ac22449d21d4c0afc3fce to your computer and use it in GitHub Desktop.
Save clipperhouse/add34daff16ac22449d21d4c0afc3fce to your computer and use it in GitHub Desktop.
A PeekRune method for bufio.Reader
package jargon
import (
"bufio"
"fmt"
"io"
"unicode/utf8"
)
type RuneReader struct {
*bufio.Reader
}
func NewRuneReader(r io.Reader) *RuneReader {
return &RuneReader{bufio.NewReader(r)}
}
var peekRuneBytes = []int{4, 3, 2, 1}
func (r *RuneReader) PeekRune() (rune, error) {
for peekBytes := 4; peekBytes > 0; peekBytes-- { // unicode rune can be up to 4 bytes
b, err := r.Peek(peekBytes)
if err == nil {
rune, _ := utf8.DecodeRune(b)
if rune == utf8.RuneError {
return rune, fmt.Errorf("Rune error")
}
// success
return rune, nil
}
// Otherwise, we ignore Peek errors and try the next smallest number of bytes
}
// Pretty sure we can assume EOF if we get this far
return -1, io.EOF
}
func TestPeekRune(t *testing.T) {
s := "Quick fox 世 jumped 界"
original := []rune(s)
reader := NewRuneReader(strings.NewReader(s))
lasti := len(original) - 1
for i := 0; i < len(original); i++ {
read, _, readErr := reader.ReadRune()
if readErr != nil {
t.Error(readErr)
}
if read != original[i] {
t.Errorf("Expect ReadRune to read %s, got %s", string(original[i]), string(read))
}
peek, peekErr := reader.PeekRune()
if i == lasti {
// Read the last rune above, Peek should return EOF
if peekErr != io.EOF {
t.Errorf("Expected io.EOF, got %s", peekErr)
}
}
if i < lasti {
if peekErr != nil {
t.Error(peekErr)
}
expected := original[i+1]
if peek != expected {
t.Errorf("Expect PeekRune to get %s, got %s", string(expected), string(peek))
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment