source.mx raw

   1  package main
   2  
   3  import (
   4  	"io"
   5  	"unicode/utf8"
   6  )
   7  
   8  type Source struct {
   9  	in   io.Reader
  10  	errh func(line, col uint32, msg string)
  11  
  12  	buf       []byte
  13  	ioerr     error
  14  	b, r, e   int32
  15  	line, col uint32
  16  	ch        rune
  17  	chw       int32
  18  }
  19  
  20  const sentinel = 0x80
  21  
  22  func (s *Source) release() {
  23  	s.buf = nil
  24  	s.in = nil
  25  	s.errh = nil
  26  	s.ioerr = nil
  27  }
  28  
  29  func (s *Source) init(in io.Reader, errh func(line, col uint32, msg string)) {
  30  	s.in = in
  31  	s.errh = errh
  32  
  33  	if s.buf == nil {
  34  		s.buf = []byte{:nextSize(0)}
  35  	}
  36  	s.buf[0] = sentinel
  37  	s.ioerr = nil
  38  	s.b, s.r, s.e = -1, 0, 0
  39  	s.line, s.col = 0, 0
  40  	s.ch = ' '
  41  	s.chw = 0
  42  }
  43  
  44  const Linebase = 1
  45  const Colbase = 1
  46  
  47  func (s *Source) pos() (line, col uint32) {
  48  	return Linebase + s.line, Colbase + s.col
  49  }
  50  
  51  func (s *Source) Debugpos() (line, col uint32) {
  52  	return Linebase + s.line, Colbase + s.col
  53  }
  54  
  55  func (s *Source) error(msg string) {
  56  	line, col := s.pos()
  57  	s.errh(line, col, msg)
  58  }
  59  
  60  func (s *Source) start()          { s.b = s.r - s.chw }
  61  func (s *Source) stop()           { s.b = -1 }
  62  func (s *Source) segment() []byte { return s.buf[s.b : s.r-s.chw] }
  63  
  64  // segmentCopy returns a copy of the current segment that survives buffer
  65  // reallocation in fill(). In Moxie string=[]byte so string(segment()) does
  66  // NOT copy - the returned slice still aliases s.buf.
  67  func (s *Source) segmentCopy() []byte {
  68  	b := s.buf[s.b : s.r-s.chw]
  69  	c := []byte{:len(b)}
  70  	copy(c, b)
  71  	return c
  72  }
  73  
  74  func (s *Source) rewind() {
  75  	if s.b < 0 {
  76  		panic("no active segment")
  77  	}
  78  	s.col -= uint32(s.r - s.b)
  79  	s.r = s.b
  80  	s.nextch()
  81  }
  82  
  83  func (s *Source) nextch() {
  84  redo:
  85  	s.col += uint32(s.chw)
  86  	if s.ch == '\n' {
  87  		s.line++
  88  		s.col = 0
  89  	}
  90  
  91  	if s.ch = rune(s.buf[s.r]); s.ch < sentinel {
  92  		s.r++
  93  		s.chw = 1
  94  		if s.ch == 0 {
  95  			s.error("invalid NUL character")
  96  			goto redo
  97  		}
  98  		return
  99  	}
 100  
 101  	for s.e-s.r < utf8.UTFMax && !utf8.FullRune(s.buf[s.r:s.e]) && s.ioerr == nil {
 102  		s.fill()
 103  	}
 104  
 105  	if s.r == s.e {
 106  		if s.ioerr != io.EOF {
 107  			s.error("I/O error: " | s.ioerr.Error())
 108  			s.ioerr = nil
 109  		}
 110  		s.ch = -1
 111  		s.chw = 0
 112  		return
 113  	}
 114  
 115  	var w int32
 116  	s.ch, w = utf8.DecodeRune(s.buf[s.r:s.e])
 117  	s.chw = int32(w)
 118  	s.r += s.chw
 119  
 120  	if s.ch == utf8.RuneError && s.chw == 1 {
 121  		s.error("invalid UTF-8 encoding")
 122  		goto redo
 123  	}
 124  
 125  	const BOM = 0xfeff
 126  	if s.ch == BOM {
 127  		if s.line > 0 || s.col > 0 {
 128  			s.error("invalid BOM in the middle of the file")
 129  		}
 130  		goto redo
 131  	}
 132  }
 133  
 134  func (s *Source) fill() {
 135  	b := s.r
 136  	if s.b >= 0 {
 137  		b = s.b
 138  		s.b = 0
 139  	}
 140  	content := s.buf[b:s.e]
 141  
 142  	if len(content)*2 > len(s.buf) {
 143  		s.buf = []byte{:nextSize(int32(len(s.buf)))}
 144  		copy(s.buf, content)
 145  	} else if b > 0 {
 146  		copy(s.buf, content)
 147  	}
 148  	s.r -= b
 149  	s.e -= b
 150  
 151  	for i := 0; i < 10; i++ {
 152  		var n int32
 153  		var nn int32
 154  		nn, s.ioerr = s.in.Read(s.buf[s.e : len(s.buf)-1])
 155  		n = int32(nn)
 156  		if n < 0 {
 157  			panic("negative read")
 158  		}
 159  		if n > 0 || s.ioerr != nil {
 160  			s.e += n
 161  			s.buf[s.e] = sentinel
 162  			return
 163  		}
 164  	}
 165  
 166  	s.buf[s.e] = sentinel
 167  	s.ioerr = io.ErrNoProgress
 168  }
 169  
 170  func nextSize(size int32) int32 {
 171  	const min = 4 << 10
 172  	const max = 1 << 20
 173  	if size < min {
 174  		return min
 175  	}
 176  	if size <= max {
 177  		return size << 1
 178  	}
 179  	return size + max
 180  }
 181