...

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