parser: rename all P* prod classes to A*
[nit.git] / src / syntax / control_flow.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008-2009 Jean Privat <jean@pryen.org>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 # Analysis control flow and variable visibility in property bodies, statements and expressions
18 package control_flow
19
20 import syntax_base
21
22 # Associate symbols to variable and variables to type
23 # Can be nested
24 abstract class VariableContext
25 # Look for the variable from its name
26 # Return null if nothing found
27 fun [](s: Symbol): nullable Variable
28 do
29 if _dico.has_key(s) then
30 return _dico[s]
31 else
32 return null
33 end
34 end
35
36 # Register a new variable with its name
37 fun add(v: Variable)
38 do
39 _dico[v.name] = v
40 _all_variables.add(v)
41 end
42
43 fun mark_is_set(v: Variable)
44 do
45 _set_variables.add(v)
46 end
47
48 fun check_is_set(n: ANode, v: Variable)
49 do
50 if v.must_be_set and not is_set(v) then
51 _visitor.error(n, "Error: variable '{v}' is possibly unset.")
52 var x = self
53 while true do
54 var loc = x.node.location
55 print " {if loc != null then loc.to_s else "????"}: {x._set_variables.join(", ")} ; {x._dico.join(", ")}"
56 var x0 = x
57 if x0 isa SubVariableContext then
58 x = x0.prev
59 else
60 break
61 end
62 end
63 end
64 end
65
66 # The effective static type of a given variable
67 # May be different from the declaration static type
68 fun stype(v: Variable): nullable MMType
69 do
70 if _stypes.has_key(v) then
71 return _stypes[v]
72 else
73 return v.stype
74 end
75 end
76
77 # Set effective static type of a given variable
78 # May be different from the declaration static type
79 fun stype=(v: Variable, t: nullable MMType)
80 do
81 _stypes[v] = t
82 end
83
84 # Variables by name (in the current context only)
85 var _dico: Map[Symbol, Variable]
86
87 # All variables in all contextes
88 var _all_variables: Set[Variable]
89
90 # Updated static type of variables
91 var _stypes: Map[Variable, nullable MMType] = new HashMap[Variable, nullable MMType]
92
93 # Build a new VariableContext
94 fun sub(node: ANode): SubVariableContext
95 do
96 return new SubVariableContext.with_prev(self, node)
97 end
98
99 # Build a nested VariableContext with new variable information
100 fun sub_with(node: ANode, v: Variable, t: MMType): SubVariableContext
101 do
102 var ctx = sub(node)
103 ctx.stype(v) = t
104 return ctx
105 end
106
107 # The visitor of the context (used to display error)
108 var _visitor: AbsSyntaxVisitor
109
110 # The syntax node that introduced the context
111 readable var _node: ANode
112
113 init(visitor: AbsSyntaxVisitor, node: ANode)
114 do
115 _visitor = visitor
116 _node = node
117 _dico = new HashMap[Symbol, Variable]
118 end
119
120 # Is a control flow break met? (return, break, continue)
121 readable writable var _unreash: Bool = false
122
123 # Is a control flow already broken?
124 # Used to avoid repeating the same error message
125 readable writable var _already_unreash: Bool = false
126
127 # Set of variable that are set (assigned)
128 readable var _set_variables: HashSet[Variable] = new HashSet[Variable]
129
130 # Is a variable set?
131 fun is_set(v: Variable): Bool
132 do
133 return _set_variables.has(v)
134 end
135
136 # Merge back one flow context information
137 fun merge(ctx: VariableContext)
138 do
139 if ctx.unreash then
140 unreash = true
141 if ctx.already_unreash then already_unreash = true
142 return
143 end
144 for v in _all_variables do
145 if not is_set(v) and ctx.is_set(v) then
146 mark_is_set(v)
147 end
148 var s = stype(v)
149 var s1 = ctx.stype(v)
150 if s1 != s then stype(v) = s1
151 end
152 end
153
154 # Merge back two alternative flow context informations
155 fun merge2(ctx1, ctx2, basectx: VariableContext)
156 do
157 if ctx1.unreash then
158 merge(ctx2)
159 else if ctx2.unreash then
160 merge(ctx1)
161 end
162 for v in _all_variables do
163 if not is_set(v) and ctx1.is_set(v) and ctx2.is_set(v) then
164 mark_is_set(v)
165 end
166
167 var s = stype(v)
168 var s1 = ctx1.stype(v)
169 var s2 = ctx2.stype(v)
170 if s1 == s and s2 == s then
171 # NOP
172 else if s1 == s2 then
173 stype(v) = s1
174 else if s2 == null or s1 < s2 then
175 stype(v) = s2
176 else if s1 == null or s2 < s1 then
177 stype(v) = s1
178 else
179 stype(v) = basectx.stype(v)
180 end
181 end
182 end
183
184 redef fun to_s
185 do
186 var s = new Buffer
187 s.append(node.location.to_s)
188 for v in _all_variables do
189 var t = stype(v)
190 if t == null then continue
191 s.append(" {v}:{t}")
192 end
193 return s.to_s
194 end
195 end
196
197 class RootVariableContext
198 special VariableContext
199 init(visitor: AbsSyntaxVisitor, node: ANode)
200 do
201 super(visitor, node)
202 _all_variables = new HashSet[Variable]
203 end
204 end
205
206 class SubVariableContext
207 special VariableContext
208 readable var _prev: VariableContext
209
210 redef fun [](s)
211 do
212 if _dico.has_key(s) then
213 return _dico[s]
214 else
215 return prev[s]
216 end
217 end
218
219 redef fun stype(v)
220 do
221 if _stypes.has_key(v) then
222 return _stypes[v]
223 else
224 return prev.stype(v)
225 end
226 end
227
228 init with_prev(p: VariableContext, node: ANode)
229 do
230 init(p._visitor, node)
231 _prev = p
232 _all_variables = p._all_variables
233 end
234
235 redef fun is_set(v)
236 do
237 return _set_variables.has(v) or _prev.is_set(v)
238 end
239 end
240
241 redef class Variable
242 # Is the variable must be set before being used ?
243 fun must_be_set: Bool do return false
244 end
245
246 redef class VarVariable
247 redef fun must_be_set do return true
248 end