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