1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2008 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 # Manage nested escapable blocks (while, for and closure) and escape statements (break and continue)
23 # All-in-one context for scoped things.
25 # * variable and labels visibility
26 # * control for 'break' and 'continue'
27 # This class manage itself the entree and the exit of scopes:
28 # * use push or push_escapable to enter in a new scope block
29 # * use pop to remove the last scope block (do not foget do pop!)
30 # Warning: ScopeContext are created empty: you must perform a first push to register variables
32 # Stack of scope blocks
33 var _stack
: Array[ScopeBlock] = new Array[ScopeBlock]
36 # (all variables, even out of scope ones)
38 var _variables
: Array[Variable] = new Array[Variable]
41 # (all labels, even out of scopes ones)
42 # Used to find duplicates
43 var _labels
: Array[ALabel] = new Array[ALabel]
45 # Return the variable associated with a name
46 fun [](n
: Symbol): nullable Variable
48 var i
= _stack
.length
- 1
51 var va
= b
.get_variable
(n
)
52 if va
!= null then return va
58 # Register a variable with its name in the current scope
59 # Display an error if name conflict
60 fun add_variable
(v
: Variable)
62 var old_var
= self[v
.name
]
63 if old_var
!= null then
64 _visitor
.error
(v
.decl
, "Error: '{v}' already defined at {old_var.decl.location.relative_to(v.decl.location)}.")
66 _stack
.last
.add_variable
(v
)
70 # Push a simple unlabeled variable scope block
73 var block
= new ScopeBlock(node
)
77 # Push a specific escapable block
78 # Display error message if there is a problem with the label
79 fun push_escapable
(block
: EscapableBlock, n_label
: nullable ALabel)
82 if n_label
!= null then
83 var lab
= n_label
.n_id
.to_symbol
85 if n_label
!= nl
and lab
== nl
.n_id
.to_symbol
then
86 visitor
.error
(n_label
, "Syntax error: label {lab} already defined at {nl.location.relative_to(n_label.location)}.")
95 # Return the last stacked block that accepts unlabelled break/continue
96 fun head
: nullable EscapableBlock
98 var i
= _stack
.length
- 1
101 if h
isa EscapableBlock and not (h
isa BreakOnlyEscapableBlock) then return h
107 # Return the block associed to a label
108 # Output an error end return null if the label is not known
109 fun get_by_label
(nl
: ALabel): nullable EscapableBlock
111 var i
= _stack
.length
- 1
112 var lab
= nl
.n_id
.to_symbol
115 if b
isa EscapableBlock and b
.lab
== lab
then return b
118 visitor
.error
(nl
, "Syntax error: invalid label {lab}.")
122 # Remove the last block (the last stacked)
128 readable var _visitor
: AbsSyntaxVisitor
129 init (v
: AbsSyntaxVisitor)
135 ###############################################################################
137 # A single scope block. Thez are stacked in a ScopeContext
138 # This block is used only to store local variables
140 # The syntax node of the block
141 readable var _node
: ANode
143 # List of local variables of the block
144 # Lazily constructed since many blocks does not have local variables
145 var _dico
: nullable HashMap[Symbol, Variable] = null
147 private fun add_variable
(v
: Variable)
151 dico
= new HashMap[Symbol, Variable]
157 private fun get_variable
(n
: Symbol): nullable Variable
160 if dico
== null then return null
161 if not dico
.has_key
(n
) then return null
171 # A escapable block correspond to a scope block where break and/or continue can by used.
172 # EscapableBlocks can also be labeled.
173 # 'for' and 'while' use this class
174 # labeled 'do' uses the BreakOnlyEscapableBlock subclass
175 # closures uses the EscapableClosure subclass
178 # The label of the block (if any)
179 # Set by the push in EscapableContext
180 readable var _lab
: nullable Symbol
182 # Is self a break closure ?
183 fun is_break_block
: Bool do return false
185 # Collected expressions used in breaks.
186 # null if break does not accept values.
187 # break_list is used to store expressions used in break statments and perform type checks latter
188 fun break_list
: nullable Array[AExpr] do return null
190 # The static type required by the continue statement (if any)
191 fun continue_stype
: nullable MMType do return null
193 # Alternatives flow contexts for breaks
194 readable var _break_flow_contexts
: Array[FlowContext] = new Array[FlowContext]
202 # specific EscapableBlock where only labelled break can be used
203 class BreakOnlyEscapableBlock
205 redef fun is_break_block
: Bool do return true
207 init(node
: ANode) do super
210 # specific EscapableBlock for closures
211 class EscapableClosure
213 # The associated closure
214 readable var _closure
: MMClosure
216 redef fun is_break_block
do return _closure
.is_break
218 redef readable var _break_list
: nullable Array[AExpr]
220 redef fun continue_stype
do return _closure
.signature
.return_type
222 init(node
: ANode, closure
: MMClosure, break_list
: nullable Array[AExpr])
226 _break_list
= break_list
230 ###############################################################################
234 # The associated escapable block
235 readable var _escapable
: nullable EscapableBlock
237 # The name of the keyword
238 fun kwname
: String is abstract
240 # Compute, set and return the associated escapable block
241 fun compute_escapable_block
(lctx
: ScopeContext): nullable EscapableBlock
243 var block
: nullable EscapableBlock
246 block
= lctx
.get_by_label
(nl
)
249 if block
== null then
250 lctx
.visitor
.error
(self, "Syntax Error: '{kwname}' statment outside block.")
258 redef class AContinueExpr
260 redef fun kwname
do return "continue"
263 redef class ABreakExpr
265 redef fun kwname
do return "break"