1
16
17
18 package bzlenv
19
20 import (
21 "github.com/bazelbuild/buildtools/build"
22 )
23
24
25 type ValueKind int
26
27
28 const (
29 Builtin ValueKind = iota
30 Imported
31 Global
32 Function
33 Parameter
34 Local
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
57 type NameInfo struct {
58 ID int
59 Name string
60 Kind ValueKind
61 Definition build.Expr
62 }
63
64 type block map[string]NameInfo
65
66
67 type Environment struct {
68 Blocks []block
69 Function *build.DefStmt
70 nextID int
71 Stack []build.Expr
72 }
73
74
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
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
141
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
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