...

Source file src/cuelang.org/go/cue/load/read.go

Documentation: cuelang.org/go/cue/load

     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  	"cuelang.org/go/cue/errors"
    23  	"cuelang.org/go/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  // readImports is like io.ReadAll, except that it expects a CUE file as
   212  // input and stops reading the input once the imports have completed.
   213  func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, errors.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  }
   248  

View as plain text