...

Source file src/cmd/dist/imports.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	// 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	}
   246	

View as plain text