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 import modelbuilder
#FIXME useless
26 redef class ToolContext
27 var scope_phase
: Phase = new ScopePhase(self, null)
30 private class ScopePhase
32 redef fun process_npropdef
(npropdef
) do npropdef
.do_scope
(toolcontext
)
36 # A local variable (including parameters, automatic variables and self)
38 # The name of the variable (as used in the program)
42 redef fun to_s
do return self.name
45 # A local variable associated to a closure definition
50 # Mark where break and continue will branch.
51 # Marks are either associated with a label of with a for_loop structure
53 # The name of the label (unless the mark is an anonymous loop mark)
54 var name
: nullable String
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'
60 # Each 'continue' attached to the mark
61 var continues
: Array[AContinueExpr] = new Array[AContinueExpr]
63 # Each 'break' attached to the mark
64 var breaks
: Array[ABreakExpr] = new Array[ABreakExpr]
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
76 # The tool context used to display errors
77 var toolcontext
: ToolContext
79 var selfvariable
: Variable = new Variable("self")
81 init(toolcontext
: ToolContext)
83 self.toolcontext
= toolcontext
87 # All stacked scope. `scopes.first' is the current scope
88 private var scopes
: List[Scope] = new List[Scope]
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
94 var name
= variable
.name
95 var found
= search_variable
(name
)
97 self.error
(node
, "Error: A variable named `{name}' already exists")
100 scopes
.first
.variables
[name
] = variable
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
108 for scope
in scopes
do
109 var res
= scope
.get_variable
(name
)
117 redef fun visit
(n
: nullable ANode)
119 n
.accept_scope_visitor
(self)
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)
126 if node
== null then return
127 var scope
= new Scope
128 scope
.escapemark
= escapemark
129 scopes
.unshift
(scope
)
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
138 for scope
in scopes
do
139 var res
= scope
.escapemark
140 if res
!= null and res
.name
== name
then
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
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.")
162 var res
= new EscapeMark(name
, for_loop
)
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
173 if nlabel
!= null then
174 var name
= nlabel
.n_id
.text
175 var res
= search_label
(name
)
177 self.error
(nlabel
, "Syntax error: invalid label {name}.")
182 for scope
in scopes
do
183 var res
= scope
.escapemark
184 if res
!= null and res
.for_loop
then
188 self.error
(node
, "Syntax Error: 'break' statment outside block.")
194 private fun error
(node
: ANode, message
: String)
196 self.toolcontext
.error
(node
.hot_location
, message
)
201 var variables
: HashMap[String, Variable] = new HashMap[String, Variable]
203 var escapemark
: nullable EscapeMark = null
205 fun get_variable
(name
: String): nullable Variable
207 if self.variables
.has_key
(name
) then
208 return self.variables
[name
]
216 private fun accept_scope_visitor
(v
: ScopeVisitor)
223 # Entry point of the scope analysis
224 fun do_scope
(toolcontext
: ToolContext)
226 var v
= new ScopeVisitor(toolcontext
)
232 # The variable associated with the parameter
233 var variable
: nullable Variable
234 redef fun accept_scope_visitor
(v
)
238 var variable
= new Variable(nid
.text
)
239 v
.register_variable
(nid
, variable
)
240 self.variable
= variable
244 redef class AClosureDecl
245 # The variable associated with the closure declaration
246 var variable
: nullable ClosureVariable
247 redef fun accept_scope_visitor
(v
)
250 var variable
= new ClosureVariable(nid
.text
)
251 v
.register_variable
(nid
, variable
)
252 self.variable
= variable
256 redef class AVardeclExpr
257 # The variable associated with the variable declaration
258 var variable
: nullable Variable
259 redef fun accept_scope_visitor
(v
)
263 var variable
= new Variable(nid
.text
)
264 v
.register_variable
(nid
, variable
)
265 self.variable
= variable
269 redef class ASelfExpr
270 # The variable associated with the self reciever
271 var variable
: nullable Variable
272 redef fun accept_scope_visitor
(v
)
275 self.variable
= v
.selfvariable
279 redef class AContinueExpr
280 # The escape mark associated with the continue
281 var escapemark
: nullable EscapeMark
282 redef fun accept_scope_visitor
(v
)
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'.")
290 escapemark
.continues
.add
(self)
291 self.escapemark
= escapemark
295 redef class ABreakExpr
296 # The escape mark associated with the break
297 var escapemark
: nullable EscapeMark
298 redef fun accept_scope_visitor
(v
)
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
310 # The escape mark associated with the 'do' block
311 var escapemark
: nullable EscapeMark
312 redef fun accept_scope_visitor
(v
)
314 if n_label
!= null then
315 self.escapemark
= v
.make_escape_mark
(n_label
, false)
317 v
.enter_visit_block
(n_block
, self.escapemark
)
322 redef fun accept_scope_visitor
(v
)
324 v
.enter_visit
(n_expr
)
325 v
.enter_visit_block
(n_then
, null)
326 v
.enter_visit_block
(n_else
, null)
330 redef class AWhileExpr
331 # The escape mark associated with the 'while'
332 var escapemark
: nullable EscapeMark
333 redef fun accept_scope_visitor
(v
)
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
)
342 redef class ALoopExpr
343 # The escape mark associated with the 'loop'
344 var escapemark
: nullable EscapeMark
345 redef fun accept_scope_visitor
(v
)
347 var escapemark
= v
.make_escape_mark
(n_label
, true)
348 self.escapemark
= escapemark
349 v
.enter_visit_block
(n_block
, escapemark
)
354 # The automatic variables in order
355 var variables
: nullable Array[Variable]
357 # The escape mark associated with the 'for'
358 var escapemark
: nullable EscapeMark
360 redef fun accept_scope_visitor
(v
)
362 v
.enter_visit
(n_expr
)
364 # Protect automatic variables
365 v
.scopes
.unshift
(new Scope)
367 # Create the automatic variables
368 var variables
= new Array[Variable]
369 self.variables
= variables
371 var va
= new Variable(nid
.text
)
372 v
.register_variable
(nid
, va
)
376 var escapemark
= v
.make_escape_mark
(n_label
, true)
377 self.escapemark
= escapemark
378 v
.enter_visit_block
(n_block
, escapemark
)
384 redef class AVarFormExpr
385 # The associated variable
386 var variable
: nullable Variable
389 redef class ACallFormExpr
390 redef fun accept_scope_visitor
(v
)
392 if n_expr
isa AImplicitSelfExpr then
394 var variable
= v
.search_variable
(name
)
395 if variable
!= null then
397 if variable
isa ClosureVariable then
398 n
= new AClosureCallExpr.init_aclosurecallexpr
(n_id
, n_args
, n_closure_defs
)
399 n
.variable
= variable
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.")
405 n
= variable_create
(variable
)
406 n
.variable
= variable
409 n
.accept_scope_visitor
(v
)
417 # Create a variable acces corresponding to the call form
418 private fun variable_create
(variable
: Variable): AVarFormExpr is abstract
421 redef class ACallExpr
422 redef fun variable_create
(variable
)
424 return new AVarExpr.init_avarexpr
(n_id
)
428 redef class ACallAssignExpr
429 redef fun variable_create
(variable
)
431 return new AVarAssignExpr.init_avarassignexpr
(n_id
, n_assign
, n_value
)
435 redef class ACallReassignExpr
436 redef fun variable_create
(variable
)
438 return new AVarReassignExpr.init_avarreassignexpr
(n_id
, n_assign_op
, n_value
)
442 redef class AClosureCallExpr
443 # the associate closure variable
444 var variable
: nullable ClosureVariable
447 redef class ASendExpr
448 # The escape mark used with the closures if any
449 var escapemark
: nullable EscapeMark
451 redef fun accept_scope_visitor
(v
)
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
461 redef class AClosureDef
462 # The automatic variables in order
463 var variables
: nullable Array[Variable]
465 # The escape mark used with the closure
466 var escapemark
: nullable EscapeMark
468 redef fun accept_scope_visitor
(v
)
470 v
.scopes
.unshift
(new Scope)
472 var variables
= new Array[Variable]
473 self.variables
= variables
475 for nid
in self.n_ids
do
476 var va
= new Variable(nid
.text
)
477 v
.register_variable
(nid
, va
)
481 self.escapemark
= self.parent
.as(ASendExpr).escapemark
482 v
.enter_visit_block
(self.n_expr
, escapemark
)