model: new metamodel
[nit.git] / src / model / model_base.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 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 module model_base
18
19 import poset
20 import location
21
22 # Simple way to store an HashMap[K, Array[V]]
23 # FIXME: this should move to its own module
24 class MultiHashMap[K: Object, V]
25 super HashMap[K, Array[V]]
26
27 # Add `v' to the array associated with `k'.
28 # If there is no array associated, then create it.
29 fun add_one(k: K, v: V)
30 do
31 if self.has_key(k) then
32 self[k].add(v)
33 else
34 self[k] = [v]
35 end
36 end
37
38 init do end
39 end
40
41 # Simple way to store an HashMap[K1, HashMap[K2, V]]
42 # FIXME: this should move to its own module
43 class HashMap2[K1: Object, K2: Object, V]
44 private var level1: HashMap[K1, HashMap[K2, V]] = new HashMap[K1, HashMap[K2, V]]
45 fun [](k1: K1, k2: K2): nullable V
46 do
47 var level1 = self.level1
48 if not level1.has_key(k1) then return null
49 var level2 = level1[k1]
50 if not level2.has_key(k2) then return null
51 return level2[k2]
52 end
53 fun []=(k1: K1, k2: K2, v: V)
54 do
55 var level1 = self.level1
56 var level2: HashMap[K2, V]
57 if not level1.has_key(k1) then
58 level2 = new HashMap[K2, V]
59 level1[k1] = level2
60 else
61 level2 = level1[k1]
62 end
63 level2[k2] = v
64 end
65 end
66
67 # Simple way to store an HashMap[K1, HashMap[K2, HashMap[K3, V]]]
68 # FIXME: this should move to its own module
69 class HashMap3[K1: Object, K2: Object, K3: Object, V]
70 private var level1: HashMap[K1, HashMap2[K2, K3, V]] = new HashMap[K1, HashMap2[K2, K3, V]]
71 fun [](k1: K1, k2: K2, k3: K3): nullable V
72 do
73 var level1 = self.level1
74 if not level1.has_key(k1) then return null
75 var level2 = level1[k1]
76 return level2[k2, k3]
77 end
78 fun []=(k1: K1, k2: K2, k3: K3, v: V)
79 do
80 var level1 = self.level1
81 var level2: HashMap2[K2, K3, V]
82 if not level1.has_key(k1) then
83 level2 = new HashMap2[K2, K3, V]
84 level1[k1] = level2
85 else
86 level2 = level1[k1]
87 end
88 level2[k2, k3] = v
89 end
90 end
91
92 # The container class of a Nit object-oriented model.
93 # A model knows modules, classes and properties and can retrieve them.
94 class Model
95 # All known modules
96 var mmodules: Array[MModule] = new Array[MModule]
97
98 # Module nesting hierarchy.
99 # where mainmodule < mainmodule::nestedmodule
100 var mmodule_nesting_hierarchy: POSet[MModule] = new POSet[MModule]
101
102 # Full module importation hierarchy including private or nested links.
103 var mmodule_importation_hierarchy: POSet[MModule] = new POSet[MModule]
104
105 # Collections of modules grouped by their short names
106 private var mmodules_by_name: MultiHashMap[String, MModule] = new MultiHashMap[String, MModule]
107
108 # Return all module named `name'
109 # If such a module does not exist, null is returned (instead of an empty array)
110 #
111 # Visibility or modules are not considered
112 fun get_mmodules_by_name(name: String): nullable Array[MModule]
113 do
114 if mmodules_by_name.has_key(name) then
115 return mmodules_by_name[name]
116 else
117 return null
118 end
119 end
120 end
121
122 # A Nit module is usually associated with a Nit source file.
123 # Modules can be nested (see `direct_owner', `public_owner', and `in_nesting')
124 class MModule
125 # The model considered
126 var model: Model
127
128 # The direct nesting module, return null if self is not nested (ie. is a top-level module)
129 var direct_owner: nullable MModule
130
131 # The short name of the module
132 var name: String
133
134 # The origin of the definition
135 var location: Location
136
137 # Alias for `name'
138 redef fun to_s do return self.name
139
140 # The view of the module in the module_nesting_hierarchy
141 var in_nesting: POSetElement[MModule]
142
143 # The view of the module in the module_importation_hierarchy
144 var in_importation: POSetElement[MModule]
145
146 # The canonical name of the module
147 # Example: "owner::name"
148 fun full_name: String
149 do
150 var owner = self.public_owner
151 if owner == null then
152 return self.name
153 else
154 return "{owner.full_name}::{self.name}"
155 end
156 end
157
158 # Create a new empty module and register it to a model
159 # `direct_owner' is the direct owner (null if top-level module)
160 init(model: Model, direct_owner: nullable MModule, name: String, location: Location)
161 do
162 self.model = model
163 self.name = name
164 self.location = location
165 model.mmodules_by_name.add_one(name, self)
166 model.mmodules.add(self)
167 self.in_nesting = model.mmodule_nesting_hierarchy.add_node(self)
168 self.direct_owner = direct_owner
169 if direct_owner != null then
170 model.mmodule_nesting_hierarchy.add_edge(direct_owner, self)
171 end
172 self.in_importation = model.mmodule_importation_hierarchy.add_node(self)
173 end
174
175 # Register the imported modules (ie "import some_module")
176 # This function can only invoked once by mmodule.
177 # The visibility must be set with `se_visibility_for'.
178 fun set_imported_mmodules(imported_mmodules: Array[MModule])
179 do
180 assert unique_invocation: self.in_importation.direct_greaters.is_empty
181 for m in imported_mmodules do
182 self.model.mmodule_importation_hierarchy.add_edge(self, m)
183 end
184 end
185
186 private var intrude_mmodules: HashSet[MModule] = new HashSet[MModule]
187 private var public_mmodules: HashSet[MModule] = new HashSet[MModule]
188 private var private_mmodules: HashSet[MModule] = new HashSet[MModule]
189
190 # Return the visibility level of an imported module `m`
191 fun visibility_for(m: MModule): MVisibility
192 do
193 if m == self then return intrude_visibility
194 if self.intrude_mmodules.has(m) then return intrude_visibility
195 if self.public_mmodules.has(m) then return public_visibility
196 if self.private_mmodules.has(m) then return private_visibility
197 return none_visibility
198 end
199
200 # Set the visibility of an imported module
201 # REQUIRE: the visibility of the modules imported by `m' are already set for `m'
202 fun set_visibility_for(m: MModule, v: MVisibility)
203 do
204 if v == intrude_visibility then
205 self.intrude_mmodules.add(m)
206 self.intrude_mmodules.add_all(m.intrude_mmodules)
207 self.public_mmodules.add_all(m.public_mmodules)
208 self.private_mmodules.add_all(m.private_mmodules)
209 else if v == public_visibility then
210 self.public_mmodules.add(m)
211 self.public_mmodules.add_all(m.intrude_mmodules)
212 self.public_mmodules.add_all(m.public_mmodules)
213 else if v == private_visibility then
214 self.private_mmodules.add(m)
215 self.private_mmodules.add_all(m.intrude_mmodules)
216 self.private_mmodules.add_all(m.public_mmodules)
217 else
218 print "{self} visibility for {m} = {v}"
219 abort # invalid visibility
220 end
221 end
222
223 # The first module in the nesting hierarchy to export self as public
224 # This function is used to determine the canonical name of modules, classes and properties.
225 # REQUIRE: the visibility of all nesting modules is already set for `m'.
226 fun public_owner: nullable MModule
227 do
228 var res = self.direct_owner
229 var last = res
230 while last != null do
231 if last.visibility_for(self) >= public_visibility then res = last
232 last = last.direct_owner
233 end
234 return res
235 end
236
237 # Return true if a class or a property introduced in `intro_mmodule' with a visibility of 'visibility' is visible in self.
238 fun is_visible(intro_mmodule: MModule, visibility: MVisibility): Bool
239 do
240 var v = visibility_for(intro_mmodule)
241 if v == intrude_visibility then
242 return visibility >= private_visibility
243 else if v == public_visibility then
244 return visibility > private_visibility
245 else if v == private_visibility then
246 return visibility > private_visibility
247 else if v == none_visibility then
248 return false
249 else
250 abort
251 end
252 end
253 end
254
255 # A visibility (for modules, class and properties)
256 # Valid visibility are:
257 #
258 # * intrude_visibility
259 # * public_visibility
260 # * protected_visibility
261 # * none_visibility
262 #
263 # Note this class is basically an enum.
264 # FIXME: use a real enum once user-defined enums are available
265 class MVisibility
266 super Comparable
267 redef type OTHER: MVisibility
268
269 redef var to_s: String
270
271 private var level: Int
272
273 private init(s: String, level: Int)
274 do
275 self.to_s = s
276 self.level = level
277 end
278
279 # Is self give less visibility than other
280 # none < private < protected < public < intrude
281 redef fun <(other)
282 do
283 return self.level < other.level
284 end
285 end
286
287 fun intrude_visibility: MVisibility do return once new MVisibility("intrude", 4)
288 fun public_visibility: MVisibility do return once new MVisibility("public", 4)
289 fun protected_visibility: MVisibility do return once new MVisibility("protected", 3)
290 fun private_visibility: MVisibility do return once new MVisibility("private", 2)
291 fun none_visibility: MVisibility do return once new MVisibility("none", 2)