github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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(save *[]string) {
   150  	switch r.nextByte(true) {
   151  	case '`':
   152  		start := len(r.buf) - 1
   153  		for r.err == nil {
   154  			if r.nextByte(false) == '`' {
   155  				if save != nil {
   156  					*save = append(*save, string(r.buf[start:]))
   157  				}
   158  				break
   159  			}
   160  			if r.eof {
   161  				r.syntaxError()
   162  			}
   163  		}
   164  	case '"':
   165  		start := len(r.buf) - 1
   166  		for r.err == nil {
   167  			c := r.nextByte(false)
   168  			if c == '"' {
   169  				if save != nil {
   170  					*save = append(*save, string(r.buf[start:]))
   171  				}
   172  				break
   173  			}
   174  			if r.eof || c == '\n' {
   175  				r.syntaxError()
   176  			}
   177  			if c == '\\' {
   178  				r.nextByte(false)
   179  			}
   180  		}
   181  	default:
   182  		r.syntaxError()
   183  	}
   184  }
   185  
   186  // readImport reads an import clause - optional identifier followed by quoted string -
   187  // from the input.
   188  func (r *importReader) readImport(imports *[]string) {
   189  	c := r.peekByte(true)
   190  	if c == '.' {
   191  		r.peek = 0
   192  	} else if isIdent(c) {
   193  		r.readIdent()
   194  	}
   195  	r.readString(imports)
   196  }
   197  
   198  // readComments is like ioutil.ReadAll, except that it only reads the leading
   199  // block of comments in the file.
   200  func readComments(f io.Reader) ([]byte, error) {
   201  	r := &importReader{b: bufio.NewReader(f)}
   202  	r.peekByte(true)
   203  	if r.err == nil && !r.eof {
   204  		// Didn't reach EOF, so must have found a non-space byte. Remove it.
   205  		r.buf = r.buf[:len(r.buf)-1]
   206  	}
   207  	return r.buf, r.err
   208  }
   209  
   210  // readImports is like ioutil.ReadAll, except that it expects a Go file as input
   211  // and stops reading the input once the imports have completed.
   212  func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
   213  	r := &importReader{b: bufio.NewReader(f)}
   214  
   215  	r.readKeyword("package")
   216  	r.readIdent()
   217  	for r.peekByte(true) == 'i' {
   218  		r.readKeyword("import")
   219  		if r.peekByte(true) == '(' {
   220  			r.nextByte(false)
   221  			for r.peekByte(true) != ')' && r.err == nil {
   222  				r.readImport(imports)
   223  			}
   224  			r.nextByte(false)
   225  		} else {
   226  			r.readImport(imports)
   227  		}
   228  	}
   229  
   230  	// If we stopped successfully before EOF, we read a byte that told us we were done.
   231  	// Return all but that last byte, which would cause a syntax error if we let it through.
   232  	if r.err == nil && !r.eof {
   233  		return r.buf[:len(r.buf)-1], nil
   234  	}
   235  
   236  	// If we stopped for a syntax error, consume the whole file so that
   237  	// we are sure we don't change the errors that go/parser returns.
   238  	if r.err == errSyntax && !reportSyntaxError {
   239  		r.err = nil
   240  		for r.err == nil && !r.eof {
   241  			r.readByte()
   242  		}
   243  	}
   244  
   245  	return r.buf, r.err
   246  }