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