1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2012 Jean Privat <jean@pryen.org>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Identification and scping of local variables and labels.
24 redef class ToolContext
25 var scope_phase
: Phase = new ScopePhase(self, null)
28 private class ScopePhase
30 redef fun process_npropdef
(npropdef
) do npropdef
.do_scope
(toolcontext
)
34 # A local variable (including parameters, automatic variables and self)
36 # The name of the variable (as used in the program)
40 redef fun to_s
do return self.name
43 # A local variable associated to a closure definition
48 # Mark where break and continue will branch.
49 # Marks are either associated with a label of with a for_loop structure
51 # The name of the label (unless the mark is an anonymous loop mark)
52 var name
: nullable String
54 # Is the mark atached to a loop (loop, while, for, closure)
55 # Such a mark is a candidate to a labelless 'continue' or 'break'
58 # Each 'continue' attached to the mark
59 var continues
: Array[AContinueExpr] = new Array[AContinueExpr]
61 # Each 'break' attached to the mark
62 var breaks
: Array[ABreakExpr] = new Array[ABreakExpr]
65 # Visit a npropdef and:
66 # * Identify variables, closures and labels
67 # * Associate each break and continue to its escapemark
68 # * Transform `ACallFormExpr` that access a variable into `AVarFormExpr`
69 # * Transform `ACallFormExpr` that call a closure into `AClosureCallExpr`
70 # FIXME: Should the class be private?
71 private class ScopeVisitor
74 # The tool context used to display errors
75 var toolcontext
: ToolContext
77 var selfvariable
: Variable = new Variable("self")
79 init(toolcontext
: ToolContext)
81 self.toolcontext
= toolcontext
85 # All stacked scope. `scopes.first` is the current scope
86 private var scopes
: List[Scope] = new List[Scope]
88 # Regiter a local variable.
89 # Display an error on toolcontext if a variable with the same name is masked.
90 fun register_variable
(node
: ANode, variable
: Variable): Bool
92 var name
= variable
.name
93 var found
= search_variable
(name
)
95 self.error
(node
, "Error: A variable named `{name}' already exists")
98 scopes
.first
.variables
[name
] = variable
102 # Look for a variable named `name`.
103 # Return null if no such a variable is found.
104 fun search_variable
(name
: String): nullable Variable
106 for scope
in scopes
do
107 var res
= scope
.get_variable
(name
)
117 n
.accept_scope_visitor
(self)
120 # Enter in a statement block `node` as inside a new scope.
121 # The block can be optionally attached to an `escapemark`.
122 private fun enter_visit_block
(node
: nullable AExpr, escapemark
: nullable EscapeMark)
124 if node
== null then return
125 var scope
= new Scope
126 scope
.escapemark
= escapemark
127 scopes
.unshift
(scope
)
132 # Look for a label `name`.
133 # Return nulll if no such a label is found.
134 private fun search_label
(name
: String): nullable EscapeMark
136 for scope
in scopes
do
137 var res
= scope
.escapemark
138 if res
!= null and res
.name
== name
then
145 # Create a new escape mark (possibly with a label)
146 # Display an error on toolcontext if a label with the same name is masked.
147 private fun make_escape_mark
(nlabel
: nullable ALabel, for_loop
: Bool): EscapeMark
149 var name
: nullable String
150 if nlabel
!= null then
151 name
= nlabel
.n_id
.text
152 var found
= self.search_label
(name
)
153 if found
!= null then
154 self.error
(nlabel
, "Syntax error: label {name} already defined.")
159 var res
= new EscapeMark(name
, for_loop
)
163 # Look for an escape mark optionally associated with a label.
164 # If a label is given, the the escapemark of this label is returned.
165 # If there is no label, the nearest escapemark that is `for loop` is returned.
166 # If there is no valid escapemark, then an error is displayed ans null is returned.
167 # Return nulll if no such a label is found.
168 private fun get_escapemark
(node
: ANode, nlabel
: nullable ALabel): nullable EscapeMark
170 if nlabel
!= null then
171 var name
= nlabel
.n_id
.text
172 var res
= search_label
(name
)
174 self.error
(nlabel
, "Syntax error: invalid label {name}.")
179 for scope
in scopes
do
180 var res
= scope
.escapemark
185 self.error
(node
, "Syntax Error: 'break' statment outside block.")
191 private fun error
(node
: ANode, message
: String)
193 self.toolcontext
.error
(node
.hot_location
, message
)
198 var variables
: HashMap[String, Variable] = new HashMap[String, Variable]
200 var escapemark
: nullable EscapeMark = null
202 fun get_variable
(name
: String): nullable Variable
204 if self.variables
.has_key
(name
) then
205 return self.variables
[name
]
213 private fun accept_scope_visitor
(v
: ScopeVisitor)
220 # Entry point of the scope analysis
221 fun do_scope
(toolcontext
: ToolContext)
223 var v
= new ScopeVisitor(toolcontext
)
229 # The variable associated with the parameter
230 var variable
: nullable Variable
231 redef fun accept_scope_visitor
(v
)
235 var variable
= new Variable(nid
.text
)
236 v
.register_variable
(nid
, variable
)
237 self.variable
= variable
241 redef class AClosureDecl
242 # The variable associated with the closure declaration
243 var variable
: nullable ClosureVariable
244 redef fun accept_scope_visitor
(v
)
247 var variable
= new ClosureVariable(nid
.text
)
248 v
.register_variable
(nid
, variable
)
249 self.variable
= variable
253 redef class AVardeclExpr
254 # The variable associated with the variable declaration
255 var variable
: nullable Variable
256 redef fun accept_scope_visitor
(v
)
260 var variable
= new Variable(nid
.text
)
261 v
.register_variable
(nid
, variable
)
262 self.variable
= variable
266 redef class ASelfExpr
267 # The variable associated with the self reciever
268 var variable
: nullable Variable
269 redef fun accept_scope_visitor
(v
)
272 self.variable
= v
.selfvariable
276 redef class AContinueExpr
277 # The escape mark associated with the continue
278 var escapemark
: nullable EscapeMark
279 redef fun accept_scope_visitor
(v
)
282 var escapemark
= v
.get_escapemark
(self, self.n_label
)
283 if escapemark
== null then return # Skip error
284 if not escapemark
.for_loop
then
285 v
.error
(self, "Error: cannot 'continue', only 'break'.")
287 escapemark
.continues
.add
(self)
288 self.escapemark
= escapemark
292 redef class ABreakExpr
293 # The escape mark associated with the break
294 var escapemark
: nullable EscapeMark
295 redef fun accept_scope_visitor
(v
)
298 var escapemark
= v
.get_escapemark
(self, self.n_label
)
299 if escapemark
== null then return # Skip error
300 escapemark
.breaks
.add
(self)
301 self.escapemark
= escapemark
307 # The escape mark associated with the 'do' block
308 var escapemark
: nullable EscapeMark
309 redef fun accept_scope_visitor
(v
)
311 self.escapemark
= v
.make_escape_mark
(n_label
, false)
312 v
.enter_visit_block
(n_block
, self.escapemark
)
317 redef fun accept_scope_visitor
(v
)
319 v
.enter_visit
(n_expr
)
320 v
.enter_visit_block
(n_then
, null)
321 v
.enter_visit_block
(n_else
, null)
325 redef class AWhileExpr
326 # The escape mark associated with the 'while'
327 var escapemark
: nullable EscapeMark
328 redef fun accept_scope_visitor
(v
)
330 var escapemark
= v
.make_escape_mark
(n_label
, true)
331 self.escapemark
= escapemark
332 v
.enter_visit
(n_expr
)
333 v
.enter_visit_block
(n_block
, escapemark
)
337 redef class ALoopExpr
338 # The escape mark associated with the 'loop'
339 var escapemark
: nullable EscapeMark
340 redef fun accept_scope_visitor
(v
)
342 var escapemark
= v
.make_escape_mark
(n_label
, true)
343 self.escapemark
= escapemark
344 v
.enter_visit_block
(n_block
, escapemark
)
349 # The automatic variables in order
350 var variables
: nullable Array[Variable]
352 # The escape mark associated with the 'for'
353 var escapemark
: nullable EscapeMark
355 redef fun accept_scope_visitor
(v
)
357 v
.enter_visit
(n_expr
)
359 # Protect automatic variables
360 v
.scopes
.unshift
(new Scope)
362 # Create the automatic variables
363 var variables
= new Array[Variable]
364 self.variables
= variables
366 var va
= new Variable(nid
.text
)
367 v
.register_variable
(nid
, va
)
371 var escapemark
= v
.make_escape_mark
(n_label
, true)
372 self.escapemark
= escapemark
373 v
.enter_visit_block
(n_block
, escapemark
)
379 redef class AVarFormExpr
380 # The associated variable
381 var variable
: nullable Variable
384 redef class ACallFormExpr
385 redef fun accept_scope_visitor
(v
)
387 if n_expr
isa AImplicitSelfExpr then
389 var variable
= v
.search_variable
(name
)
390 if variable
!= null then
392 if variable
isa ClosureVariable then
393 n
= new AClosureCallExpr.init_aclosurecallexpr
(n_id
, n_args
, n_closure_defs
)
394 n
.variable
= variable
396 if not n_args
.n_exprs
.is_empty
or n_args
isa AParExprs then
397 v
.error
(self, "Error: {name} is variable, not a function.")
400 n
= variable_create
(variable
)
401 n
.variable
= variable
404 n
.accept_scope_visitor
(v
)
412 # Create a variable acces corresponding to the call form
413 private fun variable_create
(variable
: Variable): AVarFormExpr is abstract
416 redef class ACallExpr
417 redef fun variable_create
(variable
)
419 return new AVarExpr.init_avarexpr
(n_id
)
423 redef class ACallAssignExpr
424 redef fun variable_create
(variable
)
426 return new AVarAssignExpr.init_avarassignexpr
(n_id
, n_assign
, n_value
)
430 redef class ACallReassignExpr
431 redef fun variable_create
(variable
)
433 return new AVarReassignExpr.init_avarreassignexpr
(n_id
, n_assign_op
, n_value
)
437 redef class AClosureCallExpr
438 # the associate closure variable
439 var variable
: nullable ClosureVariable
442 redef class ASendExpr
443 # The escape mark used with the closures if any
444 var escapemark
: nullable EscapeMark
446 redef fun accept_scope_visitor
(v
)
448 if self.n_closure_defs
.length
> 0 then
449 var escapemark
= v
.make_escape_mark
(self.n_closure_defs
.last
.n_label
, true)
450 self.escapemark
= escapemark
456 redef class AClosureDef
457 # The automatic variables in order
458 var variables
: nullable Array[Variable]
460 # The escape mark used with the closure
461 var escapemark
: nullable EscapeMark
463 redef fun accept_scope_visitor
(v
)
465 v
.scopes
.unshift
(new Scope)
467 var variables
= new Array[Variable]
468 self.variables
= variables
470 for nid
in self.n_ids
do
471 var va
= new Variable(nid
.text
)
472 v
.register_variable
(nid
, va
)
476 self.escapemark
= self.parent
.as(ASendExpr).escapemark
477 v
.enter_visit_block
(self.n_expr
, escapemark
)