src: Introduces `neo` a module for save and load Nit models intro a Neo4j graph database.
[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 into Neo4j database.
19 #
20 # see `Neo4jClient`.
21 #
22 # NeoNodes can also be translated back to MEntities to rebuild a Nit `Model`.
23 #
24 # Structure of the nit `Model` in base:
25 #
26 # `MProject`
27 #
28 # * labels: `model_name`, `MEntity`, `MProject`
29 # * `(:MProject)-[:ROOT]->(:MGroup)`
30 #
31 # `MGroup`
32 #
33 # * labels: `model_name`, `MEntity`, `MGroup`
34 # * `(:MGroup)-[:PROJECT]->(:MProject)`
35 # * `(:MGroup)-[:PARENT]->(:MGroup)`
36 #
37 # `MModule`
38 #
39 # * labels: `model_name`, `MEntity`, `MModule`
40 # * `(:MModule)-[:IMPORTS]->(:MModule)`
41 # * `(:MModule)-[:INTRODUCES]->(:MClass)`
42 # * `(:MModule)-[:DEFINES]->(:MClassDef)`
43 #
44 # `MClass`
45 #
46 # * labels: `model_name`, `MEntity`, `MClass`
47 # * `(:MClass)-[:CLASSTYPE]->(:MClassType)`
48 #
49 # `MClassDef`
50 #
51 # * labels: `model_name`, `MEntity`, `MClassDef`
52 # * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)`
53 # * `(:MClassDef)-[:MCLASS]->(:MClass)`
54 # * `(:MClassDef)-[:INTRODUCES]->(:MProperty)`
55 # * `(:MClassDef)-[:DECLARES]->(:MPropDef)`
56 #
57 # `MProperty`
58 #
59 # * labels: `model_name`, `MEntity`, `MProperty`
60 # * `(:MProperty)-[:INTRO_CLASSDEF]->(:MClassDef)`
61 #
62 # MProperties can also have labels `MMethod`, `MAttribute`, `MVirtualTypeProp`.
63 #
64 # `MPropDef`
65 #
66 # * labels: `model_name`, `MEntity`, `MPropDef`
67 # * `(:MPropDef)-[:DEFINES]->(:MProperty)`
68 #
69 # MPropdefs can also have labels `MMethodDef`, `MAttributeDef`, `MVirtualTypeDef`.
70 #
71 # `MMethodDef` are linked to a `MSignature`:
72 #
73 # * `(:MMethodDef)-[:SIGNATURE]->(:MSignature)`
74 #
75 # `MVirtualTypeDef` are linked to a `MType` (its `bound`):
76 #
77 # * `(:MVirtualTypeDef)-[:BOUND]->(:MType)`
78 #
79 # `MType`
80 #
81 # * labels: `model_name`, `MEntity`, `MType`
82 #
83 # MTypes can also have labels `MClassType`, `MGenericType`, `MNullableType`, `MVirtualType`
84 # and `MSignature`.
85 #
86 # `MClassType` and `MGenericType` both point to a `MClass` and have type `arguments`:
87 #
88 # * `(:MClassType)-[:CLASS]->(:MClass)`
89 # * `(:MClassType)-[:ARGUMENT]->(:MType)`
90 #
91 # `MVirtualType` points to its introducing `MProperty`:
92 #
93 # * `(:MVirtualType)-[:PROPERTY]->(:MVirtualTypeDef)`
94 #
95 # `MParameterType` points to its introducing `MClass`:
96 #
97 # * `(:MParameterType)-[:CLASS]->(:MClass)`
98 #
99 # `MNullableType` points to its non-nullable `MType`:
100 #
101 # * `(:MNullableType)-[:TYPE]->(:MType)`
102 #
103 # `MSignature` can have `parameters` and a `return_mtype`:
104 #
105 # * `(:MSignature)-[:PARAMETER]->(:MParameter)`
106 # * `(:MSignature)-[:RETURNTYPE]->(:MType)`
107 #
108 # `MParameter`
109 #
110 # * labels: `model_name`, `MEntity`, `MParameter`
111 # * `(:MParameter)-[:TYPE]->(:MType)`
112 module neo
113
114 import model
115 import neo4j
116 import toolcontext
117
118 # Helper class that can save and load a `Model` into a Neo4j database.
119 class NeoModel
120
121 # The model name.
122 #
123 # Because we use only one Neo4j instance to store all the models,
124 # we need to mark their appartenance to a particular model and avoid loading all models.
125 #
126 # The name is used as a Neo label on each created nodes and used to load nodes from base.
127 var model_name: String
128
129 # The toolcontext used to init the `NeoModel` tool.
130 var toolcontext: ToolContext
131
132 # The Neo4j `client` used to communicate with the Neo4j instance.
133 var client: Neo4jClient
134
135 # Fill `model` using base pointed by `client`.
136 fun load(model: Model): Model do
137 toolcontext.info("Locate all mentities...", 1)
138 var nodes = client.nodes_with_label(model_name)
139
140 toolcontext.info("Preload nodes...", 1)
141 pull_all_nodes(nodes)
142 toolcontext.info("Preload edges...", 1)
143 pull_all_edges(nodes)
144
145 toolcontext.info("Build model...", 1)
146 nodes = client.nodes_with_labels([model_name, "MProject"])
147 for node in nodes do to_mproject(model, node)
148 nodes = client.nodes_with_labels([model_name, "MGroup"])
149 for node in nodes do to_mgroup(model, node)
150 nodes = client.nodes_with_labels([model_name, "MModule"])
151 for node in nodes do to_mmodule(model, node)
152 nodes = client.nodes_with_labels([model_name, "MClass"])
153 for node in nodes do to_mclass(model, node)
154 nodes = client.nodes_with_labels([model_name, "MClassDef"])
155 for node in nodes do to_mclassdef(model, node)
156 nodes = client.nodes_with_labels([model_name, "MProperty"])
157 for node in nodes do to_mproperty(model, node)
158 nodes = client.nodes_with_labels([model_name, "MPropDef"])
159 for node in nodes do to_mpropdef(model, node)
160 return model
161 end
162
163 # Save `model` in the base pointed by `client`.
164 fun save(model: Model) do
165 var nodes = collect_model_nodes(model)
166 toolcontext.info("Save {nodes.length} nodes...", 1)
167 push_all(nodes)
168 var edges = collect_model_edges(model)
169 toolcontext.info("Save {edges.length} edges...", 1)
170 push_all(edges)
171 end
172
173 # Save `neo_entities` in base using batch mode.
174 private fun push_all(neo_entities: Collection[NeoEntity]) do
175 var batch = new NeoBatch(client)
176 var len = neo_entities.length
177 var sum = 0
178 var i = 1
179 for nentity in neo_entities do
180 batch.save_entity(nentity)
181 if i == batch_max_size then
182 do_batch(batch)
183 sum += batch_max_size
184 toolcontext.info(" {sum * 100 / len}% done", 1)
185 batch = new NeoBatch(client)
186 i = 1
187 else
188 i += 1
189 end
190 end
191 do_batch(batch)
192 end
193
194 # Load content for all `nodes` from base.
195 #
196 # Content corresponds to properties and labels that are loaded in batch mode.
197 private fun pull_all_nodes(nodes: Collection[NeoNode]) do
198 var batch = new NeoBatch(client)
199 var len = nodes.length
200 var sum = 0
201 var i = 1
202 for node in nodes do
203 batch.load_node(node)
204 if i == batch_max_size then
205 do_batch(batch)
206 sum += batch_max_size
207 toolcontext.info(" {sum * 100 / len}% done", 1)
208 batch = new NeoBatch(client)
209 i = 1
210 else
211 i += 1
212 end
213 end
214 do_batch(batch)
215 end
216
217 # Load all edges from base linked to `nodes`.
218 #
219 # Edges are loaded in batch mode.
220 private fun pull_all_edges(nodes: Collection[NeoNode]) do
221 var batch = new NeoBatch(client)
222 var len = nodes.length
223 var sum = 0
224 var i = 1
225 for node in nodes do
226 batch.load_node_edges(node)
227 if i == batch_max_size then
228 do_batch(batch)
229 sum += batch_max_size
230 toolcontext.info(" {sum * 100 / len}% done", 1)
231 batch = new NeoBatch(client)
232 i = 1
233 else
234 i += 1
235 end
236 end
237 do_batch(batch)
238 end
239
240 # How many operation can be executed in one batch?
241 private var batch_max_size = 1000
242
243 # Execute `batch` and check for errors.
244 #
245 # Abort if `batch.execute` returns errors.
246 private fun do_batch(batch: NeoBatch) do
247 var errors = batch.execute
248 if not errors.is_empty then
249 print errors
250 exit(1)
251 end
252 end
253
254 # Collect all nodes from the current `model`.
255 private fun collect_model_nodes(model: Model): Collection[NeoNode] do
256 for mproject in model.mprojects do
257 to_node(mproject)
258 for mgroup in mproject.mgroups do to_node(mgroup)
259 end
260 return nodes.values
261 end
262
263 # Collect all edges from the current `model`.
264 #
265 # Actually collect all out_edges from all nodes.
266 private fun collect_model_edges(model: Model): Collection[NeoEdge] do
267 var edges = new HashSet[NeoEdge]
268 for node in nodes.values do edges.add_all(node.out_edges)
269 return edges
270 end
271
272 # Mentities associated to nodes.
273 private var mentities = new HashMap[NeoNode, MEntity]
274
275 # Nodes associated with MEntities.
276 private var nodes = new HashMap[MEntity, NeoNode]
277
278 # Get the `NeoNode` associated with `mentity`.
279 # `mentities` are stored locally to avoid duplication.
280 fun to_node(mentity: MEntity): NeoNode do
281 if nodes.has_key(mentity) then return nodes[mentity]
282 if mentity isa MProject then return mproject_node(mentity)
283 if mentity isa MGroup then return mgroup_node(mentity)
284 if mentity isa MModule then return mmodule_node(mentity)
285 if mentity isa MClass then return mclass_node(mentity)
286 if mentity isa MClassDef then return mclassdef_node(mentity)
287 if mentity isa MProperty then return mproperty_node(mentity)
288 if mentity isa MPropDef then return mpropdef_node(mentity)
289 if mentity isa MType then return mtype_node(mentity)
290 if mentity isa MParameter then return mparameter_node(mentity)
291 abort
292 end
293
294 # Make a new `NeoNode` based on `mentity`.
295 private fun make_node(mentity: MEntity): NeoNode do
296 var node = new NeoNode
297 nodes[mentity] = node
298 node.labels.add "MEntity"
299 node.labels.add model_name
300 node["name"] = mentity.name
301 if mentity.mdoc != null then node["mdoc"] = new JsonArray.from(mentity.mdoc.content)
302 return node
303 end
304
305 # Build a `NeoNode` representing `mproject`.
306 private fun mproject_node(mproject: MProject): NeoNode do
307 var node = make_node(mproject)
308 node.labels.add "MProject"
309 var root = mproject.root
310 if root != null then
311 node.out_edges.add(new NeoEdge(node, "ROOT", to_node(root)))
312 end
313 return node
314 end
315
316 # Build a new `MProject` from a `node`.
317 #
318 # REQUIRE `node.labels.has("MProject")`
319 private fun to_mproject(model: Model, node: NeoNode): MProject do
320 if mentities.has_key(node) then return mentities[node].as(MProject)
321 assert node.labels.has("MProject")
322 var mproject = new MProject(node["name"].to_s, model)
323 mentities[node] = mproject
324 set_doc(node, mproject)
325 mproject.root = to_mgroup(model, node.out_nodes("ROOT").first)
326 return mproject
327 end
328
329 # Build a `NeoNode` representing `mgroup`.
330 private fun mgroup_node(mgroup: MGroup): NeoNode do
331 var node = make_node(mgroup)
332 node.labels.add "MGroup"
333 node["full_name"] = mgroup.full_name
334 var parent = mgroup.parent
335 node.out_edges.add(new NeoEdge(node, "PROJECT", to_node(mgroup.mproject)))
336 if parent != null then
337 node.out_edges.add(new NeoEdge(node, "PARENT", to_node(parent)))
338 end
339 for mmodule in mgroup.mmodules do
340 node.out_edges.add(new NeoEdge(node, "DECLARES", to_node(mmodule)))
341 end
342 for subgroup in mgroup.in_nesting.direct_smallers do
343 node.in_edges.add(new NeoEdge(node, "NESTS", to_node(subgroup)))
344 end
345 return node
346 end
347
348 # Build a new `MGroup` from a `node`.
349 #
350 # REQUIRE `node.labels.has("MGroup")`
351 private fun to_mgroup(model: Model, node: NeoNode): MGroup do
352 if mentities.has_key(node) then return mentities[node].as(MGroup)
353 assert node.labels.has("MGroup")
354 var mproject = to_mproject(model, node.out_nodes("PROJECT").first)
355 var parent: nullable MGroup = null
356 var out = node.out_nodes("PARENT")
357 if not out.is_empty then
358 parent = to_mgroup(model, out.first)
359 end
360 var mgroup = new MGroup(node["name"].to_s, mproject, parent)
361 mentities[node] = mgroup
362 set_doc(node, mgroup)
363 return mgroup
364 end
365
366 # Build a `NeoNode` representing `mmodule`.
367 private fun mmodule_node(mmodule: MModule): NeoNode do
368 var node = make_node(mmodule)
369 node.labels.add "MModule"
370 node["full_name"] = mmodule.full_name
371 node["location"] = mmodule.location.to_s
372 var mgroup = mmodule.mgroup
373 for parent in mmodule.in_importation.direct_greaters do
374 node.out_edges.add(new NeoEdge(node, "IMPORTS", to_node(parent)))
375 end
376 for mclass in mmodule.intro_mclasses do
377 node.out_edges.add(new NeoEdge(node, "INTRODUCES", to_node(mclass)))
378 end
379 for mclassdef in mmodule.mclassdefs do
380 node.out_edges.add(new NeoEdge(node, "DEFINES", to_node(mclassdef)))
381 end
382 return node
383 end
384
385 # Build a new `MModule` from a `node`.
386 #
387 # REQUIRE `node.labels.has("MModule")`
388 private fun to_mmodule(model: Model, node: NeoNode): MModule do
389 if mentities.has_key(node) then return mentities[node].as(MModule)
390 assert node.labels.has("MModule")
391 var ins = node.in_nodes("DECLARES")
392 var mgroup: nullable MGroup = null
393 if not ins.is_empty then
394 mgroup = to_mgroup(model, ins.first)
395 end
396 var name = node["name"].to_s
397 var location = to_location(node["location"].to_s)
398 var mmodule = new MModule(model, mgroup, name, location)
399 mentities[node] = mmodule
400 set_doc(node, mmodule)
401 var imported_mmodules = new Array[MModule]
402 for smod in node.out_nodes("IMPORTS") do
403 imported_mmodules.add to_mmodule(model, smod)
404 end
405 mmodule.set_imported_mmodules(imported_mmodules)
406 return mmodule
407 end
408
409 # Build a `NeoNode` representing `mclass`.
410 private fun mclass_node(mclass: MClass): NeoNode do
411 var node = make_node(mclass)
412 node.labels.add "MClass"
413 node["arity"] = mclass.arity
414 node["full_name"] = mclass.full_name
415 node["kind"] = mclass.kind.to_s
416 node["visibility"] = mclass.visibility.to_s
417 node.out_edges.add(new NeoEdge(node, "CLASSTYPE", to_node(mclass.mclass_type)))
418 return node
419 end
420
421 # Build a new `MClass` from a `node`.
422 #
423 # REQUIRE `node.labels.has("MClass")`
424 private fun to_mclass(model: Model, node: NeoNode): MClass do
425 if mentities.has_key(node) then return mentities[node].as(MClass)
426 assert node.labels.has("MClass")
427 var mmodule = to_mmodule(model, node.in_nodes("INTRODUCES").first)
428 var name = node["name"].to_s
429 var arity = node["arity"].to_s.to_i
430 var kind = to_kind(node["kind"].to_s)
431 var visibility = to_visibility(node["visibility"].to_s)
432 var mclass = new MClass(mmodule, name, arity, kind, visibility)
433 mentities[node] = mclass
434 set_doc(node, mclass)
435 return mclass
436 end
437
438 # Build a `NeoNode` representing `mclassdef`.
439 private fun mclassdef_node(mclassdef: MClassDef): NeoNode do
440 var node = make_node(mclassdef)
441 node.labels.add "MClassDef"
442 node["is_intro"] = mclassdef.is_intro
443 node["location"] = mclassdef.location.to_s
444 if not mclassdef.parameter_names.is_empty then
445 node["parameter_names"] = new JsonArray.from(mclassdef.parameter_names)
446 end
447 node.out_edges.add(new NeoEdge(node, "BOUNDTYPE", to_node(mclassdef.bound_mtype)))
448 node.out_edges.add(new NeoEdge(node, "MCLASS", to_node(mclassdef.mclass)))
449 for mproperty in mclassdef.intro_mproperties do
450 node.out_edges.add(new NeoEdge(node, "INTRODUCES", to_node(mproperty)))
451 end
452 for mpropdef in mclassdef.mpropdefs do
453 node.out_edges.add(new NeoEdge(node, "DECLARES", to_node(mpropdef)))
454 end
455 for sup in mclassdef.supertypes do
456 node.out_edges.add(new NeoEdge(node, "INHERITS", to_node(sup)))
457 end
458 return node
459 end
460
461 # Build a new `MClassDef` from a `node`.
462 #
463 # REQUIRE `node.labels.has("MClassDef")`
464 private fun to_mclassdef(model: Model, node: NeoNode): MClassDef do
465 if mentities.has_key(node) then return mentities[node].as(MClassDef)
466 assert node.labels.has("MClassDef")
467 var mmodule = to_mmodule(model, node.in_nodes("DEFINES").first)
468 var mtype = to_mtype(model, node.out_nodes("BOUNDTYPE").first).as(MClassType)
469 var location = to_location(node["location"].to_s)
470 var parameter_names = new Array[String]
471 if node.has_key("parameter_names") then
472 for e in node["parameter_names"].as(JsonArray) do
473 parameter_names.add e.to_s
474 end
475 end
476 var mclassdef = new MClassDef(mmodule, mtype, location, parameter_names)
477 mentities[node] = mclassdef
478 set_doc(node, mclassdef)
479 var supertypes = new Array[MClassType]
480 for sup in node.out_nodes("INHERITS") do
481 supertypes.add to_mtype(model, sup).as(MClassType)
482 end
483 mclassdef.set_supertypes(supertypes)
484 mclassdef.add_in_hierarchy
485 return mclassdef
486 end
487
488 # Build a `NeoNode` representing `mproperty`.
489 private fun mproperty_node(mproperty: MProperty): NeoNode do
490 var node = make_node(mproperty)
491 node.labels.add "MProperty"
492 node["full_name"] = mproperty.full_name
493 node["visibility"] = mproperty.visibility.to_s
494 if mproperty isa MMethod then
495 node.labels.add "MMethod"
496 node["is_init"] = mproperty.is_init
497 else if mproperty isa MAttribute then
498 node.labels.add "MAttribute"
499 else if mproperty isa MVirtualTypeProp then
500 node.labels.add "MVirtualTypeProp"
501 end
502 node.out_edges.add(new NeoEdge(node, "INTRO_CLASSDEF", to_node(mproperty.intro_mclassdef)))
503 return node
504 end
505
506 # Build a new `MProperty` from a `node`.
507 #
508 # REQUIRE `node.labels.has("MProperty")`
509 private fun to_mproperty(model: Model, node: NeoNode): MProperty do
510 if mentities.has_key(node) then return mentities[node].as(MProperty)
511 assert node.labels.has("MProperty")
512 var intro_mclassdef = to_mclassdef(model, node.out_nodes("INTRO_CLASSDEF").first)
513 var name = node["name"].to_s
514 var visibility = to_visibility(node["visibility"].to_s)
515 var mprop: nullable MProperty = null
516 if node.labels.has("MMethod") then
517 mprop = new MMethod(intro_mclassdef, name, visibility)
518 mprop.is_init = node["is_init"].as(Bool)
519 else if node.labels.has("MAttribute") then
520 mprop = new MAttribute(intro_mclassdef, name, visibility)
521 else if node.labels.has("MVirtualTypeProp") then
522 mprop = new MVirtualTypeProp(intro_mclassdef, name, visibility)
523 end
524 if mprop == null then
525 print "not yet implemented to_mproperty for {node.labels.join(",")}"
526 abort
527 end
528 mentities[node] = mprop
529 set_doc(node, mprop)
530 for npropdef in node.in_nodes("DEFINES") do
531 var mpropdef = to_mpropdef(model, npropdef)
532 if npropdef["is_intro"].as(Bool) then
533 mprop.mpropdefs.unshift mpropdef
534 else
535 mprop.mpropdefs.add mpropdef
536 end
537 end
538 return mprop
539 end
540
541 # Build a `NeoNode` representing `mpropdef`.
542 private fun mpropdef_node(mpropdef: MPropDef): NeoNode do
543 var node = make_node(mpropdef)
544 node.labels.add "MPropDef"
545 node["is_intro"] = mpropdef.is_intro
546 node["location"] = mpropdef.location.to_s
547 node.out_edges.add(new NeoEdge(node, "DEFINES", to_node(mpropdef.mproperty)))
548 if mpropdef isa MMethodDef then
549 node.labels.add "MMethodDef"
550 node["is_abstract"] = mpropdef.is_abstract
551 node["is_intern"] = mpropdef.is_intern
552 node["is_extern"] = mpropdef.is_extern
553 var msignature = mpropdef.msignature
554 if msignature != null then
555 node.out_edges.add(new NeoEdge(node, "SIGNATURE", to_node(msignature)))
556 end
557 else if mpropdef isa MAttributeDef then
558 node.labels.add "MAttributeDef"
559 else if mpropdef isa MVirtualTypeDef then
560 node.labels.add "MVirtualTypeDef"
561 var bound = mpropdef.bound
562 if bound != null then
563 node.out_edges.add(new NeoEdge(node, "BOUND", to_node(bound)))
564 end
565 end
566 return node
567 end
568
569 # Build a new `MPropDef` from a `node`.
570 #
571 # REQUIRE `node.labels.has("MPropDef")`
572 private fun to_mpropdef(model: Model, node: NeoNode): MPropDef do
573 if mentities.has_key(node) then return mentities[node].as(MPropDef)
574 assert node.labels.has("MPropDef")
575 var mclassdef = to_mclassdef(model, node.in_nodes("DECLARES").first)
576 var mproperty = to_mproperty(model, node.out_nodes("DEFINES").first)
577 var location = to_location(node["location"].to_s)
578 var mpropdef: nullable MPropDef = null
579 if node.labels.has("MMethodDef") then
580 mpropdef = new MMethodDef(mclassdef, mproperty.as(MMethod), location)
581 mpropdef.is_abstract = node["is_abstract"].as(Bool)
582 mpropdef.is_intern = node["is_intern"].as(Bool)
583 mpropdef.is_extern = node["is_extern"].as(Bool)
584 mentities[node] = mpropdef
585 mpropdef.msignature = to_mtype(model, node.out_nodes("SIGNATURE").first).as(MSignature)
586 else if node.labels.has("MAttributeDef") then
587 mpropdef = new MAttributeDef(mclassdef, mproperty.as(MAttribute), location)
588 mentities[node] = mpropdef
589 else if node.labels.has("MVirtualTypeDef") then
590 mpropdef = new MVirtualTypeDef(mclassdef, mproperty.as(MVirtualTypeProp), location)
591 mentities[node] = mpropdef
592 var bound = node.out_nodes("BOUND")
593 if not bound.is_empty then mpropdef.bound = to_mtype(model, bound.first)
594 end
595 if mpropdef == null then
596 print "not yet implemented to_mpropdef for {node.labels.join(",")}"
597 abort
598 end
599 set_doc(node, mpropdef)
600 return mpropdef
601 end
602
603 # Build a `NeoNode` representing `mtype`.
604 private fun mtype_node(mtype: MType): NeoNode do
605 var node = make_node(mtype)
606 node.labels.add "MType"
607 if mtype isa MClassType then
608 node.labels.add "MClassType"
609 node.out_edges.add(new NeoEdge(node, "CLASS", to_node(mtype.mclass)))
610 for arg in mtype.arguments do
611 node.out_edges.add(new NeoEdge(node, "ARGUMENT", to_node(arg)))
612 end
613 if mtype isa MGenericType then
614 node.labels.add "MGenericType"
615 end
616 else if mtype isa MVirtualType then
617 node.labels.add "MVirtualType"
618 node.out_edges.add(new NeoEdge(node, "PROPERTY", to_node(mtype.mproperty)))
619 else if mtype isa MParameterType then
620 node.labels.add "MParameterType"
621 node["rank"] = mtype.rank
622 node.out_edges.add(new NeoEdge(node, "CLASS", to_node(mtype.mclass)))
623 else if mtype isa MNullableType then
624 node.labels.add "MNullableType"
625 node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mtype.mtype)))
626 else if mtype isa MSignature then
627 node.labels.add "MSignature"
628 for mparameter in mtype.mparameters do
629 node.out_edges.add(new NeoEdge(node, "PARAMETER", to_node(mparameter)))
630 end
631 var return_mtype = mtype.return_mtype
632 if return_mtype != null then
633 node.out_edges.add(new NeoEdge(node, "RETURNTYPE", to_node(return_mtype)))
634 end
635 end
636 return node
637 end
638
639 # Build a new `MType` from a `node`.
640 #
641 # REQUIRE `node.labels.has("MType")`
642 private fun to_mtype(model: Model, node: NeoNode): MType do
643 if mentities.has_key(node) then return mentities[node].as(MType)
644 assert node.labels.has("MType")
645 if node.labels.has("MClassType") then
646 var mclass = to_mclass(model, node.out_nodes("CLASS").first)
647 var args = new Array[MType]
648 for narg in node.out_nodes("ARGUMENT") do
649 args.add to_mtype(model, narg)
650 end
651 var mtype = mclass.get_mtype(args)
652 mentities[node] = mtype
653 return mtype
654 else if node.labels.has("MParameterType") then
655 var mclass = to_mclass(model, node.out_nodes("CLASS").first)
656 var rank = node["rank"].to_s.to_i
657 var mtype = new MParameterType(mclass, rank)
658 mentities[node] = mtype
659 return mtype
660 else if node.labels.has("MNullableType") then
661 var intype = to_mtype(model, node.out_nodes("TYPE").first)
662 var mtype = new MNullableType(intype)
663 mentities[node] = mtype
664 return mtype
665 else if node.labels.has("MVirtualType") then
666 var mproperty = to_mproperty(model, node.out_nodes("PROPERTY").first)
667 var mtype = new MVirtualType(mproperty)
668 mentities[node] = mtype
669 return mtype
670 else if node.labels.has("MSignature") then
671 var mparameters = new Array[MParameter]
672 for pnode in node.out_nodes("PARAMETER") do
673 mparameters.add to_mparameter(model, pnode)
674 end
675 var return_mtype: nullable MType = null
676 var ret_nodes = node.out_nodes("RETURNTYPE")
677 if not ret_nodes.is_empty then
678 return_mtype = to_mtype(model, ret_nodes.first)
679 end
680 var mtype = new MSignature(mparameters, return_mtype)
681 mentities[node] = mtype
682 return mtype
683 end
684 print "not yet implemented to_mtype for {node.labels.join(",")}"
685 abort
686 end
687
688 # Build a `NeoNode` representing `mparameter`.
689 private fun mparameter_node(mparameter: MParameter): NeoNode do
690 var node = make_node(mparameter)
691 node.labels.add "MParameter"
692 node["name"] = mparameter.name
693 node["is_vararg"] = mparameter.is_vararg
694 node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mparameter.mtype)))
695 return node
696 end
697
698 # Build a new `MParameter` from `node`.
699 #
700 # REQUIRE `node.labels.has("MParameter")`
701 private fun to_mparameter(model: Model, node: NeoNode): MParameter do
702 if mentities.has_key(node) then return mentities[node].as(MParameter)
703 assert node.labels.has("MParameter")
704 var name = node["name"].to_s
705 var mtype = to_mtype(model, node.out_nodes("TYPE").first)
706 var is_vararg = node["is_vararg"].as(Bool)
707 var mparameter = new MParameter(name, mtype, is_vararg)
708 mentities[node] = mparameter
709 return mparameter
710 end
711
712 # Get a `Location` from its string representation.
713 private fun to_location(loc: String): Location do
714 #TODO filepath
715 var parts = loc.split_with(":")
716 var file = new SourceFile.from_string(parts[0], "")
717 var pos = parts[1].split_with("--")
718 var pos1 = pos[0].split_with(",")
719 var pos2 = pos[1].split_with(",")
720 var line_s = pos1[0].to_i
721 var line_e = pos2[0].to_i
722 var column_s = pos1[1].to_i
723 var column_e = 0
724 if pos2.length == 2 then pos2[1].to_i
725 return new Location(file, line_s, line_e, column_s, column_e)
726 end
727
728 # Get a `MVisibility` from its string representation.
729 private fun to_visibility(vis: String): MVisibility do
730 if vis == intrude_visibility.to_s then
731 return intrude_visibility
732 else if vis == public_visibility.to_s then
733 return public_visibility
734 else if vis == protected_visibility.to_s then
735 return protected_visibility
736 else if vis == private_visibility.to_s then
737 return private_visibility
738 else
739 return none_visibility
740 end
741 end
742
743 # Get a `MKind` from its string representation.
744 private fun to_kind(kind: String): MClassKind do
745 if kind == abstract_kind.to_s then
746 return abstract_kind
747 else if kind == concrete_kind.to_s then
748 return concrete_kind
749 else if kind == interface_kind.to_s then
750 return interface_kind
751 else if kind == enum_kind.to_s then
752 return enum_kind
753 else if kind == extern_kind.to_s then
754 return extern_kind
755 end
756 abort
757 end
758
759 # Extract the `MDoc` from `node` and link it to `mentity`.
760 private fun set_doc(node: NeoNode, mentity: MEntity) do
761 if node.has_key("mdoc") then
762 var lines = new Array[String]
763 for e in node["mdoc"].as(JsonArray) do
764 lines.add e.to_s#.replace("\n", "\\n")
765 end
766 var mdoc = new MDoc
767 mdoc.content.add_all(lines)
768 mdoc.original_mentity = mentity
769 mentity.mdoc = mdoc
770 end
771 end
772 end