Source file src/cmd/go/internal/imports/read.go

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

View as plain text