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