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