...

Source file src/github.com/bazelbuild/buildtools/bzlenv/bzlenv.go

Documentation: github.com/bazelbuild/buildtools/bzlenv

     1  /*
     2  Copyright 2020 Google LLC
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      https://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package bzlenv provides function to create and update a static environment.
    18  package bzlenv
    19  
    20  import (
    21  	"github.com/bazelbuild/buildtools/build"
    22  )
    23  
    24  // ValueKind describes how a binding was declared.
    25  type ValueKind int
    26  
    27  // List of ValueKind values.
    28  const (
    29  	Builtin   ValueKind = iota // language builtin
    30  	Imported                   // declared with load()
    31  	Global                     // declared with assignment on top-level
    32  	Function                   // declared with a def
    33  	Parameter                  // function parameter
    34  	Local                      // local variable, defined with assignment or as a loop variable
    35  )
    36  
    37  func (k ValueKind) String() string {
    38  	switch k {
    39  	case Builtin:
    40  		return "builtin"
    41  	case Imported:
    42  		return "imported"
    43  	case Global:
    44  		return "global"
    45  	case Function:
    46  		return "function"
    47  	case Parameter:
    48  		return "parameter"
    49  	case Local:
    50  		return "local"
    51  	default:
    52  		panic(k)
    53  	}
    54  }
    55  
    56  // NameInfo represents information about a symbol name.
    57  type NameInfo struct {
    58  	ID         int    // unique identifier
    59  	Name       string // name of the variable (not unique)
    60  	Kind       ValueKind
    61  	Definition build.Expr // node that defines the value
    62  }
    63  
    64  type block map[string]NameInfo
    65  
    66  // Environment represents a static environment (e.g. information about all available symbols).
    67  type Environment struct {
    68  	Blocks   []block
    69  	Function *build.DefStmt // enclosing function (or nil on top-level)
    70  	nextID   int            // used to create unique identifiers
    71  	Stack    []build.Expr   // parents of the current node
    72  }
    73  
    74  // NewEnvironment creates a new empty Environment.
    75  func NewEnvironment() *Environment {
    76  	sc := block{}
    77  	return &Environment{[]block{sc}, nil, 0, []build.Expr{}}
    78  }
    79  
    80  func (e *Environment) enterBlock() {
    81  	e.Blocks = append(e.Blocks, block{})
    82  }
    83  
    84  func (e *Environment) exitBlock() {
    85  	if len(e.Blocks) < 1 {
    86  		panic("no block to close")
    87  	}
    88  	e.Blocks = e.Blocks[:len(e.Blocks)-1]
    89  }
    90  
    91  func (e *Environment) currentBlock() block {
    92  	return e.Blocks[len(e.Blocks)-1]
    93  }
    94  
    95  func (sc *block) declare(name string, kind ValueKind, definition build.Expr, id int) {
    96  	(*sc)[name] = NameInfo{
    97  		ID:         id,
    98  		Name:       name,
    99  		Definition: definition,
   100  		Kind:       kind}
   101  }
   102  
   103  // Get resolves the name and resolves information about the binding (or nil if it's not defined).
   104  func (e *Environment) Get(name string) *NameInfo {
   105  	for i := len(e.Blocks) - 1; i >= 0; i-- {
   106  		if ret, ok := e.Blocks[i][name]; ok {
   107  			return &ret
   108  		}
   109  	}
   110  	return nil
   111  }
   112  
   113  func (e *Environment) declare(name string, kind ValueKind, node build.Expr) {
   114  	sc := e.currentBlock()
   115  	sc.declare(name, kind, node, e.nextID)
   116  	e.nextID++
   117  }
   118  
   119  func declareGlobals(stmts []build.Expr, env *Environment) {
   120  	for _, node := range stmts {
   121  		switch node := node.(type) {
   122  		case *build.LoadStmt:
   123  			for _, ident := range node.To {
   124  				env.declare(ident.Name, Imported, ident)
   125  			}
   126  		case *build.AssignExpr:
   127  			kind := Local
   128  			if env.Function == nil {
   129  				kind = Global
   130  			}
   131  			for _, id := range CollectLValues(node.LHS) {
   132  				env.declare(id.Name, kind, node)
   133  			}
   134  		case *build.DefStmt:
   135  			env.declare(node.Name, Function, node)
   136  		}
   137  	}
   138  }
   139  
   140  // CollectLValues returns the list of identifiers that are assigned (assuming that node is a valid
   141  // LValue). For example, it returns `a`, `b` and `c` for the input `a, (b, c)`.
   142  func CollectLValues(node build.Expr) []*build.Ident {
   143  	var result []*build.Ident
   144  	switch node := node.(type) {
   145  	case *build.Ident:
   146  		result = append(result, node)
   147  	case *build.TupleExpr:
   148  		for _, item := range node.List {
   149  			result = append(result, CollectLValues(item)...)
   150  		}
   151  	case *build.ListExpr:
   152  		for _, item := range node.List {
   153  			result = append(result, CollectLValues(item)...)
   154  		}
   155  	}
   156  	return result
   157  }
   158  
   159  func declareParams(fct *build.DefStmt, env *Environment) {
   160  	for _, node := range fct.Params {
   161  		name, _ := build.GetParamName(node)
   162  		env.declare(name, Parameter, node)
   163  	}
   164  }
   165  
   166  func declareLocalVariables(stmts []build.Expr, env *Environment) {
   167  	for _, stmt := range stmts {
   168  		switch node := stmt.(type) {
   169  		case *build.AssignExpr:
   170  			kind := Local
   171  			if env.Function == nil {
   172  				kind = Global
   173  			}
   174  			for _, id := range CollectLValues(node.LHS) {
   175  				env.declare(id.Name, kind, node)
   176  			}
   177  		case *build.IfStmt:
   178  			declareLocalVariables(node.True, env)
   179  			declareLocalVariables(node.False, env)
   180  		case *build.ForStmt:
   181  			for _, id := range CollectLValues(node.Vars) {
   182  				env.declare(id.Name, Local, node)
   183  			}
   184  			declareLocalVariables(node.Body, env)
   185  		}
   186  	}
   187  }
   188  
   189  // WalkOnceWithEnvironment calls fct on every child of node, while maintaining the Environment of all available symbols.
   190  func WalkOnceWithEnvironment(node build.Expr, env *Environment, fct func(e *build.Expr, env *Environment)) {
   191  	env.Stack = append(env.Stack, node)
   192  	switch node := node.(type) {
   193  	case *build.File:
   194  		declareGlobals(node.Stmt, env)
   195  		build.WalkOnce(node, func(e *build.Expr) { fct(e, env) })
   196  	case *build.DefStmt:
   197  		env.enterBlock()
   198  		env.Function = node
   199  		declareParams(node, env)
   200  		declareLocalVariables(node.Body, env)
   201  		build.WalkOnce(node, func(e *build.Expr) { fct(e, env) })
   202  		env.Function = nil
   203  		env.exitBlock()
   204  	case *build.Comprehension:
   205  		env.enterBlock()
   206  		for _, clause := range node.Clauses {
   207  			switch clause := clause.(type) {
   208  			case *build.ForClause:
   209  				for _, id := range CollectLValues(clause.Vars) {
   210  					env.declare(id.Name, Local, node)
   211  				}
   212  			}
   213  		}
   214  		build.WalkOnce(node, func(e *build.Expr) { fct(e, env) })
   215  		env.exitBlock()
   216  	default:
   217  		build.WalkOnce(node, func(e *build.Expr) { fct(e, env) })
   218  	}
   219  	env.Stack = env.Stack[:len(env.Stack)-1]
   220  }
   221  

View as plain text