source.mx raw

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