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