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