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.
23 # A local variable (including parameters, automatic variables and self)
25 # The name of the variable (as used in the program)
29 redef fun to_s
do return self.name
32 # A local variable associated to a closure definition
37 # Mark where break and continue will branch.
38 # Marks are either associated with a label of with a for_loop structure
40 # The name of the label (unless the mark is an anonymous loop mark)
41 var name
: nullable String
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'
47 # Each 'continue' attached to the mark
48 var continues
: Array[AContinueExpr] = new Array[AContinueExpr]
50 # Each 'break' attached to the mark
51 var breaks
: Array[ABreakExpr] = new Array[ABreakExpr]
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
63 # The tool context used to display errors
64 var toolcontext
: ToolContext
66 var selfvariable
: Variable = new Variable("self")
68 init(toolcontext
: ToolContext)
70 self.toolcontext
= toolcontext
74 # All stacked scope. `scopes.first' is the current scope
75 private var scopes
: List[Scope] = new List[Scope]
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
81 var name
= variable
.name
82 var found
= search_variable
(name
)
84 self.error
(node
, "Error: A variable named `{name}' already exists")
87 scopes
.first
.variables
[name
] = variable
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
95 for scope
in scopes
do
96 var res
= scope
.get_variable
(name
)
104 redef fun visit
(n
: nullable ANode)
106 n
.accept_scope_visitor
(self)
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)
113 if node
== null then return
114 var scope
= new Scope
115 scope
.escapemark
= escapemark
116 scopes
.unshift
(scope
)
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
125 for scope
in scopes
do
126 var res
= scope
.escapemark
127 if res
!= null and res
.name
== name
then
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
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.")
149 var res
= new EscapeMark(name
, for_loop
)
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
160 if nlabel
!= null then
161 var name
= nlabel
.n_id
.text
162 var res
= search_label
(name
)
164 self.error
(nlabel
, "Syntax error: invalid label {name}.")
169 for scope
in scopes
do
170 var res
= scope
.escapemark
171 if res
!= null and res
.for_loop
then
175 self.error
(node
, "Syntax Error: 'break' statment outside block.")
181 private fun error
(node
: ANode, message
: String)
183 self.toolcontext
.error
(node
.hot_location
, message
)
188 var variables
: HashMap[String, Variable] = new HashMap[String, Variable]
190 var escapemark
: nullable EscapeMark = null
192 fun get_variable
(name
: String): nullable Variable
194 if self.variables
.has_key
(name
) then
195 return self.variables
[name
]
203 private fun accept_scope_visitor
(v
: ScopeVisitor)
210 # Entry point of the scope analysis
211 fun do_scope
(toolcontext
: ToolContext)
213 var v
= new ScopeVisitor(toolcontext
)
219 # The variable associated with the parameter
220 var variable
: nullable Variable
221 redef fun accept_scope_visitor
(v
)
225 var variable
= new Variable(nid
.text
)
226 v
.register_variable
(nid
, variable
)
227 self.variable
= variable
231 redef class AClosureDecl
232 # The variable associated with the closure declaration
233 var variable
: nullable ClosureVariable
234 redef fun accept_scope_visitor
(v
)
237 var variable
= new ClosureVariable(nid
.text
)
238 v
.register_variable
(nid
, variable
)
239 self.variable
= variable
243 redef class AVardeclExpr
244 # The variable associated with the variable declaration
245 var variable
: nullable Variable
246 redef fun accept_scope_visitor
(v
)
250 var variable
= new Variable(nid
.text
)
251 v
.register_variable
(nid
, variable
)
252 self.variable
= variable
256 redef class ASelfExpr
257 # The variable associated with the self reciever
258 var variable
: nullable Variable
259 redef fun accept_scope_visitor
(v
)
262 self.variable
= v
.selfvariable
266 redef class AContinueExpr
267 # The escape mark associated with the continue
268 var escapemark
: nullable EscapeMark
269 redef fun accept_scope_visitor
(v
)
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'.")
277 escapemark
.continues
.add
(self)
278 self.escapemark
= escapemark
282 redef class ABreakExpr
283 # The escape mark associated with the break
284 var escapemark
: nullable EscapeMark
285 redef fun accept_scope_visitor
(v
)
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
297 # The escape mark associated with the 'do' block
298 var escapemark
: nullable EscapeMark
299 redef fun accept_scope_visitor
(v
)
301 if n_label
!= null then
302 self.escapemark
= v
.make_escape_mark
(n_label
, false)
304 v
.enter_visit_block
(n_block
, self.escapemark
)
309 redef fun accept_scope_visitor
(v
)
311 v
.enter_visit
(n_expr
)
312 v
.enter_visit_block
(n_then
, null)
313 v
.enter_visit_block
(n_else
, null)
317 redef class AWhileExpr
318 # The escape mark associated with the 'while'
319 var escapemark
: nullable EscapeMark
320 redef fun accept_scope_visitor
(v
)
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
)
329 redef class ALoopExpr
330 # The escape mark associated with the 'loop'
331 var escapemark
: nullable EscapeMark
332 redef fun accept_scope_visitor
(v
)
334 var escapemark
= v
.make_escape_mark
(n_label
, true)
335 self.escapemark
= escapemark
336 v
.enter_visit_block
(n_block
, escapemark
)
341 # The automatic variables in order
342 var variables
: nullable Array[Variable]
344 # The escape mark associated with the 'for'
345 var escapemark
: nullable EscapeMark
347 redef fun accept_scope_visitor
(v
)
349 v
.enter_visit
(n_expr
)
351 # Protect automatic variables
352 v
.scopes
.unshift
(new Scope)
354 # Create the automatic variables
355 var variables
= new Array[Variable]
356 self.variables
= variables
358 var va
= new Variable(nid
.text
)
359 v
.register_variable
(nid
, va
)
363 var escapemark
= v
.make_escape_mark
(n_label
, true)
364 self.escapemark
= escapemark
365 v
.enter_visit_block
(n_block
, escapemark
)
371 redef class AVarFormExpr
372 # The associated variable
373 var variable
: nullable Variable
376 redef class ACallFormExpr
377 redef fun accept_scope_visitor
(v
)
379 if n_expr
isa AImplicitSelfExpr then
381 var variable
= v
.search_variable
(name
)
382 if variable
!= null then
384 if variable
isa ClosureVariable then
385 n
= new AClosureCallExpr.init_aclosurecallexpr
(n_id
, n_args
, n_closure_defs
)
386 n
.variable
= variable
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.")
392 n
= variable_create
(variable
)
393 n
.variable
= variable
396 n
.accept_scope_visitor
(v
)
404 # Create a variable acces corresponding to the call form
405 private fun variable_create
(variable
: Variable): AVarFormExpr is abstract
408 redef class ACallExpr
409 redef fun variable_create
(variable
)
411 return new AVarExpr.init_avarexpr
(n_id
)
415 redef class ACallAssignExpr
416 redef fun variable_create
(variable
)
418 return new AVarAssignExpr.init_avarassignexpr
(n_id
, n_assign
, n_value
)
422 redef class ACallReassignExpr
423 redef fun variable_create
(variable
)
425 return new AVarReassignExpr.init_avarreassignexpr
(n_id
, n_assign_op
, n_value
)
429 redef class AClosureCallExpr
430 # the associate closure variable
431 var variable
: nullable ClosureVariable
434 redef class AClosureDef
435 # The automatic variables in order
436 var variables
: nullable Array[Variable]
438 # The escape mark used with the closure
439 var escapemark
: nullable EscapeMark
441 redef fun accept_scope_visitor
(v
)
443 v
.scopes
.unshift
(new Scope)
445 var variables
= new Array[Variable]
446 self.variables
= variables
448 for nid
in self.n_ids
do
449 var va
= new Variable(nid
.text
)
450 v
.register_variable
(nid
, va
)
454 var escapemark
= v
.make_escape_mark
(n_label
, true)
455 self.escapemark
= escapemark
456 v
.enter_visit_block
(self.n_expr
, escapemark
)