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