github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/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  	"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  }