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