X-Git-Url: http://nitlanguage.org diff --git a/src/neo.nit b/src/neo.nit index f7ab770..f9d3c9a 100644 --- a/src/neo.nit +++ b/src/neo.nit @@ -12,103 +12,184 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Save and load `Model` from/to Neo4j base. +# Save and load a `Model` to/from a Neo4j graph. # # Nit models are composed by MEntities. -# This module creates NeoNode for each MEntity found in a `Model` and save them into Neo4j database. +# This module creates NeoNode for each MEntity found in a `Model` and save them +# into Neo4j database. # -# see `Neo4jClient`. +# SEE: `Neo4jClient` # # NeoNodes can also be translated back to MEntities to rebuild a Nit `Model`. # -# Structure of the nit `Model` in base: +# Structure of the nit `Model` in the graph: +# +# Note : Any null or empty attribute will not be saved in the database. +# +# For any `MEntity` (in addition to specific data): +# +# * labels: model name (`model_name`) and `MEntity`. +# * `name`: short (unqualified) name. +# * `mdoc`: JSON array representing the associated Markdown documentation +# (one item by line). +# +# Note : All nodes described here are MEntities. # # `MProject` # -# * labels: `model_name`, `MEntity`, `MProject` -# * `(:MProject)-[:ROOT]->(:MGroup)` +# * labels: `MProject`, `model_name` and `MEntity`. +# * `(:MProject)-[:ROOT]->(:MGroup)`: root of the group tree. # # `MGroup` # -# * labels: `model_name`, `MEntity`, `MGroup` -# * `(:MGroup)-[:PROJECT]->(:MProject)` -# * `(:MGroup)-[:PARENT]->(:MGroup)` +# * labels: `MGroup`, `model_name` and `MEntity`. +# * `full_name`: fully qualified name. +# * `(:MGroup)-[:PROJECT]->(:MProject)`: associated project. +# * `(:MGroup)-[:PARENT]->(:MGroup)`: parent group. Does not exist for the root +# group. +# * `(:MGroup)-[:DECLARES]->(:MModule)`: modules that are direct children of +# this group. +# * `(:MGroup)-[:NESTS]->(:MGroup)`: nested groups that are direct children of +# this group. # # `MModule` # -# * labels: `model_name`, `MEntity`, `MModule` -# * `(:MModule)-[:IMPORTS]->(:MModule)` -# * `(:MModule)-[:INTRODUCES]->(:MClass)` -# * `(:MModule)-[:DEFINES]->(:MClassDef)` +# * labels: `MModule`, `model_name` and `MEntity`. +# * `full_name`: fully qualified name. +# * `location`: origin of the definition. SEE: `Location.to_s` +# * `(:MModule)-[:IMPORTS]->(:MModule)`: modules that are imported directly. +# * `(:MModule)-[:INTRODUCES]->(:MClass)`: all by classes introduced by this +# module. +# * `(:MModule)-[:DEFINES]->(:MClassDef)`: all class definitons contained in +# this module. # # `MClass` # -# * labels: `model_name`, `MEntity`, `MClass` -# * `(:MClass)-[:CLASSTYPE]->(:MClassType)` +# * labels: `MClass`, `model_name` and `MEntity`. +# * `full_name`: fully qualified name. +# * `kind`: kind of the class (`interface`, `abstract class`, etc.) +# * `visibility`: visibility of the class. +# * `parameter_names`: JSON array listing the name of each formal generic +# parameter (in order of declaration). +# * `(:MClass)-[:CLASSTYPE]->(:MClassType)`: SEE: `MClass.mclass_type` +# +# Arguments in the `CLASSTYPE` are named following the `parameter_names` +# attribute of the `MClassDef` that introduces the class. A class definition +# introduces a class if and only if it has this class as `MCLASS` and +# has `is_intro` set to `true`. # # `MClassDef` # -# * labels: `model_name`, `MEntity`, `MClassDef` -# * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)` -# * `(:MClassDef)-[:MCLASS]->(:MClass)` -# * `(:MClassDef)-[:INTRODUCES]->(:MProperty)` -# * `(:MClassDef)-[:DECLARES]->(:MPropDef)` +# * labels: `MClassDef`, `model_name` and `MEntity`. +# * `is_intro`: Does this definition introduce the class? +# * `location`: origin of the definition. SEE: `Location.to_s` +# * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)`: bounded type associated to the +# classdef. +# * `(:MClassDef)-[:MCLASS]->(:MClass)`: associated `MClass`. +# * `(:MClassDef)-[:INTRODUCES]->(:MProperty)`: all properties introduced by +# the classdef. +# * `(:MClassDef)-[:DECLARES]->(:MPropDef)`: all property definitions in the +# classdef (introductions and redefinitions). +# * `(:MClassDef)-[:INHERITS]->(:MClassType)`: all declared super-types # # `MProperty` # -# * labels: `model_name`, `MEntity`, `MProperty` -# * `(:MProperty)-[:INTRO_CLASSDEF]->(:MClassDef)` -# -# MProperties can also have labels `MMethod`, `MAttribute`, `MVirtualTypeProp`. +# * labels: `MProperty`, `model_name` and `MEntity`. Must also have `MMethod`, +# `MAttribute` or `MVirtualTypeProp`, depending on the class of the represented +# entity. +# * `full_name`: fully qualified name. +# * `visibility`: visibility of the property. +# * `is_init`: Indicates if the property is a constructor. Exists only if the +# node is a `MMethod`. +# * `(:MProperty)-[:INTRO_CLASSDEF]->(:MClassDef)`: classdef that introduces +# the property. # # `MPropDef` # -# * labels: `model_name`, `MEntity`, `MPropDef` -# * `(:MPropDef)-[:DEFINES]->(:MProperty)` +# * labels: `MPropDef`, `model_name` and `MEntity`. Must also have `MMethodDef`, +# `MAttributeDef` or `MVirtualTypeDef`, depending on the class of the +# represented entity. +# * `is_intro`: Does this definition introduce the property? +# * `location`: origin of the definition. SEE: `Location.to_s`. +# * `(:MPropDef)-[:DEFINES]->(:MProperty)`: associated property. # -# MPropdefs can also have labels `MMethodDef`, `MAttributeDef`, `MVirtualTypeDef`. +# Additional attributes and relationship for `MMethodDef`: # -# `MMethodDef` are linked to a `MSignature`: +# * `is_abstract`: Is the method definition abstract? +# * `is_intern`: Is the method definition intern? +# * `is_extern`: Is the method definition extern? +# * `(:MMethodDef)-[:SIGNATURE]->(:MSignature)`: signature attached to the +# property definition. # -# * `(:MMethodDef)-[:SIGNATURE]->(:MSignature)` +# Additional relationship for `MAttributeDef`: # -# `MVirtualTypeDef` are linked to a `MType` (its `bound`): +# * `(:MAttributeDef)-[:TYPE]->(:MType)`: static type of the attribute, +# if specified. # -# * `(:MVirtualTypeDef)-[:BOUND]->(:MType)` +# Additional relationship for `MVirtualTypeDef`: +# +# * `(:MVirtualTypeDef)-[:BOUND]->(:MType)`: type to which the virtual type +# is bound in this definition. Exists only if this definition bound the virtual +# type to an effective type. # # `MType` # -# * labels: `model_name`, `MEntity`, `MType` +# * labels: `MType`, `model_name` and `MEntity`. Must also have `MClassType`, +# `MNullableType`, `MVirtualType` or `MSignature`, depending on the class of +# the represented entity. +# +# Additional label and relationships for `MClassType`: # -# MTypes can also have labels `MClassType`, `MGenericType`, `MNullableType`, `MVirtualType` -# and `MSignature`. +# * If it is a `MGenericType`, also has the `MGenericType` label. +# * `(:MClassType)-[:CLASS]->(:MClass)`: SEE: `MClassType.mclass` +# * `(:MClassType)-[:ARGUMENT]->(:MType)`: type arguments. # -# `MClassType` and `MGenericType` both point to a `MClass` and have type `arguments`: +# Arguments are named following the `parameter_names` attribute of the +# `MClass` referred by `CLASS`. # -# * `(:MClassType)-[:CLASS]->(:MClass)` -# * `(:MClassType)-[:ARGUMENT]->(:MType)` +# Additional relationship for `MVirtualType`: # -# `MVirtualType` points to its introducing `MProperty`: +# * `(:MVirtualType)-[:PROPERTY]->(:MProperty)`: associated property that +# determines the type (usually a `MVirtualTypeProp`). # -# * `(:MVirtualType)-[:PROPERTY]->(:MVirtualTypeDef)` +# Additional attribute and relationship for `MParameterType`: # -# `MParameterType` points to its introducing `MClass`: +# * `rank`: position of the parameter (0 for the first parameter). +# * `(:MParameterType)-[:CLASS]->(:MClass)`: generic class where the parameter +# belong. # -# * `(:MParameterType)-[:CLASS]->(:MClass)` +# Additional relationship for `MNullableType`: # -# `MNullableType` points to its non-nullable `MType`: +# * `(:MNullableType)-[:TYPE]->(:MType)`: base type of the nullable type. # -# * `(:MNullableType)-[:TYPE]->(:MType)` +# Additional attribute and relationships for `MSignature`: # -# `MSignature` can have `parameters` and a `return_mtype`: +# * `parameter_names`: JSON array representing the list of the parameter names. +# * `(:MSignature)-[:PARAMETER]->(:MParameter)`: parameters. +# * `(:MSignature)-[:RETURNTYPE]->(:MType)`: return type. Does not exist for +# procedures. # -# * `(:MSignature)-[:PARAMETER]->(:MParameter)` -# * `(:MSignature)-[:RETURNTYPE]->(:MType)` +# In order to maintain the correct parameters order, each `MSignature` node +# contains an array of parameter names corresponding to the parameter order in +# the signature. +# +# For example, if the source code contains: +# +# fun foo(a: A, b: B, c: C) +# +# The `MSignature` node will contain a property +# `parameter_names = ["a", "b", "c"]` so the MSignature can be reconstructed +# with the parameters in the correct order. # # `MParameter` # -# * labels: `model_name`, `MEntity`, `MParameter` -# * `(:MParameter)-[:TYPE]->(:MType)` +# * labels: `MParameter`, `model_name` and `MEntity`. +# * `is_vararg`: Is the parameter a vararg? +# * `rank`: position of the parameter (0 for the first parameter). +# * `(:MParameter)-[:TYPE]->(:MType)`: static type of the parameter. +# +# MParameters are also ranked by their position in the corresponding signature. +# Rank 0 for the first parameter, 1 for the next one and etc. module neo import model @@ -369,7 +450,6 @@ class NeoModel node.labels.add "MModule" node["full_name"] = mmodule.full_name node["location"] = mmodule.location.to_s - var mgroup = mmodule.mgroup for parent in mmodule.in_importation.direct_greaters do node.out_edges.add(new NeoEdge(node, "IMPORTS", to_node(parent))) end @@ -410,10 +490,14 @@ class NeoModel private fun mclass_node(mclass: MClass): NeoNode do var node = make_node(mclass) node.labels.add "MClass" - node["arity"] = mclass.arity node["full_name"] = mclass.full_name node["kind"] = mclass.kind.to_s node["visibility"] = mclass.visibility.to_s + if not mclass.mparameters.is_empty then + var parameter_names = new Array[String] + for p in mclass.mparameters do parameter_names.add(p.name) + node["parameter_names"] = new JsonArray.from(parameter_names) + end node.out_edges.add(new NeoEdge(node, "CLASSTYPE", to_node(mclass.mclass_type))) return node end @@ -426,10 +510,15 @@ class NeoModel assert node.labels.has("MClass") var mmodule = to_mmodule(model, node.in_nodes("INTRODUCES").first) var name = node["name"].to_s - var arity = node["arity"].to_s.to_i var kind = to_kind(node["kind"].to_s) var visibility = to_visibility(node["visibility"].to_s) - var mclass = new MClass(mmodule, name, arity, kind, visibility) + var parameter_names = new Array[String] + if node.has_key("parameter_names") then + for e in node["parameter_names"].as(JsonArray) do + parameter_names.add e.to_s + end + end + var mclass = new MClass(mmodule, name, parameter_names, kind, visibility) mentities[node] = mclass set_doc(node, mclass) return mclass @@ -441,9 +530,6 @@ class NeoModel node.labels.add "MClassDef" node["is_intro"] = mclassdef.is_intro node["location"] = mclassdef.location.to_s - if not mclassdef.parameter_names.is_empty then - node["parameter_names"] = new JsonArray.from(mclassdef.parameter_names) - end node.out_edges.add(new NeoEdge(node, "BOUNDTYPE", to_node(mclassdef.bound_mtype))) node.out_edges.add(new NeoEdge(node, "MCLASS", to_node(mclassdef.mclass))) for mproperty in mclassdef.intro_mproperties do @@ -467,13 +553,7 @@ class NeoModel var mmodule = to_mmodule(model, node.in_nodes("DEFINES").first) var mtype = to_mtype(model, node.out_nodes("BOUNDTYPE").first).as(MClassType) var location = to_location(node["location"].to_s) - var parameter_names = new Array[String] - if node.has_key("parameter_names") then - for e in node["parameter_names"].as(JsonArray) do - parameter_names.add e.to_s - end - end - var mclassdef = new MClassDef(mmodule, mtype, location, parameter_names) + var mclassdef = new MClassDef(mmodule, mtype, location) mentities[node] = mclassdef set_doc(node, mclassdef) var supertypes = new Array[MClassType] @@ -556,6 +636,10 @@ class NeoModel end else if mpropdef isa MAttributeDef then node.labels.add "MAttributeDef" + var static_mtype = mpropdef.static_mtype + if static_mtype != null then + node.out_edges.add(new NeoEdge(node, "TYPE", to_node(static_mtype))) + end else if mpropdef isa MVirtualTypeDef then node.labels.add "MVirtualTypeDef" var bound = mpropdef.bound @@ -586,6 +670,8 @@ class NeoModel else if node.labels.has("MAttributeDef") then mpropdef = new MAttributeDef(mclassdef, mproperty.as(MAttribute), location) mentities[node] = mpropdef + var static_mtype = node.out_nodes("TYPE") + if not static_mtype.is_empty then mpropdef.static_mtype = to_mtype(model, static_mtype.first) else if node.labels.has("MVirtualTypeDef") then mpropdef = new MVirtualTypeDef(mclassdef, mproperty.as(MVirtualTypeProp), location) mentities[node] = mpropdef @@ -625,9 +711,15 @@ class NeoModel node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mtype.mtype))) else if mtype isa MSignature then node.labels.add "MSignature" + var names = new JsonArray + var rank = 0 for mparameter in mtype.mparameters do - node.out_edges.add(new NeoEdge(node, "PARAMETER", to_node(mparameter))) + names.add mparameter.name + var pnode = to_node(mparameter) + pnode["rank"] = rank + node.out_edges.add(new NeoEdge(node, "PARAMETER", pnode)) end + if not names.is_empty then node["parameter_names"] = names var return_mtype = mtype.return_mtype if return_mtype != null then node.out_edges.add(new NeoEdge(node, "RETURNTYPE", to_node(return_mtype))) @@ -654,23 +746,35 @@ class NeoModel else if node.labels.has("MParameterType") then var mclass = to_mclass(model, node.out_nodes("CLASS").first) var rank = node["rank"].to_s.to_i - var mtype = new MParameterType(mclass, rank) + var mtype = mclass.mparameters[rank] mentities[node] = mtype return mtype else if node.labels.has("MNullableType") then var intype = to_mtype(model, node.out_nodes("TYPE").first) - var mtype = new MNullableType(intype) + var mtype = intype.as_nullable mentities[node] = mtype return mtype else if node.labels.has("MVirtualType") then var mproperty = to_mproperty(model, node.out_nodes("PROPERTY").first) - var mtype = new MVirtualType(mproperty) + assert mproperty isa MVirtualTypeProp + var mtype = mproperty.mvirtualtype mentities[node] = mtype return mtype else if node.labels.has("MSignature") then - var mparameters = new Array[MParameter] + # Get all param nodes + var mparam_nodes = new HashMap[String, MParameter] for pnode in node.out_nodes("PARAMETER") do - mparameters.add to_mparameter(model, pnode) + var mparam = to_mparameter(model, pnode) + mparam_nodes[mparam.name] = mparam + end + # Load params in the good order + var mparam_names = node["parameter_names"] + var mparameters = new Array[MParameter] + if mparam_names isa JsonArray then + for mparam_name in mparam_names do + var mparam = mparam_nodes[mparam_name.to_s] + mparameters.add mparam + end end var return_mtype: nullable MType = null var ret_nodes = node.out_nodes("RETURNTYPE")