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