syntax: Move VariableContext and related to a new control_flow.nit
[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 meth [](s: Symbol): 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 meth add(v: Variable)
38 do
39 _dico[v.name] = v
40 _all_variables.add(v)
41 end
42
43 meth mark_is_set(v: Variable)
44 do
45 _set_variables.add(v)
46 end
47
48 meth check_is_set(n: PNode, 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 print " {x.node.locate}: {x._set_variables.join(", ")} ; {x._dico.join(", ")}"
55 var x0 = x
56 if x0 isa SubVariableContext then
57 x = x0.prev
58 else
59 break
60 end
61 end
62 end
63 end
64
65 # The effective static type of a given variable
66 # May be different from the declaration static type
67 meth stype(v: Variable): MMType
68 do
69 return v.stype
70 end
71
72 # Variables by name (in the current context only)
73 attr _dico: Map[Symbol, Variable]
74
75 # All variables in all contextes
76 attr _all_variables: Set[Variable]
77
78 # Build a new VariableContext
79 meth sub(node: PNode): SubVariableContext
80 do
81 return new SubVariableContext.with_prev(self, node)
82 end
83
84 # Build a nested VariableContext with new variable information
85 meth sub_with(node: PNode, v: Variable, t: MMType): SubVariableContext
86 do
87 return new CastVariableContext.with_prev(self, node, v, t)
88 end
89
90 # The visitor of the context (used to display error)
91 attr _visitor: AbsSyntaxVisitor
92
93 # The syntax node that introduced the context
94 readable attr _node: PNode
95
96 init(visitor: AbsSyntaxVisitor, node: PNode)
97 do
98 _visitor = visitor
99 _node = node
100 _dico = new HashMap[Symbol, Variable]
101 end
102
103 # Is a control flow break met? (return, break, continue)
104 readable writable attr _unreash: Bool = false
105
106 # Is a control flow already broken?
107 # Used to avoid repeating the same error message
108 readable writable attr _already_unreash: Bool = false
109
110 # Set of variable that are set (assigned)
111 readable attr _set_variables: HashSet[Variable] = new HashSet[Variable]
112
113 # Is a variable set?
114 meth is_set(v: Variable): Bool
115 do
116 return _set_variables.has(v)
117 end
118
119 # Merge back one flow context information
120 meth merge(ctx: VariableContext)
121 do
122 if ctx.unreash then
123 unreash = true
124 if ctx.already_unreash then already_unreash = true
125 return
126 end
127 for v in _all_variables do
128 if not is_set(v) and ctx.is_set(v) then
129 mark_is_set(v)
130 end
131 end
132 end
133
134 # Merge back two alternative flow context informations
135 meth merge2(ctx1, ctx2: VariableContext)
136 do
137 if ctx1.unreash then
138 merge(ctx2)
139 else if ctx2.unreash then
140 merge(ctx1)
141 end
142 for v in _all_variables do
143 if not is_set(v) and ctx1.is_set(v) and ctx2.is_set(v) then
144 mark_is_set(v)
145 end
146 end
147 end
148 end
149
150 class RootVariableContext
151 special VariableContext
152 init(visitor: AbsSyntaxVisitor, node: PNode)
153 do
154 super(visitor, node)
155 _all_variables = new HashSet[Variable]
156 end
157 end
158
159 class SubVariableContext
160 special VariableContext
161 readable attr _prev: VariableContext
162
163 redef meth [](s)
164 do
165 if _dico.has_key(s) then
166 return _dico[s]
167 else
168 return prev[s]
169 end
170 end
171
172 redef meth stype(v)
173 do
174 return prev.stype(v)
175 end
176
177 init with_prev(p: VariableContext, node: PNode)
178 do
179 init(p._visitor, node)
180 _prev = p
181 _all_variables = p._all_variables
182 end
183
184 redef meth is_set(v)
185 do
186 return _set_variables.has(v) or _prev.is_set(v)
187 end
188 end
189
190 class CastVariableContext
191 special SubVariableContext
192 attr _variable: Variable
193 attr _var_type: MMType
194
195 redef meth stype(v)
196 do
197 if _variable == v then
198 return _var_type
199 end
200 return prev.stype(v)
201 end
202
203 init with_prev(p: VariableContext, node: PNode, v: Variable, t: MMType)
204 do
205 super(p, node)
206 _variable = v
207 _var_type =t
208 end
209 end
210
211 redef class Variable
212 # Is the variable must be set before being used ?
213 meth must_be_set: Bool do return false
214 end
215
216 redef class VarVariable
217 redef meth must_be_set do return true
218 end