source.mx raw

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