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