...

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

View as plain text