package main import ( "io" "unicode/utf8" ) type Source struct { in io.Reader errh func(line, col uint32, msg string) buf []byte ioerr error b, r, e int32 line, col uint32 ch rune chw int32 } const sentinel = utf8.RuneSelf func (s *Source) init(in io.Reader, errh func(line, col uint32, msg string)) { s.in = in s.errh = errh if s.buf == nil { s.buf = []byte{:nextSize(0)} } s.buf[0] = sentinel s.ioerr = nil s.b, s.r, s.e = -1, 0, 0 s.line, s.col = 0, 0 s.ch = ' ' s.chw = 0 } const Linebase = 1 const Colbase = 1 func (s *Source) pos() (line, col uint32) { return Linebase + s.line, Colbase + s.col } func (s *Source) Debugpos() (line, col uint32) { return Linebase + s.line, Colbase + s.col } func (s *Source) error(msg string) { line, col := s.pos() s.errh(line, col, msg) } func (s *Source) start() { s.b = s.r - s.chw } func (s *Source) stop() { s.b = -1 } func (s *Source) segment() []byte { return s.buf[s.b : s.r-s.chw] } func (s *Source) rewind() { if s.b < 0 { panic("no active segment") } s.col -= uint32(s.r - s.b) s.r = s.b s.nextch() } func (s *Source) nextch() { redo: s.col += uint32(s.chw) if s.ch == '\n' { s.line++ s.col = 0 } if s.ch = rune(s.buf[s.r]); s.ch < sentinel { s.r++ s.chw = 1 if s.ch == 0 { s.error("invalid NUL character") goto redo } return } for s.e-s.r < utf8.UTFMax && !utf8.FullRune(s.buf[s.r:s.e]) && s.ioerr == nil { s.fill() } if s.r == s.e { if s.ioerr != io.EOF { s.error("I/O error: " | s.ioerr.Error()) s.ioerr = nil } s.ch = -1 s.chw = 0 return } var w int s.ch, w = utf8.DecodeRune(s.buf[s.r:s.e]) s.chw = int32(w) s.r += s.chw if s.ch == utf8.RuneError && s.chw == 1 { s.error("invalid UTF-8 encoding") goto redo } const BOM = 0xfeff if s.ch == BOM { if s.line > 0 || s.col > 0 { s.error("invalid BOM in the middle of the file") } goto redo } } func (s *Source) fill() { b := s.r if s.b >= 0 { b = s.b s.b = 0 } content := s.buf[b:s.e] if len(content)*2 > len(s.buf) { s.buf = []byte{:nextSize(int32(len(s.buf)))} copy(s.buf, content) } else if b > 0 { copy(s.buf, content) } s.r -= b s.e -= b for i := 0; i < 10; i++ { var n int32 var nn int nn, s.ioerr = s.in.Read(s.buf[s.e : len(s.buf)-1]) n = int32(nn) if n < 0 { panic("negative read") } if n > 0 || s.ioerr != nil { s.e += n s.buf[s.e] = sentinel return } } s.buf[s.e] = sentinel s.ioerr = io.ErrNoProgress } func nextSize(size int32) int32 { const min = 4 << 10 const max = 1 << 20 if size < min { return min } if size <= max { return size << 1 } return size + max }