neo: Remove `is_intro`.
[nit.git] / src / neo.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Save and load `Model` from/to Neo4j base.
16 #
17 # Nit models are composed by MEntities.
18 # This module creates NeoNode for each MEntity found in a `Model` and save them
19 # into Neo4j database.
20 #
21 # see `Neo4jClient`.
22 #
23 # NeoNodes can also be translated back to MEntities to rebuild a Nit `Model`.
24 #
25 # Structure of the nit `Model` in base:
26 #
27 # Note : Any null or empty attribute will not be saved in the database.
28 #
29 # For any `MEntity` (in addition to specific data):
30 #
31 # * labels: model name (`model_name`) and `MEntity`.
32 # * `name`: short (unqualified) name.
33 # * `mdoc`: JSON array representing the associated Markdown documentation
34 # (one item by line).
35 #
36 # Note : All nodes described here are MEntities.
37 #
38 # `MProject`
39 #
40 # * labels: `MProject`, `model_name` and `MEntity`.
41 # * `(:MProject)-[:ROOT]->(:MGroup)`: root of the group tree.
42 #
43 # `MGroup`
44 #
45 # * labels: `MGroup`, `model_name` and `MEntity`.
46 # * `full_name`: fully qualified name.
47 # * `(:MGroup)-[:PROJECT]->(:MProject)`: associated project.
48 # * `(:MGroup)-[:PARENT]->(:MGroup)`: parent group. Does not exist for the root
49 # group.
50 # * `(:MGroup)-[:DECLARES]->(:MModule)`: modules that are direct children of
51 # this group.
52 # * `(:MGroup)-[:NESTS]->(:MGroup)`: nested groups that are direct children of
53 # this group.
54 #
55 # `MModule`
56 #
57 # * labels: `MModule`, `model_name` and `MEntity`.
58 # * `full_name`: fully qualified name.
59 # * `location`: origin of the definition. SEE: `Location.to_s`
60 # * `(:MModule)-[:IMPORTS]->(:MModule)`: modules that are imported directly.
61 # * `(:MModule)-[:INTRODUCES]->(:MClass)`: all by classes introduced by this
62 # module.
63 # * `(:MModule)-[:DEFINES]->(:MClassDef)`: all class definitons contained in
64 # this module.
65 #
66 # `MClass`
67 #
68 # * labels: `MClass`, `model_name` and `MEntity`.
69 # * `full_name`: fully qualified name.
70 # * `arity`: number of generic formal parameters. 0 if the class is not generic.
71 # * `kind`: kind of the class (`interface`, `abstract class`, etc.)
72 # * `visibility`: visibility of the class.
73 # * `(:MClass)-[:CLASSTYPE]->(:MClassType)`: SEE: `MClass.mclass_type`
74 #
75 # Arguments in the `CLASSTYPE` are named following the `parameter_names`
76 # attribute of the `MClassDef` that introduces the class. A class definition
77 # introduces a class if and only if it has this class as `MCLASS` and
78 # has `is_intro` set to `true`.
79 #
80 # `MClassDef`
81 #
82 # * labels: `MClassDef`, `model_name` and `MEntity`.
83 # * `location`: origin of the definition. SEE: `Location.to_s`
84 # * `parameter_names`: JSON array listing the name of each formal generic
85 # parameter (in order of declaration).
86 # * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)`: bounded type associated to the
87 # classdef.
88 # * `(:MClassDef)-[:MCLASS]->(:MClass)`: associated `MClass`.
89 # * `(:MClassDef)-[:INTRODUCES]->(:MProperty)`: all properties introduced by
90 # the classdef.
91 # * `(:MClassDef)-[:DECLARES]->(:MPropDef)`: all property definitions in the
92 # classdef (introductions and redefinitions).
93 # * `(:MClassDef)-[:INHERITS]->(:MClassType)`: all declared super-types
94 #
95 # `MProperty`
96 #
97 # * labels: `MProperty`, `model_name` and `MEntity`. Must also have `MMethod`,
98 # `MAttribute` or `MVirtualTypeProp`, depending on the class of the represented
99 # entity.
100 # * `full_name`: fully qualified name.
101 # * `visibility`: visibility of the property.
102 # * `is_init`: Indicates if the property is a constructor. Exists only if the
103 # node is a `MMethod`.
104 # * `(:MProperty)-[:INTRO_CLASSDEF]->(:MClassDef)`: classdef that introduces
105 # the property.
106 #
107 # `MPropDef`
108 #
109 # * labels: `MPropDef`, `model_name` and `MEntity`. Must also have `MMethodDef`,
110 # `MAttributeDef` or `MVirtualTypeDef`, depending on the class of the
111 # represented entity.
112 # * `location`: origin of the definition. SEE: `Location.to_s`.
113 # * `(:MPropDef)-[:DEFINES]->(:MProperty)`: associated property.
114 #
115 # Additional attributes and relationship for `MMethodDef`:
116 #
117 # * `is_abstract`: Is the method definition abstract?
118 # * `is_intern`: Is the method definition intern?
119 # * `is_extern`: Is the method definition extern?
120 # * `(:MMethodDef)-[:SIGNATURE]->(:MSignature)`: signature attached to the
121 # property definition.
122 #
123 # Additional relationship for `MVirtualTypeDef`:
124 #
125 # * `(:MVirtualTypeDef)-[:BOUND]->(:MType)`: type to which the virtual type
126 # is bound in this definition. Exists only if this definition bound the virtual
127 # type to an effective type.
128 #
129 # `MType`
130 #
131 # * labels: `MType`, `model_name` and `MEntity`. Must also have `MClassType`,
132 # `MNullableType`, `MVirtualType` or `MSignature`, depending on the class of
133 # the represented entity.
134 #
135 # Additional label and relationships for `MClassType`:
136 #
137 # * If it is a `MGenericType`, also has the `MGenericType` label.
138 # * `(:MClassType)-[:CLASS]->(:MClass)`: SEE: `MClassType.mclass`
139 # * `(:MClassType)-[:ARGUMENT]->(:MType)`: type arguments.
140 #
141 # Arguments are named following the `parameter_names` attribute of the
142 # `MClassDef` that introduces the class referred by `CLASS`.
143 #
144 # Additional relationship for `MVirtualType`:
145 #
146 # * `(:MVirtualType)-[:PROPERTY]->(:MProperty)`: associated property that
147 # determines the type (usually a `MVirtualTypeProp`).
148 #
149 # Additional attribute and relationship for `MParameterType`:
150 #
151 # * `rank`: position of the parameter (0 for the first parameter).
152 # * `(:MParameterType)-[:CLASS]->(:MClass)`: generic class where the parameter
153 # belong.
154 #
155 # Additional relationship for `MNullableType`:
156 #
157 # * `(:MNullableType)-[:TYPE]->(:MType)`: base type of the nullable type.
158 #
159 # Additional attribute and relationships for `MSignature`:
160 #
161 # * `parameter_names`: JSON array representing the list of the parameter names.
162 # * `(:MSignature)-[:PARAMETER]->(:MParameter)`: parameters.
163 # * `(:MSignature)-[:RETURNTYPE]->(:MType)`: return type. Does not exist for
164 # procedures.
165 #
166 # In order to maintain the correct parameters order, each `MSignature` node
167 # contains an array of parameter names corresponding to the parameter order in
168 # the signature.
169 #
170 # For example, if the source code contains:
171 #
172 # fun foo(a: A, b: B, c: C)
173 #
174 # The `MSignature` node will contain a property
175 # `parameter_names = ["a", "b", "c"]` so the MSignature can be reconstructed
176 # with the parameters in the correct order.
177 #
178 # `MParameter`
179 #
180 # * labels: `MParameter`, `model_name` and `MEntity`.
181 # * `is_vararg`: Is the parameter a vararg?
182 # * `rank`: position of the parameter (0 for the first parameter).
183 # * `(:MParameter)-[:TYPE]->(:MType)`: static type of the parameter.
184 #
185 # MParameters are also ranked by their position in the corresponding signature.
186 # Rank 0 for the first parameter, 1 for the next one and etc.
187 module neo
188
189 import model
190 import neo4j
191 import toolcontext
192
193 # Helper class that can save and load a `Model` into a Neo4j database.
194 class NeoModel
195
196 # The model name.
197 #
198 # Because we use only one Neo4j instance to store all the models,
199 # we need to mark their appartenance to a particular model and avoid loading all models.
200 #
201 # The name is used as a Neo label on each created nodes and used to load nodes from base.
202 var model_name: String
203
204 # The toolcontext used to init the `NeoModel` tool.
205 var toolcontext: ToolContext
206
207 # The Neo4j `client` used to communicate with the Neo4j instance.
208 var client: Neo4jClient
209
210 # Fill `model` using base pointed by `client`.
211 fun load(model: Model): Model do
212 toolcontext.info("Locate all mentities...", 1)
213 var nodes = client.nodes_with_label(model_name)
214
215 toolcontext.info("Preload nodes...", 1)
216 pull_all_nodes(nodes)
217 toolcontext.info("Preload edges...", 1)
218 pull_all_edges(nodes)
219
220 toolcontext.info("Build model...", 1)
221 nodes = client.nodes_with_labels([model_name, "MProject"])
222 for node in nodes do to_mproject(model, node)
223 nodes = client.nodes_with_labels([model_name, "MGroup"])
224 for node in nodes do to_mgroup(model, node)
225 nodes = client.nodes_with_labels([model_name, "MModule"])
226 for node in nodes do to_mmodule(model, node)
227 nodes = client.nodes_with_labels([model_name, "MClass"])
228 for node in nodes do to_mclass(model, node)
229 nodes = client.nodes_with_labels([model_name, "MClassDef"])
230 for node in nodes do to_mclassdef(model, node)
231 nodes = client.nodes_with_labels([model_name, "MProperty"])
232 for node in nodes do to_mproperty(model, node)
233 nodes = client.nodes_with_labels([model_name, "MPropDef"])
234 for node in nodes do to_mpropdef(model, node)
235 return model
236 end
237
238 # Save `model` in the base pointed by `client`.
239 fun save(model: Model) do
240 var nodes = collect_model_nodes(model)
241 toolcontext.info("Save {nodes.length} nodes...", 1)
242 push_all(nodes)
243 var edges = collect_model_edges(model)
244 toolcontext.info("Save {edges.length} edges...", 1)
245 push_all(edges)
246 end
247
248 # Save `neo_entities` in base using batch mode.
249 private fun push_all(neo_entities: Collection[NeoEntity]) do
250 var batch = new NeoBatch(client)
251 var len = neo_entities.length
252 var sum = 0
253 var i = 1
254 for nentity in neo_entities do
255 batch.save_entity(nentity)
256 if i == batch_max_size then
257 do_batch(batch)
258 sum += batch_max_size
259 toolcontext.info(" {sum * 100 / len}% done", 1)
260 batch = new NeoBatch(client)
261 i = 1
262 else
263 i += 1
264 end
265 end
266 do_batch(batch)
267 end
268
269 # Load content for all `nodes` from base.
270 #
271 # Content corresponds to properties and labels that are loaded in batch mode.
272 private fun pull_all_nodes(nodes: Collection[NeoNode]) do
273 var batch = new NeoBatch(client)
274 var len = nodes.length
275 var sum = 0
276 var i = 1
277 for node in nodes do
278 batch.load_node(node)
279 if i == batch_max_size then
280 do_batch(batch)
281 sum += batch_max_size
282 toolcontext.info(" {sum * 100 / len}% done", 1)
283 batch = new NeoBatch(client)
284 i = 1
285 else
286 i += 1
287 end
288 end
289 do_batch(batch)
290 end
291
292 # Load all edges from base linked to `nodes`.
293 #
294 # Edges are loaded in batch mode.
295 private fun pull_all_edges(nodes: Collection[NeoNode]) do
296 var batch = new NeoBatch(client)
297 var len = nodes.length
298 var sum = 0
299 var i = 1
300 for node in nodes do
301 batch.load_node_edges(node)
302 if i == batch_max_size then
303 do_batch(batch)
304 sum += batch_max_size
305 toolcontext.info(" {sum * 100 / len}% done", 1)
306 batch = new NeoBatch(client)
307 i = 1
308 else
309 i += 1
310 end
311 end
312 do_batch(batch)
313 end
314
315 # How many operation can be executed in one batch?
316 private var batch_max_size = 1000
317
318 # Execute `batch` and check for errors.
319 #
320 # Abort if `batch.execute` returns errors.
321 private fun do_batch(batch: NeoBatch) do
322 var errors = batch.execute
323 if not errors.is_empty then
324 print errors
325 exit(1)
326 end
327 end
328
329 # Collect all nodes from the current `model`.
330 private fun collect_model_nodes(model: Model): Collection[NeoNode] do
331 for mproject in model.mprojects do
332 to_node(mproject)
333 for mgroup in mproject.mgroups do to_node(mgroup)
334 end
335 return nodes.values
336 end
337
338 # Collect all edges from the current `model`.
339 #
340 # Actually collect all out_edges from all nodes.
341 private fun collect_model_edges(model: Model): Collection[NeoEdge] do
342 var edges = new HashSet[NeoEdge]
343 for node in nodes.values do edges.add_all(node.out_edges)
344 return edges
345 end
346
347 # Mentities associated to nodes.
348 private var mentities = new HashMap[NeoNode, MEntity]
349
350 # Nodes associated with MEntities.
351 private var nodes = new HashMap[MEntity, NeoNode]
352
353 # Get the `NeoNode` associated with `mentity`.
354 # `mentities` are stored locally to avoid duplication.
355 fun to_node(mentity: MEntity): NeoNode do
356 if nodes.has_key(mentity) then return nodes[mentity]
357 if mentity isa MProject then return mproject_node(mentity)
358 if mentity isa MGroup then return mgroup_node(mentity)
359 if mentity isa MModule then return mmodule_node(mentity)
360 if mentity isa MClass then return mclass_node(mentity)
361 if mentity isa MClassDef then return mclassdef_node(mentity)
362 if mentity isa MProperty then return mproperty_node(mentity)
363 if mentity isa MPropDef then return mpropdef_node(mentity)
364 if mentity isa MType then return mtype_node(mentity)
365 if mentity isa MParameter then return mparameter_node(mentity)
366 abort
367 end
368
369 # Make a new `NeoNode` based on `mentity`.
370 private fun make_node(mentity: MEntity): NeoNode do
371 var node = new NeoNode
372 nodes[mentity] = node
373 node.labels.add "MEntity"
374 node.labels.add model_name
375 node["name"] = mentity.name
376 if mentity.mdoc != null then node["mdoc"] = new JsonArray.from(mentity.mdoc.content)
377 return node
378 end
379
380 # Build a `NeoNode` representing `mproject`.
381 private fun mproject_node(mproject: MProject): NeoNode do
382 var node = make_node(mproject)
383 node.labels.add "MProject"
384 var root = mproject.root
385 if root != null then
386 node.out_edges.add(new NeoEdge(node, "ROOT", to_node(root)))
387 end
388 return node
389 end
390
391 # Build a new `MProject` from a `node`.
392 #
393 # REQUIRE `node.labels.has("MProject")`
394 private fun to_mproject(model: Model, node: NeoNode): MProject do
395 if mentities.has_key(node) then return mentities[node].as(MProject)
396 assert node.labels.has("MProject")
397 var mproject = new MProject(node["name"].to_s, model)
398 mentities[node] = mproject
399 set_doc(node, mproject)
400 mproject.root = to_mgroup(model, node.out_nodes("ROOT").first)
401 return mproject
402 end
403
404 # Build a `NeoNode` representing `mgroup`.
405 private fun mgroup_node(mgroup: MGroup): NeoNode do
406 var node = make_node(mgroup)
407 node.labels.add "MGroup"
408 node["full_name"] = mgroup.full_name
409 var parent = mgroup.parent
410 node.out_edges.add(new NeoEdge(node, "PROJECT", to_node(mgroup.mproject)))
411 if parent != null then
412 node.out_edges.add(new NeoEdge(node, "PARENT", to_node(parent)))
413 end
414 for mmodule in mgroup.mmodules do
415 node.out_edges.add(new NeoEdge(node, "DECLARES", to_node(mmodule)))
416 end
417 for subgroup in mgroup.in_nesting.direct_smallers do
418 node.in_edges.add(new NeoEdge(node, "NESTS", to_node(subgroup)))
419 end
420 return node
421 end
422
423 # Build a new `MGroup` from a `node`.
424 #
425 # REQUIRE `node.labels.has("MGroup")`
426 private fun to_mgroup(model: Model, node: NeoNode): MGroup do
427 if mentities.has_key(node) then return mentities[node].as(MGroup)
428 assert node.labels.has("MGroup")
429 var mproject = to_mproject(model, node.out_nodes("PROJECT").first)
430 var parent: nullable MGroup = null
431 var out = node.out_nodes("PARENT")
432 if not out.is_empty then
433 parent = to_mgroup(model, out.first)
434 end
435 var mgroup = new MGroup(node["name"].to_s, mproject, parent)
436 mentities[node] = mgroup
437 set_doc(node, mgroup)
438 return mgroup
439 end
440
441 # Build a `NeoNode` representing `mmodule`.
442 private fun mmodule_node(mmodule: MModule): NeoNode do
443 var node = make_node(mmodule)
444 node.labels.add "MModule"
445 node["full_name"] = mmodule.full_name
446 node["location"] = mmodule.location.to_s
447 for parent in mmodule.in_importation.direct_greaters do
448 node.out_edges.add(new NeoEdge(node, "IMPORTS", to_node(parent)))
449 end
450 for mclass in mmodule.intro_mclasses do
451 node.out_edges.add(new NeoEdge(node, "INTRODUCES", to_node(mclass)))
452 end
453 for mclassdef in mmodule.mclassdefs do
454 node.out_edges.add(new NeoEdge(node, "DEFINES", to_node(mclassdef)))
455 end
456 return node
457 end
458
459 # Build a new `MModule` from a `node`.
460 #
461 # REQUIRE `node.labels.has("MModule")`
462 private fun to_mmodule(model: Model, node: NeoNode): MModule do
463 if mentities.has_key(node) then return mentities[node].as(MModule)
464 assert node.labels.has("MModule")
465 var ins = node.in_nodes("DECLARES")
466 var mgroup: nullable MGroup = null
467 if not ins.is_empty then
468 mgroup = to_mgroup(model, ins.first)
469 end
470 var name = node["name"].to_s
471 var location = to_location(node["location"].to_s)
472 var mmodule = new MModule(model, mgroup, name, location)
473 mentities[node] = mmodule
474 set_doc(node, mmodule)
475 var imported_mmodules = new Array[MModule]
476 for smod in node.out_nodes("IMPORTS") do
477 imported_mmodules.add to_mmodule(model, smod)
478 end
479 mmodule.set_imported_mmodules(imported_mmodules)
480 return mmodule
481 end
482
483 # Build a `NeoNode` representing `mclass`.
484 private fun mclass_node(mclass: MClass): NeoNode do
485 var node = make_node(mclass)
486 node.labels.add "MClass"
487 node["full_name"] = mclass.full_name
488 node["kind"] = mclass.kind.to_s
489 node["visibility"] = mclass.visibility.to_s
490 if not mclass.mparameters.is_empty then
491 var parameter_names = new Array[String]
492 for p in mclass.mparameters do parameter_names.add(p.name)
493 node["parameter_names"] = new JsonArray.from(parameter_names)
494 end
495 node.out_edges.add(new NeoEdge(node, "CLASSTYPE", to_node(mclass.mclass_type)))
496 return node
497 end
498
499 # Build a new `MClass` from a `node`.
500 #
501 # REQUIRE `node.labels.has("MClass")`
502 private fun to_mclass(model: Model, node: NeoNode): MClass do
503 if mentities.has_key(node) then return mentities[node].as(MClass)
504 assert node.labels.has("MClass")
505 var mmodule = to_mmodule(model, node.in_nodes("INTRODUCES").first)
506 var name = node["name"].to_s
507 var kind = to_kind(node["kind"].to_s)
508 var visibility = to_visibility(node["visibility"].to_s)
509 var parameter_names = new Array[String]
510 if node.has_key("parameter_names") then
511 for e in node["parameter_names"].as(JsonArray) do
512 parameter_names.add e.to_s
513 end
514 end
515 var mclass = new MClass(mmodule, name, parameter_names, kind, visibility)
516 mentities[node] = mclass
517 set_doc(node, mclass)
518 return mclass
519 end
520
521 # Build a `NeoNode` representing `mclassdef`.
522 private fun mclassdef_node(mclassdef: MClassDef): NeoNode do
523 var node = make_node(mclassdef)
524 node.labels.add "MClassDef"
525 node["location"] = mclassdef.location.to_s
526 node.out_edges.add(new NeoEdge(node, "BOUNDTYPE", to_node(mclassdef.bound_mtype)))
527 node.out_edges.add(new NeoEdge(node, "MCLASS", to_node(mclassdef.mclass)))
528 for mproperty in mclassdef.intro_mproperties do
529 node.out_edges.add(new NeoEdge(node, "INTRODUCES", to_node(mproperty)))
530 end
531 for mpropdef in mclassdef.mpropdefs do
532 node.out_edges.add(new NeoEdge(node, "DECLARES", to_node(mpropdef)))
533 end
534 for sup in mclassdef.supertypes do
535 node.out_edges.add(new NeoEdge(node, "INHERITS", to_node(sup)))
536 end
537 return node
538 end
539
540 # Build a new `MClassDef` from a `node`.
541 #
542 # REQUIRE `node.labels.has("MClassDef")`
543 private fun to_mclassdef(model: Model, node: NeoNode): MClassDef do
544 if mentities.has_key(node) then return mentities[node].as(MClassDef)
545 assert node.labels.has("MClassDef")
546 var mmodule = to_mmodule(model, node.in_nodes("DEFINES").first)
547 var mtype = to_mtype(model, node.out_nodes("BOUNDTYPE").first).as(MClassType)
548 var location = to_location(node["location"].to_s)
549 var mclassdef = new MClassDef(mmodule, mtype, location)
550 mentities[node] = mclassdef
551 set_doc(node, mclassdef)
552 var supertypes = new Array[MClassType]
553 for sup in node.out_nodes("INHERITS") do
554 supertypes.add to_mtype(model, sup).as(MClassType)
555 end
556 mclassdef.set_supertypes(supertypes)
557 mclassdef.add_in_hierarchy
558 return mclassdef
559 end
560
561 # Build a `NeoNode` representing `mproperty`.
562 private fun mproperty_node(mproperty: MProperty): NeoNode do
563 var node = make_node(mproperty)
564 node.labels.add "MProperty"
565 node["full_name"] = mproperty.full_name
566 node["visibility"] = mproperty.visibility.to_s
567 if mproperty isa MMethod then
568 node.labels.add "MMethod"
569 node["is_init"] = mproperty.is_init
570 else if mproperty isa MAttribute then
571 node.labels.add "MAttribute"
572 else if mproperty isa MVirtualTypeProp then
573 node.labels.add "MVirtualTypeProp"
574 end
575 node.out_edges.add(new NeoEdge(node, "INTRO_CLASSDEF", to_node(mproperty.intro_mclassdef)))
576 return node
577 end
578
579 # Build a new `MProperty` from a `node`.
580 #
581 # REQUIRE `node.labels.has("MProperty")`
582 private fun to_mproperty(model: Model, node: NeoNode): MProperty do
583 if mentities.has_key(node) then return mentities[node].as(MProperty)
584 assert node.labels.has("MProperty")
585 var intro_mclassdef = to_mclassdef(model, node.out_nodes("INTRO_CLASSDEF").first)
586 var name = node["name"].to_s
587 var visibility = to_visibility(node["visibility"].to_s)
588 var mprop: nullable MProperty = null
589 if node.labels.has("MMethod") then
590 mprop = new MMethod(intro_mclassdef, name, visibility)
591 mprop.is_init = node["is_init"].as(Bool)
592 else if node.labels.has("MAttribute") then
593 mprop = new MAttribute(intro_mclassdef, name, visibility)
594 else if node.labels.has("MVirtualTypeProp") then
595 mprop = new MVirtualTypeProp(intro_mclassdef, name, visibility)
596 end
597 if mprop == null then
598 print "not yet implemented to_mproperty for {node.labels.join(",")}"
599 abort
600 end
601 mentities[node] = mprop
602 set_doc(node, mprop)
603 return mprop
604 end
605
606 # Build a `NeoNode` representing `mpropdef`.
607 private fun mpropdef_node(mpropdef: MPropDef): NeoNode do
608 var node = make_node(mpropdef)
609 node.labels.add "MPropDef"
610 node["location"] = mpropdef.location.to_s
611 node.out_edges.add(new NeoEdge(node, "DEFINES", to_node(mpropdef.mproperty)))
612 if mpropdef isa MMethodDef then
613 node.labels.add "MMethodDef"
614 node["is_abstract"] = mpropdef.is_abstract
615 node["is_intern"] = mpropdef.is_intern
616 node["is_extern"] = mpropdef.is_extern
617 var msignature = mpropdef.msignature
618 if msignature != null then
619 node.out_edges.add(new NeoEdge(node, "SIGNATURE", to_node(msignature)))
620 end
621 else if mpropdef isa MAttributeDef then
622 node.labels.add "MAttributeDef"
623 else if mpropdef isa MVirtualTypeDef then
624 node.labels.add "MVirtualTypeDef"
625 var bound = mpropdef.bound
626 if bound != null then
627 node.out_edges.add(new NeoEdge(node, "BOUND", to_node(bound)))
628 end
629 end
630 return node
631 end
632
633 # Build a new `MPropDef` from a `node`.
634 #
635 # REQUIRE `node.labels.has("MPropDef")`
636 private fun to_mpropdef(model: Model, node: NeoNode): MPropDef do
637 if mentities.has_key(node) then return mentities[node].as(MPropDef)
638 assert node.labels.has("MPropDef")
639 var mclassdef = to_mclassdef(model, node.in_nodes("DECLARES").first)
640 var mproperty = to_mproperty(model, node.out_nodes("DEFINES").first)
641 var location = to_location(node["location"].to_s)
642 var mpropdef: nullable MPropDef = null
643 if node.labels.has("MMethodDef") then
644 mpropdef = new MMethodDef(mclassdef, mproperty.as(MMethod), location)
645 mpropdef.is_abstract = node["is_abstract"].as(Bool)
646 mpropdef.is_intern = node["is_intern"].as(Bool)
647 mpropdef.is_extern = node["is_extern"].as(Bool)
648 mentities[node] = mpropdef
649 mpropdef.msignature = to_mtype(model, node.out_nodes("SIGNATURE").first).as(MSignature)
650 else if node.labels.has("MAttributeDef") then
651 mpropdef = new MAttributeDef(mclassdef, mproperty.as(MAttribute), location)
652 mentities[node] = mpropdef
653 else if node.labels.has("MVirtualTypeDef") then
654 mpropdef = new MVirtualTypeDef(mclassdef, mproperty.as(MVirtualTypeProp), location)
655 mentities[node] = mpropdef
656 var bound = node.out_nodes("BOUND")
657 if not bound.is_empty then mpropdef.bound = to_mtype(model, bound.first)
658 end
659 if mpropdef == null then
660 print "not yet implemented to_mpropdef for {node.labels.join(",")}"
661 abort
662 end
663 set_doc(node, mpropdef)
664 return mpropdef
665 end
666
667 # Build a `NeoNode` representing `mtype`.
668 private fun mtype_node(mtype: MType): NeoNode do
669 var node = make_node(mtype)
670 node.labels.add "MType"
671 if mtype isa MClassType then
672 node.labels.add "MClassType"
673 node.out_edges.add(new NeoEdge(node, "CLASS", to_node(mtype.mclass)))
674 for arg in mtype.arguments do
675 node.out_edges.add(new NeoEdge(node, "ARGUMENT", to_node(arg)))
676 end
677 if mtype isa MGenericType then
678 node.labels.add "MGenericType"
679 end
680 else if mtype isa MVirtualType then
681 node.labels.add "MVirtualType"
682 node.out_edges.add(new NeoEdge(node, "PROPERTY", to_node(mtype.mproperty)))
683 else if mtype isa MParameterType then
684 node.labels.add "MParameterType"
685 node["rank"] = mtype.rank
686 node.out_edges.add(new NeoEdge(node, "CLASS", to_node(mtype.mclass)))
687 else if mtype isa MNullableType then
688 node.labels.add "MNullableType"
689 node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mtype.mtype)))
690 else if mtype isa MSignature then
691 node.labels.add "MSignature"
692 var names = new JsonArray
693 var rank = 0
694 for mparameter in mtype.mparameters do
695 names.add mparameter.name
696 var pnode = to_node(mparameter)
697 pnode["rank"] = rank
698 node.out_edges.add(new NeoEdge(node, "PARAMETER", pnode))
699 end
700 if not names.is_empty then node["parameter_names"] = names
701 var return_mtype = mtype.return_mtype
702 if return_mtype != null then
703 node.out_edges.add(new NeoEdge(node, "RETURNTYPE", to_node(return_mtype)))
704 end
705 end
706 return node
707 end
708
709 # Build a new `MType` from a `node`.
710 #
711 # REQUIRE `node.labels.has("MType")`
712 private fun to_mtype(model: Model, node: NeoNode): MType do
713 if mentities.has_key(node) then return mentities[node].as(MType)
714 assert node.labels.has("MType")
715 if node.labels.has("MClassType") then
716 var mclass = to_mclass(model, node.out_nodes("CLASS").first)
717 var args = new Array[MType]
718 for narg in node.out_nodes("ARGUMENT") do
719 args.add to_mtype(model, narg)
720 end
721 var mtype = mclass.get_mtype(args)
722 mentities[node] = mtype
723 return mtype
724 else if node.labels.has("MParameterType") then
725 var mclass = to_mclass(model, node.out_nodes("CLASS").first)
726 var rank = node["rank"].to_s.to_i
727 var mtype = mclass.mparameters[rank]
728 mentities[node] = mtype
729 return mtype
730 else if node.labels.has("MNullableType") then
731 var intype = to_mtype(model, node.out_nodes("TYPE").first)
732 var mtype = intype.as_nullable
733 mentities[node] = mtype
734 return mtype
735 else if node.labels.has("MVirtualType") then
736 var mproperty = to_mproperty(model, node.out_nodes("PROPERTY").first)
737 assert mproperty isa MVirtualTypeProp
738 var mtype = mproperty.mvirtualtype
739 mentities[node] = mtype
740 return mtype
741 else if node.labels.has("MSignature") then
742 # Get all param nodes
743 var mparam_nodes = new HashMap[String, MParameter]
744 for pnode in node.out_nodes("PARAMETER") do
745 var mparam = to_mparameter(model, pnode)
746 mparam_nodes[mparam.name] = mparam
747 end
748 # Load params in the good order
749 var mparam_names = node["parameter_names"]
750 var mparameters = new Array[MParameter]
751 if mparam_names isa JsonArray then
752 for mparam_name in mparam_names do
753 var mparam = mparam_nodes[mparam_name.to_s]
754 mparameters.add mparam
755 end
756 end
757 var return_mtype: nullable MType = null
758 var ret_nodes = node.out_nodes("RETURNTYPE")
759 if not ret_nodes.is_empty then
760 return_mtype = to_mtype(model, ret_nodes.first)
761 end
762 var mtype = new MSignature(mparameters, return_mtype)
763 mentities[node] = mtype
764 return mtype
765 end
766 print "not yet implemented to_mtype for {node.labels.join(",")}"
767 abort
768 end
769
770 # Build a `NeoNode` representing `mparameter`.
771 private fun mparameter_node(mparameter: MParameter): NeoNode do
772 var node = make_node(mparameter)
773 node.labels.add "MParameter"
774 node["name"] = mparameter.name
775 node["is_vararg"] = mparameter.is_vararg
776 node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mparameter.mtype)))
777 return node
778 end
779
780 # Build a new `MParameter` from `node`.
781 #
782 # REQUIRE `node.labels.has("MParameter")`
783 private fun to_mparameter(model: Model, node: NeoNode): MParameter do
784 if mentities.has_key(node) then return mentities[node].as(MParameter)
785 assert node.labels.has("MParameter")
786 var name = node["name"].to_s
787 var mtype = to_mtype(model, node.out_nodes("TYPE").first)
788 var is_vararg = node["is_vararg"].as(Bool)
789 var mparameter = new MParameter(name, mtype, is_vararg)
790 mentities[node] = mparameter
791 return mparameter
792 end
793
794 # Get a `Location` from its string representation.
795 private fun to_location(loc: String): Location do
796 #TODO filepath
797 var parts = loc.split_with(":")
798 var file = new SourceFile.from_string(parts[0], "")
799 var pos = parts[1].split_with("--")
800 var pos1 = pos[0].split_with(",")
801 var pos2 = pos[1].split_with(",")
802 var line_s = pos1[0].to_i
803 var line_e = pos2[0].to_i
804 var column_s = pos1[1].to_i
805 var column_e = 0
806 if pos2.length == 2 then pos2[1].to_i
807 return new Location(file, line_s, line_e, column_s, column_e)
808 end
809
810 # Get a `MVisibility` from its string representation.
811 private fun to_visibility(vis: String): MVisibility do
812 if vis == intrude_visibility.to_s then
813 return intrude_visibility
814 else if vis == public_visibility.to_s then
815 return public_visibility
816 else if vis == protected_visibility.to_s then
817 return protected_visibility
818 else if vis == private_visibility.to_s then
819 return private_visibility
820 else
821 return none_visibility
822 end
823 end
824
825 # Get a `MKind` from its string representation.
826 private fun to_kind(kind: String): MClassKind do
827 if kind == abstract_kind.to_s then
828 return abstract_kind
829 else if kind == concrete_kind.to_s then
830 return concrete_kind
831 else if kind == interface_kind.to_s then
832 return interface_kind
833 else if kind == enum_kind.to_s then
834 return enum_kind
835 else if kind == extern_kind.to_s then
836 return extern_kind
837 end
838 abort
839 end
840
841 # Extract the `MDoc` from `node` and link it to `mentity`.
842 private fun set_doc(node: NeoNode, mentity: MEntity) do
843 if node.has_key("mdoc") then
844 var lines = new Array[String]
845 for e in node["mdoc"].as(JsonArray) do
846 lines.add e.to_s#.replace("\n", "\\n")
847 end
848 var mdoc = new MDoc
849 mdoc.content.add_all(lines)
850 mdoc.original_mentity = mentity
851 mentity.mdoc = mdoc
852 end
853 end
854 end