scope: move selfvariable to scope from typing
[nit.git] / src / scope.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Identification and scping of local variables and labels.
18 module scope
19
20 import parser
21 import toolcontext
22
23 # A local variable (including parameters, automatic variables and self)
24 class Variable
25 # The name of the variable (as used in the program)
26 var name: String
27
28 # Alias of `name'
29 redef fun to_s do return self.name
30 end
31
32 # A local variable associated to a closure definition
33 class ClosureVariable
34 super Variable
35 end
36
37 # Mark where break and continue will branch.
38 # Marks are either associated with a label of with a for_loop structure
39 class EscapeMark
40 # The name of the label (unless the mark is an anonymous loop mark)
41 var name: nullable String
42
43 # Is the mark atached to a loop (loop, while, for, closure)
44 # Such a mark is a candidate to a labelless 'continue' or 'break'
45 var for_loop: Bool
46
47 # Each 'continue' attached to the mark
48 var continues: Array[AContinueExpr] = new Array[AContinueExpr]
49
50 # Each 'break' attached to the mark
51 var breaks: Array[ABreakExpr] = new Array[ABreakExpr]
52 end
53
54 # Visit a npropdef and:
55 # * Identify variables, closures and labels
56 # * Associate each break and continue to its escapemark
57 # * Transform ACallFormExpr that access a variable into AVarFormExpr
58 # * Transform ACallFormExpr that call a closure into AClosureCallExpr
59 # FIXME: Should the class be private?
60 private class ScopeVisitor
61 super Visitor
62
63 # The tool context used to display errors
64 var toolcontext: ToolContext
65
66 var selfvariable: Variable = new Variable("self")
67
68 init(toolcontext: ToolContext)
69 do
70 self.toolcontext = toolcontext
71 scopes.add(new Scope)
72 end
73
74 # All stacked scope. `scopes.first' is the current scope
75 private var scopes: List[Scope] = new List[Scope]
76
77 # Regiter a local variable.
78 # Display an error on toolcontext if a variable with the same name is masked.
79 fun register_variable(node: ANode, variable: Variable): Bool
80 do
81 var name = variable.name
82 var found = search_variable(name)
83 if found != null then
84 self.error(node, "Error: A variable named `{name}' already exists")
85 return false
86 end
87 scopes.first.variables[name] = variable
88 return true
89 end
90
91 # Look for a variable named `name'.
92 # Return null if no such a variable is found.
93 fun search_variable(name: String): nullable Variable
94 do
95 for scope in scopes do
96 var res = scope.get_variable(name)
97 if res != null then
98 return res
99 end
100 end
101 return null
102 end
103
104 redef fun visit(n: nullable ANode)
105 do
106 n.accept_scope_visitor(self)
107 end
108
109 # Enter in a statement block `node' as inside a new scope.
110 # The block can be optionally attached to an `escapemark'.
111 private fun enter_visit_block(node: nullable AExpr, escapemark: nullable EscapeMark)
112 do
113 if node == null then return
114 var scope = new Scope
115 scope.escapemark = escapemark
116 scopes.unshift(scope)
117 enter_visit(node)
118 scopes.shift
119 end
120
121 # Look for a label `name'.
122 # Return nulll if no such a label is found.
123 private fun search_label(name: String): nullable EscapeMark
124 do
125 for scope in scopes do
126 var res = scope.escapemark
127 if res != null and res.name == name then
128 return res
129 end
130 end
131 return null
132 end
133
134 # Create a new escape mark (possibly with a label)
135 # Display an error on toolcontext if a label with the same name is masked.
136 private fun make_escape_mark(nlabel: nullable ALabel, for_loop: Bool): EscapeMark
137 do
138 assert named_or_for_loop: nlabel != null or for_loop
139 var name: nullable String
140 if nlabel != null then
141 name = nlabel.n_id.text
142 var found = self.search_label(name)
143 if found != null then
144 self.error(nlabel, "Syntax error: label {name} already defined.")
145 end
146 else
147 name = null
148 end
149 var res = new EscapeMark(name, for_loop)
150 return res
151 end
152
153 # Look for an escape mark optionally associated with a label.
154 # If a label is given, the the escapemark of this label is returned.
155 # If there is no label, the nearest escapemark that is `for loop' ir returned.
156 # If there is no valid escapemark, then an error is displayed ans null is returned.
157 # Return nulll if no such a label is found.
158 private fun get_escapemark(node: ANode, nlabel: nullable ALabel): nullable EscapeMark
159 do
160 if nlabel != null then
161 var name = nlabel.n_id.text
162 var res = search_label(name)
163 if res == null then
164 self.error(nlabel, "Syntax error: invalid label {name}.")
165 return null
166 end
167 return res
168 else
169 for scope in scopes do
170 var res = scope.escapemark
171 if res != null and res.for_loop then
172 return res
173 end
174 end
175 self.error(node, "Syntax Error: 'break' statment outside block.")
176 return null
177 end
178 end
179
180 # Display an error
181 private fun error(node: ANode, message: String)
182 do
183 self.toolcontext.error(node.hot_location, message)
184 end
185 end
186
187 private class Scope
188 var variables: HashMap[String, Variable] = new HashMap[String, Variable]
189
190 var escapemark: nullable EscapeMark = null
191
192 fun get_variable(name: String): nullable Variable
193 do
194 if self.variables.has_key(name) then
195 return self.variables[name]
196 else
197 return null
198 end
199 end
200 end
201
202 redef class ANode
203 private fun accept_scope_visitor(v: ScopeVisitor)
204 do
205 visit_all(v)
206 end
207 end
208
209 redef class APropdef
210 # Entry point of the scope analysis
211 fun do_scope(toolcontext: ToolContext)
212 do
213 var v = new ScopeVisitor(toolcontext)
214 v.enter_visit(self)
215 end
216 end
217
218 redef class AParam
219 # The variable associated with the parameter
220 var variable: nullable Variable
221 redef fun accept_scope_visitor(v)
222 do
223 super
224 var nid = self.n_id
225 var variable = new Variable(nid.text)
226 v.register_variable(nid, variable)
227 self.variable = variable
228 end
229 end
230
231 redef class AClosureDecl
232 # The variable associated with the closure declaration
233 var variable: nullable ClosureVariable
234 redef fun accept_scope_visitor(v)
235 do
236 var nid = self.n_id
237 var variable = new ClosureVariable(nid.text)
238 v.register_variable(nid, variable)
239 self.variable = variable
240 end
241 end
242
243 redef class AVardeclExpr
244 # The variable associated with the variable declaration
245 var variable: nullable Variable
246 redef fun accept_scope_visitor(v)
247 do
248 super
249 var nid = self.n_id
250 var variable = new Variable(nid.text)
251 v.register_variable(nid, variable)
252 self.variable = variable
253 end
254 end
255
256 redef class ASelfExpr
257 # The variable associated with the self reciever
258 var variable: nullable Variable
259 redef fun accept_scope_visitor(v)
260 do
261 super
262 self.variable = v.selfvariable
263 end
264 end
265
266 redef class AContinueExpr
267 # The escape mark associated with the continue
268 var escapemark: nullable EscapeMark
269 redef fun accept_scope_visitor(v)
270 do
271 super
272 var escapemark = v.get_escapemark(self, self.n_label)
273 if escapemark == null then return # Skip error
274 if not escapemark.for_loop then
275 v.error(self, "Error: cannot 'continue', only 'break'.")
276 end
277 escapemark.continues.add(self)
278 self.escapemark = escapemark
279 end
280 end
281
282 redef class ABreakExpr
283 # The escape mark associated with the break
284 var escapemark: nullable EscapeMark
285 redef fun accept_scope_visitor(v)
286 do
287 super
288 var escapemark = v.get_escapemark(self, self.n_label)
289 if escapemark == null then return # Skip error
290 escapemark.breaks.add(self)
291 self.escapemark = escapemark
292 end
293 end
294
295
296 redef class ADoExpr
297 # The escape mark associated with the 'do' block
298 var escapemark: nullable EscapeMark
299 redef fun accept_scope_visitor(v)
300 do
301 if n_label != null then
302 self.escapemark = v.make_escape_mark(n_label, false)
303 end
304 v.enter_visit_block(n_block, self.escapemark)
305 end
306 end
307
308 redef class AIfExpr
309 redef fun accept_scope_visitor(v)
310 do
311 v.enter_visit(n_expr)
312 v.enter_visit_block(n_then, null)
313 v.enter_visit_block(n_else, null)
314 end
315 end
316
317 redef class AWhileExpr
318 # The escape mark associated with the 'while'
319 var escapemark: nullable EscapeMark
320 redef fun accept_scope_visitor(v)
321 do
322 var escapemark = v.make_escape_mark(n_label, true)
323 self.escapemark = escapemark
324 v.enter_visit(n_expr)
325 v.enter_visit_block(n_block, escapemark)
326 end
327 end
328
329 redef class ALoopExpr
330 # The escape mark associated with the 'loop'
331 var escapemark: nullable EscapeMark
332 redef fun accept_scope_visitor(v)
333 do
334 var escapemark = v.make_escape_mark(n_label, true)
335 self.escapemark = escapemark
336 v.enter_visit_block(n_block, escapemark)
337 end
338 end
339
340 redef class AForExpr
341 # The automatic variables in order
342 var variables: nullable Array[Variable]
343
344 # The escape mark associated with the 'for'
345 var escapemark: nullable EscapeMark
346
347 redef fun accept_scope_visitor(v)
348 do
349 v.enter_visit(n_expr)
350
351 # Protect automatic variables
352 v.scopes.unshift(new Scope)
353
354 # Create the automatic variables
355 var variables = new Array[Variable]
356 self.variables = variables
357 for nid in n_ids do
358 var va = new Variable(nid.text)
359 v.register_variable(nid, va)
360 variables.add(va)
361 end
362
363 var escapemark = v.make_escape_mark(n_label, true)
364 self.escapemark = escapemark
365 v.enter_visit_block(n_block, escapemark)
366
367 v.scopes.shift
368 end
369 end
370
371 redef class AVarFormExpr
372 # The associated variable
373 var variable: nullable Variable
374 end
375
376 redef class ACallFormExpr
377 redef fun accept_scope_visitor(v)
378 do
379 if n_expr isa AImplicitSelfExpr then
380 var name = n_id.text
381 var variable = v.search_variable(name)
382 if variable != null then
383 var n: AExpr
384 if variable isa ClosureVariable then
385 n = new AClosureCallExpr.init_aclosurecallexpr(n_id, n_args, n_closure_defs)
386 n.variable = variable
387 else
388 if not n_args.n_exprs.is_empty or n_args isa AParExprs then
389 v.error(self, "Error: {name} is variable, not a function.")
390 return
391 end
392 n = variable_create(variable)
393 n.variable = variable
394 end
395 replace_with(n)
396 n.accept_scope_visitor(v)
397 return
398 end
399 end
400
401 super
402 end
403
404 # Create a variable acces corresponding to the call form
405 private fun variable_create(variable: Variable): AVarFormExpr is abstract
406 end
407
408 redef class ACallExpr
409 redef fun variable_create(variable)
410 do
411 return new AVarExpr.init_avarexpr(n_id)
412 end
413 end
414
415 redef class ACallAssignExpr
416 redef fun variable_create(variable)
417 do
418 return new AVarAssignExpr.init_avarassignexpr(n_id, n_assign, n_value)
419 end
420 end
421
422 redef class ACallReassignExpr
423 redef fun variable_create(variable)
424 do
425 return new AVarReassignExpr.init_avarreassignexpr(n_id, n_assign_op, n_value)
426 end
427 end
428
429 redef class AClosureCallExpr
430 # the associate closure variable
431 var variable: nullable ClosureVariable
432 end
433
434 redef class AClosureDef
435 # The automatic variables in order
436 var variables: nullable Array[Variable]
437
438 # The escape mark used with the closure
439 var escapemark: nullable EscapeMark
440
441 redef fun accept_scope_visitor(v)
442 do
443 v.scopes.unshift(new Scope)
444
445 var variables = new Array[Variable]
446 self.variables = variables
447
448 for nid in self.n_ids do
449 var va = new Variable(nid.text)
450 v.register_variable(nid, va)
451 variables.add(va)
452 end
453
454 var escapemark = v.make_escape_mark(n_label, true)
455 self.escapemark = escapemark
456 v.enter_visit_block(self.n_expr, escapemark)
457
458 v.scopes.shift
459 end
460 end