github.com/octohelm/cuemod@v0.9.4/internal/cmd/go/internals/imports/read.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Copied from Go distribution src/go/build/read.go. 6 7 package imports 8 9 import ( 10 "bufio" 11 "bytes" 12 "errors" 13 "io" 14 "unicode/utf8" 15 ) 16 17 type importReader struct { 18 b *bufio.Reader 19 buf []byte 20 peek byte 21 err error 22 eof bool 23 nerr int 24 } 25 26 var bom = []byte{0xef, 0xbb, 0xbf} 27 28 func newImportReader(b *bufio.Reader) *importReader { 29 // Remove leading UTF-8 BOM. 30 // Per https://golang.org/ref/spec#Source_code_representation: 31 // a compiler may ignore a UTF-8-encoded byte order mark (U+FEFF) 32 // if it is the first Unicode code point in the source text. 33 if leadingBytes, err := b.Peek(3); err == nil && bytes.Equal(leadingBytes, bom) { 34 b.Discard(3) 35 } 36 return &importReader{b: b} 37 } 38 39 func isIdent(c byte) bool { 40 return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf 41 } 42 43 var ( 44 errSyntax = errors.New("syntax error") 45 errNUL = errors.New("unexpected NUL in input") 46 ) 47 48 // syntaxError records a syntax error, but only if an I/O error has not already been recorded. 49 func (r *importReader) syntaxError() { 50 if r.err == nil { 51 r.err = errSyntax 52 } 53 } 54 55 // readByte reads the next byte from the input, saves it in buf, and returns it. 56 // If an error occurs, readByte records the error in r.err and returns 0. 57 func (r *importReader) readByte() byte { 58 c, err := r.b.ReadByte() 59 if err == nil { 60 r.buf = append(r.buf, c) 61 if c == 0 { 62 err = errNUL 63 } 64 } 65 if err != nil { 66 if err == io.EOF { 67 r.eof = true 68 } else if r.err == nil { 69 r.err = err 70 } 71 c = 0 72 } 73 return c 74 } 75 76 // peekByte returns the next byte from the input reader but does not advance beyond it. 77 // If skipSpace is set, peekByte skips leading spaces and comments. 78 func (r *importReader) peekByte(skipSpace bool) byte { 79 if r.err != nil { 80 if r.nerr++; r.nerr > 10000 { 81 panic("go/build: import reader looping") 82 } 83 return 0 84 } 85 86 // Use r.peek as first input byte. 87 // Don't just return r.peek here: it might have been left by peekByte(false) 88 // and this might be peekByte(true). 89 c := r.peek 90 if c == 0 { 91 c = r.readByte() 92 } 93 for r.err == nil && !r.eof { 94 if skipSpace { 95 // For the purposes of this reader, semicolons are never necessary to 96 // understand the input and are treated as spaces. 97 switch c { 98 case ' ', '\f', '\t', '\r', '\n', ';': 99 c = r.readByte() 100 continue 101 102 case '/': 103 c = r.readByte() 104 if c == '/' { 105 for c != '\n' && r.err == nil && !r.eof { 106 c = r.readByte() 107 } 108 } else if c == '*' { 109 var c1 byte 110 for (c != '*' || c1 != '/') && r.err == nil { 111 if r.eof { 112 r.syntaxError() 113 } 114 c, c1 = c1, r.readByte() 115 } 116 } else { 117 r.syntaxError() 118 } 119 c = r.readByte() 120 continue 121 } 122 } 123 break 124 } 125 r.peek = c 126 return r.peek 127 } 128 129 // nextByte is like peekByte but advances beyond the returned byte. 130 func (r *importReader) nextByte(skipSpace bool) byte { 131 c := r.peekByte(skipSpace) 132 r.peek = 0 133 return c 134 } 135 136 // readKeyword reads the given keyword from the input. 137 // If the keyword is not present, readKeyword records a syntax error. 138 func (r *importReader) readKeyword(kw string) { 139 r.peekByte(true) 140 for i := 0; i < len(kw); i++ { 141 if r.nextByte(false) != kw[i] { 142 r.syntaxError() 143 return 144 } 145 } 146 if isIdent(r.peekByte(false)) { 147 r.syntaxError() 148 } 149 } 150 151 // readIdent reads an identifier from the input. 152 // If an identifier is not present, readIdent records a syntax error. 153 func (r *importReader) readIdent() { 154 c := r.peekByte(true) 155 if !isIdent(c) { 156 r.syntaxError() 157 return 158 } 159 for isIdent(r.peekByte(false)) { 160 r.peek = 0 161 } 162 } 163 164 // readString reads a quoted string literal from the input. 165 // If an identifier is not present, readString records a syntax error. 166 func (r *importReader) readString(save *[]string) { 167 switch r.nextByte(true) { 168 case '`': 169 start := len(r.buf) - 1 170 for r.err == nil { 171 if r.nextByte(false) == '`' { 172 if save != nil { 173 *save = append(*save, string(r.buf[start:])) 174 } 175 break 176 } 177 if r.eof { 178 r.syntaxError() 179 } 180 } 181 case '"': 182 start := len(r.buf) - 1 183 for r.err == nil { 184 c := r.nextByte(false) 185 if c == '"' { 186 if save != nil { 187 *save = append(*save, string(r.buf[start:])) 188 } 189 break 190 } 191 if r.eof || c == '\n' { 192 r.syntaxError() 193 } 194 if c == '\\' { 195 r.nextByte(false) 196 } 197 } 198 default: 199 r.syntaxError() 200 } 201 } 202 203 // readImport reads an import clause - optional identifier followed by quoted string - 204 // from the input. 205 func (r *importReader) readImport(imports *[]string) { 206 c := r.peekByte(true) 207 if c == '.' { 208 r.peek = 0 209 } else if isIdent(c) { 210 r.readIdent() 211 } 212 r.readString(imports) 213 } 214 215 // ReadComments is like io.ReadAll, except that it only reads the leading 216 // block of comments in the file. 217 func ReadComments(f io.Reader) ([]byte, error) { 218 r := newImportReader(bufio.NewReader(f)) 219 r.peekByte(true) 220 if r.err == nil && !r.eof { 221 // Didn't reach EOF, so must have found a non-space byte. Remove it. 222 r.buf = r.buf[:len(r.buf)-1] 223 } 224 return r.buf, r.err 225 } 226 227 // ReadImports is like io.ReadAll, except that it expects a Go file as input 228 // and stops reading the input once the imports have completed. 229 func ReadImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) { 230 r := newImportReader(bufio.NewReader(f)) 231 232 r.readKeyword("package") 233 r.readIdent() 234 for r.peekByte(true) == 'i' { 235 r.readKeyword("import") 236 if r.peekByte(true) == '(' { 237 r.nextByte(false) 238 for r.peekByte(true) != ')' && r.err == nil { 239 r.readImport(imports) 240 } 241 r.nextByte(false) 242 } else { 243 r.readImport(imports) 244 } 245 } 246 247 // If we stopped successfully before EOF, we read a byte that told us we were done. 248 // Return all but that last byte, which would cause a syntax error if we let it through. 249 if r.err == nil && !r.eof { 250 return r.buf[:len(r.buf)-1], nil 251 } 252 253 // If we stopped for a syntax error, consume the whole file so that 254 // we are sure we don't change the errors that go/parser returns. 255 if r.err == errSyntax && !reportSyntaxError { 256 r.err = nil 257 for r.err == nil && !r.eof { 258 r.readByte() 259 } 260 } 261 262 return r.buf, r.err 263 }