blob: 79f68b4255e611b93b47bc9a84407bb80478c453 [file] [log] [blame]
Marcel van Lohuizenbc4d65d2018-12-10 15:40:02 +01001// Copyright 2018 The CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package load
16
17import (
18 "bufio"
19 "errors"
20 "io"
21 "unicode/utf8"
22)
23
24type importReader struct {
25 b *bufio.Reader
26 buf []byte
27 peek byte
28 err error
29 eof bool
30 nerr int
31}
32
33func isIdent(c byte) bool {
34 return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
35}
36
37var (
38 errSyntax = errors.New("syntax error")
39 errNUL = errors.New("unexpected NUL in input")
40)
41
42// syntaxError records a syntax error, but only if an I/O error has not already been recorded.
43func (r *importReader) syntaxError() {
44 if r.err == nil {
45 r.err = errSyntax
46 }
47}
48
49// readByte reads the next byte from the input, saves it in buf, and returns it.
50// If an error occurs, readByte records the error in r.err and returns 0.
51func (r *importReader) readByte() byte {
52 c, err := r.b.ReadByte()
53 if err == nil {
54 r.buf = append(r.buf, c)
55 if c == 0 {
56 err = errNUL
57 }
58 }
59 if err != nil {
60 if err == io.EOF {
61 r.eof = true
62 } else if r.err == nil {
63 r.err = err
64 }
65 c = 0
66 }
67 return c
68}
69
70// peekByte returns the next byte from the input reader but does not advance beyond it.
71// If skipSpace is set, peekByte skips leading spaces and comments.
72func (r *importReader) peekByte(skipSpace bool) byte {
73 if r.err != nil {
74 if r.nerr++; r.nerr > 10000 {
75 panic("go/build: import reader looping")
76 }
77 return 0
78 }
79
80 // Use r.peek as first input byte.
81 // Don't just return r.peek here: it might have been left by peekByte(false)
82 // and this might be peekByte(true).
83 c := r.peek
84 if c == 0 {
85 c = r.readByte()
86 }
87 for r.err == nil && !r.eof {
88 if skipSpace {
89 // For the purposes of this reader, semicolons are never necessary to
90 // understand the input and are treated as spaces.
91 switch c {
92 case ' ', '\f', '\t', '\r', '\n', ';':
93 c = r.readByte()
94 continue
95
96 case '/':
97 c = r.readByte()
98 if c == '/' {
99 for c != '\n' && r.err == nil && !r.eof {
100 c = r.readByte()
101 }
102 } else if c == '*' {
103 var c1 byte
104 for (c != '*' || c1 != '/') && r.err == nil {
105 if r.eof {
106 r.syntaxError()
107 }
108 c, c1 = c1, r.readByte()
109 }
110 } else {
111 r.syntaxError()
112 }
113 c = r.readByte()
114 continue
115 }
116 }
117 break
118 }
119 r.peek = c
120 return r.peek
121}
122
123// nextByte is like peekByte but advances beyond the returned byte.
124func (r *importReader) nextByte(skipSpace bool) byte {
125 c := r.peekByte(skipSpace)
126 r.peek = 0
127 return c
128}
129
130// readKeyword reads the given keyword from the input.
131// If the keyword is not present, readKeyword records a syntax error.
132func (r *importReader) readKeyword(kw string) {
133 r.peekByte(true)
134 for i := 0; i < len(kw); i++ {
135 if r.nextByte(false) != kw[i] {
136 r.syntaxError()
137 return
138 }
139 }
140 if isIdent(r.peekByte(false)) {
141 r.syntaxError()
142 }
143}
144
145// readIdent reads an identifier from the input.
146// If an identifier is not present, readIdent records a syntax error.
147func (r *importReader) readIdent() {
148 c := r.peekByte(true)
149 if !isIdent(c) {
150 r.syntaxError()
151 return
152 }
153 for isIdent(r.peekByte(false)) {
154 r.peek = 0
155 }
156}
157
158// readString reads a quoted string literal from the input.
159// If an identifier is not present, readString records a syntax error.
160func (r *importReader) readString(save *[]string) {
161 switch r.nextByte(true) {
162 case '`':
163 start := len(r.buf) - 1
164 for r.err == nil {
165 if r.nextByte(false) == '`' {
166 if save != nil {
167 *save = append(*save, string(r.buf[start:]))
168 }
169 break
170 }
171 if r.eof {
172 r.syntaxError()
173 }
174 }
175 case '"':
176 start := len(r.buf) - 1
177 for r.err == nil {
178 c := r.nextByte(false)
179 if c == '"' {
180 if save != nil {
181 *save = append(*save, string(r.buf[start:]))
182 }
183 break
184 }
185 if r.eof || c == '\n' {
186 r.syntaxError()
187 }
188 if c == '\\' {
189 r.nextByte(false)
190 }
191 }
192 default:
193 r.syntaxError()
194 }
195}
196
197// readImport reads an import clause - optional identifier followed by quoted string -
198// from the input.
199func (r *importReader) readImport(imports *[]string) {
200 c := r.peekByte(true)
201 if c == '.' {
202 r.peek = 0
203 } else if isIdent(c) {
204 r.readIdent()
205 }
206 r.readString(imports)
207}
208
209// readComments is like ioutil.ReadAll, except that it only reads the leading
210// block of comments in the file.
211func readComments(f io.Reader) ([]byte, error) {
212 r := &importReader{b: bufio.NewReader(f)}
213 r.peekByte(true)
214 if r.err == nil && !r.eof {
215 // Didn't reach EOF, so must have found a non-space byte. Remove it.
216 r.buf = r.buf[:len(r.buf)-1]
217 }
218 return r.buf, r.err
219}
220
221// readImports is like ioutil.ReadAll, except that it expects a Go file as input
222// and stops reading the input once the imports have completed.
223func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
224 r := &importReader{b: bufio.NewReader(f)}
225
226 r.readKeyword("package")
227 r.readIdent()
228 for r.peekByte(true) == 'i' {
229 r.readKeyword("import")
230 if r.peekByte(true) == '(' {
231 r.nextByte(false)
232 for r.peekByte(true) != ')' && r.err == nil {
233 r.readImport(imports)
234 }
235 r.nextByte(false)
236 } else {
237 r.readImport(imports)
238 }
239 }
240
241 // If we stopped successfully before EOF, we read a byte that told us we were done.
242 // Return all but that last byte, which would cause a syntax error if we let it through.
243 if r.err == nil && !r.eof {
244 return r.buf[:len(r.buf)-1], nil
245 }
246
247 // If we stopped for a syntax error, consume the whole file so that
248 // we are sure we don't change the errors that go/parser returns.
249 if r.err == errSyntax && !reportSyntaxError {
250 r.err = nil
251 for r.err == nil && !r.eof {
252 r.readByte()
253 }
254 }
255
256 return r.buf, r.err
257}