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 func (srcDone) String() 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 const Linebase = 1
66 const Colbase = 1
67
68 func (s *Source) pos() (line, col uint32) {
69 return Linebase + s.line, Colbase + s.col
70 }
71
72 func (s *Source) Debugpos() (line, col uint32) {
73 return Linebase + s.line, Colbase + s.col
74 }
75
76 func (s *Source) error(msg string) {
77 line, col := s.pos()
78 s.errh(line, col, msg)
79 }
80
81 func (s *Source) start() { s.b = s.r - s.chw }
82 func (s *Source) stop() { s.b = -1 }
83 func (s *Source) segment() []byte { return s.buf[s.b : s.r-s.chw] }
84
85 // segmentCopy returns a copy of the current segment that survives buffer
86 // reallocation in fill(). In Moxie string=[]byte so string(segment()) does
87 // NOT copy - the returned slice still aliases s.buf.
88 func (s *Source) segmentCopy() []byte {
89 b := s.buf[s.b : s.r-s.chw]
90 c := []byte{:len(b)}
91 copy(c, b)
92 return c
93 }
94
95 func (s *Source) rewind() {
96 if s.b < 0 {
97 panic("no active segment")
98 }
99 s.col -= uint32(s.r - s.b)
100 s.r = s.b
101 s.nextch()
102 }
103
104 func (s *Source) nextch() {
105 redo:
106 s.col += uint32(s.chw)
107 if s.ch == '\n' {
108 s.line++
109 s.col = 0
110 }
111
112 if s.ch = rune(s.buf[s.r]); s.ch < sentinel {
113 s.r++
114 s.chw = 1
115 if s.ch == 0 {
116 s.error("invalid NUL character")
117 goto redo
118 }
119 return
120 }
121
122 for s.e-s.r < utf8.UTFMax && !utf8.FullRune(s.buf[s.r:s.e]) && s.ioerr == nil {
123 s.fill()
124 }
125
126 if s.r == s.e {
127 if s.ioerr != io.EOF && !s.noFill {
128 s.error("I/O error: " | s.ioerr.Error())
129 s.ioerr = nil
130 }
131 s.ch = -1
132 s.chw = 0
133 return
134 }
135
136 var w int
137 s.ch, w = utf8.DecodeRune(s.buf[s.r:s.e])
138 s.chw = int32(w)
139 s.r += s.chw
140
141 if s.ch == utf8.RuneError && s.chw == 1 {
142 s.error("invalid UTF-8 encoding")
143 goto redo
144 }
145
146 const BOM = 0xfeff
147 if s.ch == BOM {
148 if s.line > 0 || s.col > 0 {
149 s.error("invalid BOM in the middle of the file")
150 }
151 goto redo
152 }
153 }
154
155 func (s *Source) fill() {
156 if s.noFill {
157 s.ioerr = srcDone{}
158 return
159 }
160 b := s.r
161 if s.b >= 0 {
162 b = s.b
163 s.b = 0
164 }
165 content := s.buf[b:s.e]
166
167 if len(content)*2 > len(s.buf) {
168 s.buf = []byte{:nextSize(int32(len(s.buf)))}
169 copy(s.buf, content)
170 } else if b > 0 {
171 copy(s.buf, content)
172 }
173 s.r -= b
174 s.e -= b
175
176 for i := 0; i < 10; i++ {
177 var n int32
178 var nn int
179 nn, s.ioerr = s.in.Read(s.buf[s.e : len(s.buf)-1])
180 n = int32(nn)
181 if n < 0 {
182 panic("negative read")
183 }
184 if n > 0 || s.ioerr != nil {
185 s.e += n
186 s.buf[s.e] = sentinel
187 return
188 }
189 }
190
191 s.buf[s.e] = sentinel
192 s.ioerr = io.ErrNoProgress
193 }
194
195 func nextSize(size int32) int32 {
196 const min = 4 << 10
197 const max = 1 << 20
198 if size < min {
199 return min
200 }
201 if size <= max {
202 return size << 1
203 }
204 return size + max
205 }
206