github.com/shijuvar/go@v0.0.0-20141209052335-e8f13700b70c/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() { 150 switch r.nextByte(true) { 151 case '`': 152 for r.err == nil { 153 if r.nextByte(false) == '`' { 154 break 155 } 156 if r.eof { 157 r.syntaxError() 158 } 159 } 160 case '"': 161 for r.err == nil { 162 c := r.nextByte(false) 163 if c == '"' { 164 break 165 } 166 if r.eof || c == '\n' { 167 r.syntaxError() 168 } 169 if c == '\\' { 170 r.nextByte(false) 171 } 172 } 173 default: 174 r.syntaxError() 175 } 176 } 177 178 // readImport reads an import clause - optional identifier followed by quoted string - 179 // from the input. 180 func (r *importReader) readImport() { 181 c := r.peekByte(true) 182 if c == '.' { 183 r.peek = 0 184 } else if isIdent(c) { 185 r.readIdent() 186 } 187 r.readString() 188 } 189 190 // readComments is like ioutil.ReadAll, except that it only reads the leading 191 // block of comments in the file. 192 func readComments(f io.Reader) ([]byte, error) { 193 r := &importReader{b: bufio.NewReader(f)} 194 r.peekByte(true) 195 if r.err == nil && !r.eof { 196 // Didn't reach EOF, so must have found a non-space byte. Remove it. 197 r.buf = r.buf[:len(r.buf)-1] 198 } 199 return r.buf, r.err 200 } 201 202 // readImports is like ioutil.ReadAll, except that it expects a Go file as input 203 // and stops reading the input once the imports have completed. 204 func readImports(f io.Reader, reportSyntaxError bool) ([]byte, error) { 205 r := &importReader{b: bufio.NewReader(f)} 206 207 r.readKeyword("package") 208 r.readIdent() 209 for r.peekByte(true) == 'i' { 210 r.readKeyword("import") 211 if r.peekByte(true) == '(' { 212 r.nextByte(false) 213 for r.peekByte(true) != ')' && r.err == nil { 214 r.readImport() 215 } 216 r.nextByte(false) 217 } else { 218 r.readImport() 219 } 220 } 221 222 // If we stopped successfully before EOF, we read a byte that told us we were done. 223 // Return all but that last byte, which would cause a syntax error if we let it through. 224 if r.err == nil && !r.eof { 225 return r.buf[:len(r.buf)-1], nil 226 } 227 228 // If we stopped for a syntax error, consume the whole file so that 229 // we are sure we don't change the errors that go/parser returns. 230 if r.err == errSyntax && !reportSyntaxError { 231 r.err = nil 232 for r.err == nil && !r.eof { 233 r.readByte() 234 } 235 } 236 237 return r.buf, r.err 238 }