...

Source file src/go/parser/interface.go

Documentation: go/parser

		 1  // Copyright 2009 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  // This file contains the exported entry points for invoking the parser.
		 6  
		 7  package parser
		 8  
		 9  import (
		10  	"bytes"
		11  	"errors"
		12  	"go/ast"
		13  	"go/token"
		14  	"io"
		15  	"io/fs"
		16  	"os"
		17  	"path/filepath"
		18  	"strings"
		19  )
		20  
		21  // If src != nil, readSource converts src to a []byte if possible;
		22  // otherwise it returns an error. If src == nil, readSource returns
		23  // the result of reading the file specified by filename.
		24  //
		25  func readSource(filename string, src interface{}) ([]byte, error) {
		26  	if src != nil {
		27  		switch s := src.(type) {
		28  		case string:
		29  			return []byte(s), nil
		30  		case []byte:
		31  			return s, nil
		32  		case *bytes.Buffer:
		33  			// is io.Reader, but src is already available in []byte form
		34  			if s != nil {
		35  				return s.Bytes(), nil
		36  			}
		37  		case io.Reader:
		38  			return io.ReadAll(s)
		39  		}
		40  		return nil, errors.New("invalid source")
		41  	}
		42  	return os.ReadFile(filename)
		43  }
		44  
		45  // A Mode value is a set of flags (or 0).
		46  // They control the amount of source code parsed and other optional
		47  // parser functionality.
		48  //
		49  type Mode uint
		50  
		51  const (
		52  	PackageClauseOnly		Mode						 = 1 << iota // stop parsing after package clause
		53  	ImportsOnly																			 // stop parsing after import declarations
		54  	ParseComments																		 // parse comments and add them to AST
		55  	Trace																						 // print a trace of parsed productions
		56  	DeclarationErrors																 // report declaration errors
		57  	SpuriousErrors																		// same as AllErrors, for backward-compatibility
		58  	SkipObjectResolution															// don't resolve identifiers to objects - see ParseFile
		59  	AllErrors						= SpuriousErrors						 // report all errors (not just the first 10 on different lines)
		60  )
		61  
		62  // ParseFile parses the source code of a single Go source file and returns
		63  // the corresponding ast.File node. The source code may be provided via
		64  // the filename of the source file, or via the src parameter.
		65  //
		66  // If src != nil, ParseFile parses the source from src and the filename is
		67  // only used when recording position information. The type of the argument
		68  // for the src parameter must be string, []byte, or io.Reader.
		69  // If src == nil, ParseFile parses the file specified by filename.
		70  //
		71  // The mode parameter controls the amount of source text parsed and other
		72  // optional parser functionality. If the SkipObjectResolution mode bit is set,
		73  // the object resolution phase of parsing will be skipped, causing File.Scope,
		74  // File.Unresolved, and all Ident.Obj fields to be nil.
		75  //
		76  // Position information is recorded in the file set fset, which must not be
		77  // nil.
		78  //
		79  // If the source couldn't be read, the returned AST is nil and the error
		80  // indicates the specific failure. If the source was read but syntax
		81  // errors were found, the result is a partial AST (with ast.Bad* nodes
		82  // representing the fragments of erroneous source code). Multiple errors
		83  // are returned via a scanner.ErrorList which is sorted by source position.
		84  //
		85  func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) {
		86  	if fset == nil {
		87  		panic("parser.ParseFile: no token.FileSet provided (fset == nil)")
		88  	}
		89  
		90  	// get source
		91  	text, err := readSource(filename, src)
		92  	if err != nil {
		93  		return nil, err
		94  	}
		95  
		96  	var p parser
		97  	defer func() {
		98  		if e := recover(); e != nil {
		99  			// resume same panic if it's not a bailout
	 100  			bail, ok := e.(bailout)
	 101  			if !ok {
	 102  				panic(e)
	 103  			} else if bail.msg != "" {
	 104  				p.errors.Add(p.file.Position(bail.pos), bail.msg)
	 105  			}
	 106  		}
	 107  
	 108  		// set result values
	 109  		if f == nil {
	 110  			// source is not a valid Go source file - satisfy
	 111  			// ParseFile API and return a valid (but) empty
	 112  			// *ast.File
	 113  			f = &ast.File{
	 114  				Name:	new(ast.Ident),
	 115  				Scope: ast.NewScope(nil),
	 116  			}
	 117  		}
	 118  
	 119  		p.errors.Sort()
	 120  		err = p.errors.Err()
	 121  	}()
	 122  
	 123  	// parse source
	 124  	p.init(fset, filename, text, mode)
	 125  	f = p.parseFile()
	 126  
	 127  	return
	 128  }
	 129  
	 130  // ParseDir calls ParseFile for all files with names ending in ".go" in the
	 131  // directory specified by path and returns a map of package name -> package
	 132  // AST with all the packages found.
	 133  //
	 134  // If filter != nil, only the files with fs.FileInfo entries passing through
	 135  // the filter (and ending in ".go") are considered. The mode bits are passed
	 136  // to ParseFile unchanged. Position information is recorded in fset, which
	 137  // must not be nil.
	 138  //
	 139  // If the directory couldn't be read, a nil map and the respective error are
	 140  // returned. If a parse error occurred, a non-nil but incomplete map and the
	 141  // first error encountered are returned.
	 142  //
	 143  func ParseDir(fset *token.FileSet, path string, filter func(fs.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error) {
	 144  	list, err := os.ReadDir(path)
	 145  	if err != nil {
	 146  		return nil, err
	 147  	}
	 148  
	 149  	pkgs = make(map[string]*ast.Package)
	 150  	for _, d := range list {
	 151  		if d.IsDir() || !strings.HasSuffix(d.Name(), ".go") {
	 152  			continue
	 153  		}
	 154  		if filter != nil {
	 155  			info, err := d.Info()
	 156  			if err != nil {
	 157  				return nil, err
	 158  			}
	 159  			if !filter(info) {
	 160  				continue
	 161  			}
	 162  		}
	 163  		filename := filepath.Join(path, d.Name())
	 164  		if src, err := ParseFile(fset, filename, nil, mode); err == nil {
	 165  			name := src.Name.Name
	 166  			pkg, found := pkgs[name]
	 167  			if !found {
	 168  				pkg = &ast.Package{
	 169  					Name:	name,
	 170  					Files: make(map[string]*ast.File),
	 171  				}
	 172  				pkgs[name] = pkg
	 173  			}
	 174  			pkg.Files[filename] = src
	 175  		} else if first == nil {
	 176  			first = err
	 177  		}
	 178  	}
	 179  
	 180  	return
	 181  }
	 182  
	 183  // ParseExprFrom is a convenience function for parsing an expression.
	 184  // The arguments have the same meaning as for ParseFile, but the source must
	 185  // be a valid Go (type or value) expression. Specifically, fset must not
	 186  // be nil.
	 187  //
	 188  // If the source couldn't be read, the returned AST is nil and the error
	 189  // indicates the specific failure. If the source was read but syntax
	 190  // errors were found, the result is a partial AST (with ast.Bad* nodes
	 191  // representing the fragments of erroneous source code). Multiple errors
	 192  // are returned via a scanner.ErrorList which is sorted by source position.
	 193  //
	 194  func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode Mode) (expr ast.Expr, err error) {
	 195  	if fset == nil {
	 196  		panic("parser.ParseExprFrom: no token.FileSet provided (fset == nil)")
	 197  	}
	 198  
	 199  	// get source
	 200  	text, err := readSource(filename, src)
	 201  	if err != nil {
	 202  		return nil, err
	 203  	}
	 204  
	 205  	var p parser
	 206  	defer func() {
	 207  		if e := recover(); e != nil {
	 208  			// resume same panic if it's not a bailout
	 209  			bail, ok := e.(bailout)
	 210  			if !ok {
	 211  				panic(e)
	 212  			} else if bail.msg != "" {
	 213  				p.errors.Add(p.file.Position(bail.pos), bail.msg)
	 214  			}
	 215  		}
	 216  		p.errors.Sort()
	 217  		err = p.errors.Err()
	 218  	}()
	 219  
	 220  	// parse expr
	 221  	p.init(fset, filename, text, mode)
	 222  	expr = p.parseRhsOrType()
	 223  
	 224  	// If a semicolon was inserted, consume it;
	 225  	// report an error if there's more tokens.
	 226  	if p.tok == token.SEMICOLON && p.lit == "\n" {
	 227  		p.next()
	 228  	}
	 229  	p.expect(token.EOF)
	 230  
	 231  	return
	 232  }
	 233  
	 234  // ParseExpr is a convenience function for obtaining the AST of an expression x.
	 235  // The position information recorded in the AST is undefined. The filename used
	 236  // in error messages is the empty string.
	 237  //
	 238  // If syntax errors were found, the result is a partial AST (with ast.Bad* nodes
	 239  // representing the fragments of erroneous source code). Multiple errors are
	 240  // returned via a scanner.ErrorList which is sorted by source position.
	 241  //
	 242  func ParseExpr(x string) (ast.Expr, error) {
	 243  	return ParseExprFrom(token.NewFileSet(), "", []byte(x), 0)
	 244  }
	 245  

View as plain text