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 int
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 int
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