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