source.mx raw

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