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.
22 redef class ToolContext
23 var scope_phase
: Phase = new ScopePhase(self, null)
26 private class ScopePhase
28 redef fun process_npropdef
(npropdef
) do npropdef
.do_scope
(toolcontext
)
32 # A local variable (including parameters, automatic variables and self)
34 # The name of the variable (as used in the program)
38 redef fun to_s
do return self.name
41 # Mark where break and continue will branch.
42 # Marks are either associated with a label of with a for_loop structure
44 # The name of the label (unless the mark is an anonymous loop mark)
45 var name
: nullable String
47 # Is the mark atached to a loop (loop, while, for)
48 # Such a mark is a candidate to a labelless 'continue' or 'break'
51 # Each 'continue' attached to the mark
52 var continues
: Array[AContinueExpr] = new Array[AContinueExpr]
54 # Each 'break' attached to the mark
55 var breaks
: Array[ABreakExpr] = new Array[ABreakExpr]
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
66 # The tool context used to display errors
67 var toolcontext
: ToolContext
69 var selfvariable
: Variable = new Variable("self")
71 init(toolcontext
: ToolContext)
73 self.toolcontext
= toolcontext
77 # All stacked scope. `scopes.first` is the current scope
78 private var scopes
: List[Scope] = new List[Scope]
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
84 var name
= variable
.name
85 var found
= search_variable
(name
)
87 self.error
(node
, "Error: A variable named `{name}' already exists")
90 scopes
.first
.variables
[name
] = variable
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
98 for scope
in scopes
do
99 var res
= scope
.get_variable
(name
)
109 n
.accept_scope_visitor
(self)
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)
116 if node
== null then return
117 var scope
= new Scope
118 scope
.escapemark
= escapemark
119 scopes
.unshift
(scope
)
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
128 for scope
in scopes
do
129 var res
= scope
.escapemark
130 if res
!= null and res
.name
== name
then
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
141 var name
: nullable String
142 if nlabel
!= null then
143 var nid
= nlabel
.n_id
145 var res
= search_label
("")
147 self.error
(nlabel
, "Syntax error: anonymous label already defined.")
152 var found
= self.search_label
(name
)
153 if found
!= null then
154 self.error
(nlabel
, "Syntax error: label {name} already defined.")
160 var res
= new EscapeMark(name
, for_loop
)
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
171 if nlabel
!= null then
172 var nid
= nlabel
.n_id
174 var res
= search_label
("")
176 self.error
(nlabel
, "Syntax error: invalid anonymous label.")
182 var res
= search_label
(name
)
184 self.error
(nlabel
, "Syntax error: invalid label {name}.")
189 for scope
in scopes
do
190 var res
= scope
.escapemark
195 self.error
(node
, "Syntax Error: 'break' statment outside block.")
201 private fun error
(node
: ANode, message
: String)
203 self.toolcontext
.error
(node
.hot_location
, message
)
208 var variables
: HashMap[String, Variable] = new HashMap[String, Variable]
210 var escapemark
: nullable EscapeMark = null
212 fun get_variable
(name
: String): nullable Variable
214 if self.variables
.has_key
(name
) then
215 return self.variables
[name
]
223 private fun accept_scope_visitor
(v
: ScopeVisitor)
230 # Entry point of the scope analysis
231 fun do_scope
(toolcontext
: ToolContext)
233 var v
= new ScopeVisitor(toolcontext
)
239 # The variable associated with the parameter
240 var variable
: nullable Variable
241 redef fun accept_scope_visitor
(v
)
245 var variable
= new Variable(nid
.text
)
246 v
.register_variable
(nid
, variable
)
247 self.variable
= variable
251 redef class AVardeclExpr
252 # The variable associated with the variable declaration
253 var variable
: nullable Variable
254 redef fun accept_scope_visitor
(v
)
258 var variable
= new Variable(nid
.text
)
259 v
.register_variable
(nid
, variable
)
260 self.variable
= variable
264 redef class ASelfExpr
265 # The variable associated with the self reciever
266 var variable
: nullable Variable
267 redef fun accept_scope_visitor
(v
)
270 self.variable
= v
.selfvariable
274 redef class AContinueExpr
275 # The escape mark associated with the continue
276 var escapemark
: nullable EscapeMark
277 redef fun accept_scope_visitor
(v
)
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'.")
285 escapemark
.continues
.add
(self)
286 self.escapemark
= escapemark
290 redef class ABreakExpr
291 # The escape mark associated with the break
292 var escapemark
: nullable EscapeMark
293 redef fun accept_scope_visitor
(v
)
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
305 # The escape mark associated with the 'do' block
306 var escapemark
: nullable EscapeMark
307 redef fun accept_scope_visitor
(v
)
309 self.escapemark
= v
.make_escape_mark
(n_label
, false)
310 v
.enter_visit_block
(n_block
, self.escapemark
)
315 redef fun accept_scope_visitor
(v
)
317 v
.enter_visit
(n_expr
)
318 v
.enter_visit_block
(n_then
, null)
319 v
.enter_visit_block
(n_else
, null)
323 redef class AWhileExpr
324 # The escape mark associated with the 'while'
325 var escapemark
: nullable EscapeMark
326 redef fun accept_scope_visitor
(v
)
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
)
335 redef class ALoopExpr
336 # The escape mark associated with the 'loop'
337 var escapemark
: nullable EscapeMark
338 redef fun accept_scope_visitor
(v
)
340 var escapemark
= v
.make_escape_mark
(n_label
, true)
341 self.escapemark
= escapemark
342 v
.enter_visit_block
(n_block
, escapemark
)
347 # The automatic variables in order
348 var variables
: nullable Array[Variable]
350 # The escape mark associated with the 'for'
351 var escapemark
: nullable EscapeMark
353 redef fun accept_scope_visitor
(v
)
355 v
.enter_visit
(n_expr
)
357 # Protect automatic variables
358 v
.scopes
.unshift
(new Scope)
360 # Create the automatic variables
361 var variables
= new Array[Variable]
362 self.variables
= variables
364 var va
= new Variable(nid
.text
)
365 v
.register_variable
(nid
, va
)
369 var escapemark
= v
.make_escape_mark
(n_label
, true)
370 self.escapemark
= escapemark
371 v
.enter_visit_block
(n_block
, escapemark
)
377 redef class AVarFormExpr
378 # The associated variable
379 var variable
: nullable Variable
382 redef class ACallFormExpr
383 redef fun accept_scope_visitor
(v
)
385 if n_expr
isa AImplicitSelfExpr then
387 var variable
= v
.search_variable
(name
)
388 if variable
!= null then
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.")
394 n
= variable_create
(variable
)
395 n
.variable
= variable
397 n
.accept_scope_visitor
(v
)
405 # Create a variable acces corresponding to the call form
406 private fun variable_create
(variable
: Variable): AVarFormExpr is abstract
409 redef class ACallExpr
410 redef fun variable_create
(variable
)
412 return new AVarExpr.init_avarexpr
(n_id
)
416 redef class ACallAssignExpr
417 redef fun variable_create
(variable
)
419 return new AVarAssignExpr.init_avarassignexpr
(n_id
, n_assign
, n_value
)
423 redef class ACallReassignExpr
424 redef fun variable_create
(variable
)
426 return new AVarReassignExpr.init_avarreassignexpr
(n_id
, n_assign_op
, n_value
)