github.com/c9s/go@v0.0.0-20180120015821-984e81f64e0c/src/cmd/dist/imports.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 // This file is forked from go/build/read.go. 6 // (cmd/dist must not import go/build because we do not want it to be 7 // sensitive to the specific version of go/build present in $GOROOT_BOOTSTRAP.) 8 9 package main 10 11 import ( 12 "bufio" 13 "errors" 14 "io" 15 "strconv" 16 "strings" 17 "unicode/utf8" 18 ) 19 20 type importReader struct { 21 b *bufio.Reader 22 buf []byte 23 peek byte 24 err error 25 eof bool 26 nerr int 27 } 28 29 func isIdent(c byte) bool { 30 return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf 31 } 32 33 var ( 34 errSyntax = errors.New("syntax error") 35 errNUL = errors.New("unexpected NUL in input") 36 ) 37 38 // syntaxError records a syntax error, but only if an I/O error has not already been recorded. 39 func (r *importReader) syntaxError() { 40 if r.err == nil { 41 r.err = errSyntax 42 } 43 } 44 45 // readByte reads the next byte from the input, saves it in buf, and returns it. 46 // If an error occurs, readByte records the error in r.err and returns 0. 47 func (r *importReader) readByte() byte { 48 c, err := r.b.ReadByte() 49 if err == nil { 50 r.buf = append(r.buf, c) 51 if c == 0 { 52 err = errNUL 53 } 54 } 55 if err != nil { 56 if err == io.EOF { 57 r.eof = true 58 } else if r.err == nil { 59 r.err = err 60 } 61 c = 0 62 } 63 return c 64 } 65 66 // peekByte returns the next byte from the input reader but does not advance beyond it. 67 // If skipSpace is set, peekByte skips leading spaces and comments. 68 func (r *importReader) peekByte(skipSpace bool) byte { 69 if r.err != nil { 70 if r.nerr++; r.nerr > 10000 { 71 panic("go/build: import reader looping") 72 } 73 return 0 74 } 75 76 // Use r.peek as first input byte. 77 // Don't just return r.peek here: it might have been left by peekByte(false) 78 // and this might be peekByte(true). 79 c := r.peek 80 if c == 0 { 81 c = r.readByte() 82 } 83 for r.err == nil && !r.eof { 84 if skipSpace { 85 // For the purposes of this reader, semicolons are never necessary to 86 // understand the input and are treated as spaces. 87 switch c { 88 case ' ', '\f', '\t', '\r', '\n', ';': 89 c = r.readByte() 90 continue 91 92 case '/': 93 c = r.readByte() 94 if c == '/' { 95 for c != '\n' && r.err == nil && !r.eof { 96 c = r.readByte() 97 } 98 } else if c == '*' { 99 var c1 byte 100 for (c != '*' || c1 != '/') && r.err == nil { 101 if r.eof { 102 r.syntaxError() 103 } 104 c, c1 = c1, r.readByte() 105 } 106 } else { 107 r.syntaxError() 108 } 109 c = r.readByte() 110 continue 111 } 112 } 113 break 114 } 115 r.peek = c 116 return r.peek 117 } 118 119 // nextByte is like peekByte but advances beyond the returned byte. 120 func (r *importReader) nextByte(skipSpace bool) byte { 121 c := r.peekByte(skipSpace) 122 r.peek = 0 123 return c 124 } 125 126 // readKeyword reads the given keyword from the input. 127 // If the keyword is not present, readKeyword records a syntax error. 128 func (r *importReader) readKeyword(kw string) { 129 r.peekByte(true) 130 for i := 0; i < len(kw); i++ { 131 if r.nextByte(false) != kw[i] { 132 r.syntaxError() 133 return 134 } 135 } 136 if isIdent(r.peekByte(false)) { 137 r.syntaxError() 138 } 139 } 140 141 // readIdent reads an identifier from the input. 142 // If an identifier is not present, readIdent records a syntax error. 143 func (r *importReader) readIdent() { 144 c := r.peekByte(true) 145 if !isIdent(c) { 146 r.syntaxError() 147 return 148 } 149 for isIdent(r.peekByte(false)) { 150 r.peek = 0 151 } 152 } 153 154 // readString reads a quoted string literal from the input. 155 // If an identifier is not present, readString records a syntax error. 156 func (r *importReader) readString(save *[]string) { 157 switch r.nextByte(true) { 158 case '`': 159 start := len(r.buf) - 1 160 for r.err == nil { 161 if r.nextByte(false) == '`' { 162 if save != nil { 163 *save = append(*save, string(r.buf[start:])) 164 } 165 break 166 } 167 if r.eof { 168 r.syntaxError() 169 } 170 } 171 case '"': 172 start := len(r.buf) - 1 173 for r.err == nil { 174 c := r.nextByte(false) 175 if c == '"' { 176 if save != nil { 177 *save = append(*save, string(r.buf[start:])) 178 } 179 break 180 } 181 if r.eof || c == '\n' { 182 r.syntaxError() 183 } 184 if c == '\\' { 185 r.nextByte(false) 186 } 187 } 188 default: 189 r.syntaxError() 190 } 191 } 192 193 // readImport reads an import clause - optional identifier followed by quoted string - 194 // from the input. 195 func (r *importReader) readImport(imports *[]string) { 196 c := r.peekByte(true) 197 if c == '.' { 198 r.peek = 0 199 } else if isIdent(c) { 200 r.readIdent() 201 } 202 r.readString(imports) 203 } 204 205 // readComments is like ioutil.ReadAll, except that it only reads the leading 206 // block of comments in the file. 207 func readComments(f io.Reader) ([]byte, error) { 208 r := &importReader{b: bufio.NewReader(f)} 209 r.peekByte(true) 210 if r.err == nil && !r.eof { 211 // Didn't reach EOF, so must have found a non-space byte. Remove it. 212 r.buf = r.buf[:len(r.buf)-1] 213 } 214 return r.buf, r.err 215 } 216 217 // readimports returns the imports found in the named file. 218 func readimports(file string) []string { 219 var imports []string 220 r := &importReader{b: bufio.NewReader(strings.NewReader(readfile(file)))} 221 r.readKeyword("package") 222 r.readIdent() 223 for r.peekByte(true) == 'i' { 224 r.readKeyword("import") 225 if r.peekByte(true) == '(' { 226 r.nextByte(false) 227 for r.peekByte(true) != ')' && r.err == nil { 228 r.readImport(&imports) 229 } 230 r.nextByte(false) 231 } else { 232 r.readImport(&imports) 233 } 234 } 235 236 for i := range imports { 237 unquoted, err := strconv.Unquote(imports[i]) 238 if err != nil { 239 fatalf("reading imports from %s: %v", file, err) 240 } 241 imports[i] = unquoted 242 } 243 244 return imports 245 }