github.com/zebozhuang/go@v0.0.0-20200207033046-f8a98f6f5c5d/src/go/types/eval.go (about)

     1  // Copyright 2013 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 types
     6  
     7  import (
     8  	"fmt"
     9  	"go/parser"
    10  	"go/token"
    11  )
    12  
    13  // Eval returns the type and, if constant, the value for the
    14  // expression expr, evaluated at position pos of package pkg,
    15  // which must have been derived from type-checking an AST with
    16  // complete position information relative to the provided file
    17  // set.
    18  //
    19  // If the expression contains function literals, their bodies
    20  // are ignored (i.e., the bodies are not type-checked).
    21  //
    22  // If pkg == nil, the Universe scope is used and the provided
    23  // position pos is ignored. If pkg != nil, and pos is invalid,
    24  // the package scope is used. Otherwise, pos must belong to the
    25  // package.
    26  //
    27  // An error is returned if pos is not within the package or
    28  // if the node cannot be evaluated.
    29  //
    30  // Note: Eval should not be used instead of running Check to compute
    31  // types and values, but in addition to Check. Eval will re-evaluate
    32  // its argument each time, and it also does not know about the context
    33  // in which an expression is used (e.g., an assignment). Thus, top-
    34  // level untyped constants will return an untyped type rather then the
    35  // respective context-specific type.
    36  //
    37  func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (TypeAndValue, error) {
    38  	// determine scope
    39  	var scope *Scope
    40  	if pkg == nil {
    41  		scope = Universe
    42  		pos = token.NoPos
    43  	} else if !pos.IsValid() {
    44  		scope = pkg.scope
    45  	} else {
    46  		// The package scope extent (position information) may be
    47  		// incorrect (files spread across a wide range of fset
    48  		// positions) - ignore it and just consider its children
    49  		// (file scopes).
    50  		for _, fscope := range pkg.scope.children {
    51  			if scope = fscope.Innermost(pos); scope != nil {
    52  				break
    53  			}
    54  		}
    55  		if scope == nil || debug {
    56  			s := scope
    57  			for s != nil && s != pkg.scope {
    58  				s = s.parent
    59  			}
    60  			// s == nil || s == pkg.scope
    61  			if s == nil {
    62  				return TypeAndValue{}, fmt.Errorf("no position %s found in package %s", fset.Position(pos), pkg.name)
    63  			}
    64  		}
    65  	}
    66  
    67  	// parse expressions
    68  	node, err := parser.ParseExprFrom(fset, "eval", expr, 0)
    69  	if err != nil {
    70  		return TypeAndValue{}, err
    71  	}
    72  
    73  	// initialize checker
    74  	check := NewChecker(nil, fset, pkg, nil)
    75  	check.scope = scope
    76  	check.pos = pos
    77  	defer check.handleBailout(&err)
    78  
    79  	// evaluate node
    80  	var x operand
    81  	check.rawExpr(&x, node, nil)
    82  	return TypeAndValue{x.mode, x.typ, x.val}, err
    83  }