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