Merge: Model uml
authorJean Privat <jean@pryen.org>
Tue, 23 Sep 2014 19:10:01 +0000 (15:10 -0400)
committerJean Privat <jean@pryen.org>
Tue, 23 Sep 2014 19:10:01 +0000 (15:10 -0400)
Introducing a new tool `nituml` for the generation of UML diagrams from a Nit code base.

For now, it supports the generation of a class diagram from an endpoint based on the flattened class hierarchy and the generation of a package diagram from a module, showing all the introduced and refined classes using a colour scheme (will be changed as it is as ugly as it might be misleading for the end-user).

How to use :
`nituml --diagram class [-p] lib/standard/standard.nit` => generates a class diagram in dot format for the stdlib
`nituml --diagram package [-p] lib/standard/string.nit` => generates a package diagram for the string module, showing local intros and redefs of classes/methods

Example of diagram :
`nituml --diagram class lib/standard/kernel.nit`
![kernel](https://cloud.githubusercontent.com/assets/1444825/4342041/f600a4ea-403f-11e4-8720-3bd27d9802c7.png)
`nituml --diagram package -p lib/standard/queue.nit`
![str](https://cloud.githubusercontent.com/assets/1444825/4342100/bfb83398-4040-11e4-84e0-c83896748f87.png)

TODO :
* [ ] Generate associations between classes
* [ ] Eventually generate a diagram for a new Nit project as it could be used for model-first design approaches

Pull-Request: #760
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

14 files changed:
contrib/nitcc/src/grammar.nit
lib/standard/string.nit
src/model/model_base.nit
src/nituml.nit [new file with mode: 0644]
src/uml/uml.nit [new file with mode: 0644]
src/uml/uml_base.nit [new file with mode: 0644]
src/uml/uml_class.nit [new file with mode: 0644]
src/uml/uml_module.nit [new file with mode: 0644]
tests/nituml.args [new file with mode: 0644]
tests/sav/nituml.res [new file with mode: 0644]
tests/sav/nituml_args1.res [new file with mode: 0644]
tests/sav/nituml_args2.res [new file with mode: 0644]
tests/sav/nituml_args3.res [new file with mode: 0644]
tests/sav/nituml_args4.res [new file with mode: 0644]

index fae7ed1..620b0a0 100644 (file)
@@ -620,14 +620,6 @@ class LRAutomaton
        end
 end
 
-redef class String
-       # escape string used in labels for graphviz
-       fun escape_to_dot: String
-       do
-               return escape_more_to_c("|\{\}<>")
-       end
-end
-
 private class Generator
        var out = new Array[String]
        fun add(s: String) do out.add(s)
index cbd86f1..a81bdd6 100644 (file)
@@ -624,6 +624,14 @@ abstract class Text
                end
        end
 
+       # Escape string used in labels for graphviz
+       #
+       #   assert ">><<".escape_to_dot == "\\>\\>\\<\\<"
+       fun escape_to_dot: String
+       do
+               return escape_more_to_c("|\{\}<>")
+       end
+
        # Flat representation of self
        fun flatten: FlatText is abstract
 
index ef17543..9420ab8 100644 (file)
@@ -46,6 +46,7 @@ end
 #  * `public_visibility`
 #  * `protected_visibility`
 #  * `none_visibility`
+#  * `private_visiblity`
 #
 # Note this class is basically an enum.
 # FIXME: use a real enum once user-defined enums are available
diff --git a/src/nituml.nit b/src/nituml.nit
new file mode 100644 (file)
index 0000000..04cb4b3
--- /dev/null
@@ -0,0 +1,58 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# UML generator in dot format.
+module nituml
+
+import modelbuilder
+import frontend
+import uml
+
+redef class ToolContext
+       var umlphase: Phase = new UMLPhase(self, null)
+
+       var opt_gen = new OptionEnum(["class", "package"], "Choose which type of uml diagram to generate", 0, "--diagram")
+
+       redef init do
+               option_context.add_option opt_gen
+               super
+       end
+end
+
+private class UMLPhase
+       super Phase
+       redef fun process_mainmodule(mainmodule, mmodules)
+       do
+               var d = new UMLModel(mainmodule.model, mainmodule, toolcontext)
+               if toolcontext.opt_gen.value == 0 then
+                       print d.generate_class_uml.write_to_string
+               else if toolcontext.opt_gen.value == 1 then
+                       print d.generate_package_uml.write_to_string
+               end
+       end
+end
+
+# process options
+var toolcontext = new ToolContext
+toolcontext.process_options(args)
+var arguments = toolcontext.option_context.rest
+
+# build model
+var model = new Model
+var mbuilder = new ModelBuilder(model, toolcontext)
+var mmodules = mbuilder.parse(arguments)
+
+if mmodules.is_empty then return
+mbuilder.run_phases
+toolcontext.run_global_phases(mmodules)
diff --git a/src/uml/uml.nit b/src/uml/uml.nit
new file mode 100644 (file)
index 0000000..0403540
--- /dev/null
@@ -0,0 +1,20 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Group head module for UML generation services
+module uml
+
+import uml_base
+import uml_class
+import uml_module
diff --git a/src/uml/uml_base.nit b/src/uml/uml_base.nit
new file mode 100644 (file)
index 0000000..e54ccef
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Exposes the base class for UML generation of a `Model`
+module uml_base
+
+import toolcontext
+import model_utils
+
+redef class ToolContext
+       # -p
+       var opt_privacy = new OptionBool("Generates private API", "-p", "--private")
+
+       # Shortcut for the value of `self.opt_privacy`
+       fun private_gen: Bool do return opt_privacy.value
+
+       redef init do
+               option_context.add_option opt_privacy
+               super
+       end
+end
+
+class UMLModel
+       var model: Model
+       var mainmodule: MModule
+       var ctx: ToolContext
+end
diff --git a/src/uml/uml_class.nit b/src/uml/uml_class.nit
new file mode 100644 (file)
index 0000000..7581f2a
--- /dev/null
@@ -0,0 +1,232 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Provides facilities of exporting a `Model` to a UML class diagram
+module uml_class
+
+import uml_base
+
+redef class UMLModel
+       # Generates a UML class diagram from a `Model`
+       fun generate_class_uml: Streamable do
+               var tpl = new Template
+               tpl.add "digraph G \{\n"
+               tpl.add """     fontname = "Bitstream Vera Sans"
+                               fontsize = 8
+                               node [
+                                       fontname = "Bitstream Vera Sans"
+                                       fontsize = 8
+                                       shape = "record"
+                               ]
+
+                               edge [
+                                       fontname = "Bitstream Vera Sans"
+                                       fontsize = 8
+                               ]\n"""
+               tpl.add model.tpl_class(ctx, mainmodule)
+               tpl.add "\}"
+               return tpl
+       end
+end
+
+redef class Model
+
+       # Generates a UML Class diagram from the entities of a `Model`
+       fun tpl_class(ctx: ToolContext, main: MModule): Streamable do
+               var t = new Template
+               for i in mclasses do
+                       if not ctx.private_gen and i.visibility != public_visibility then continue
+                       t.add i.tpl_class(ctx, main)
+                       t.add "\n"
+               end
+               return t
+       end
+
+end
+
+redef class MEntity
+       # Generates a dot-compatible `Streamable` UML Class diagram from `self`
+       fun tpl_class(ctx: ToolContext, main: MModule): Streamable is abstract
+end
+
+redef class MClass
+
+       redef fun tpl_class(ctx, main): Streamable do
+               var t = new Template
+               t.add "{name} [\n label = \"\{"
+               if kind == abstract_kind then
+                       t.add "abstract\\n{name}"
+               else if kind == interface_kind then
+                       t.add "interface\\n{name}"
+               else
+                       t.add "{name}"
+               end
+               if arity > 0 then
+                       t.add "["
+                       var formal = intro.parameter_names
+                       t.add formal.first
+                       for i in [1 .. formal.length[ do
+                               t.add ", "
+                               t.add formal[i]
+                       end
+                       t.add "]"
+               end
+               t.add "|"
+               var props: Collection[MProperty]
+               if ctx.private_gen then
+                       props = intro_mproperties(none_visibility)
+               else
+                       props = intro_mproperties(public_visibility)
+               end
+               for i in props do
+                       if i isa MAttribute then
+                               t.add i.tpl_class(ctx, main)
+                               t.add "\\l"
+                       end
+               end
+               t.add "|"
+               for i in intro_methods do
+                       if not ctx.private_gen and i.visibility != public_visibility then continue
+                       t.add i.tpl_class(ctx, main)
+                       t.add "\\l"
+               end
+               t.add "\}\"\n]\n"
+               var g = in_hierarchy(main).direct_greaters
+               for i in g do
+                       if not ctx.private_gen and i.visibility != public_visibility then continue
+                       t.add "{i.name} -> {name} [dir=back"
+                       if i.kind == interface_kind then
+                               t.add " arrowtail=open style=dashed"
+                       else
+                               t.add " arrowtail=empty"
+                       end
+                       t.add "];\n"
+               end
+               return t
+       end
+
+end
+
+redef class MMethod
+       redef fun tpl_class(ctx, main) do
+               var tpl = new Template
+               tpl.add visibility.tpl_class
+               tpl.add " "
+               tpl.add name.escape_to_dot
+               tpl.add intro.msignature.tpl_class(ctx, main)
+               return tpl
+       end
+end
+
+redef class MSignature
+
+       redef fun tpl_class(ctx, main) do
+               var t = new Template
+               t.add "("
+               var params = new Array[MParameter]
+               for i in mparameters do
+                       params.add i
+               end
+               if params.length > 0 then
+                       t.add params.first.name
+                       t.add ": "
+                       t.add params.first.mtype.tpl_class(ctx, main)
+                       for i in [1 .. params.length [ do
+                               t.add ", "
+                               t.add params[i].name
+                               t.add ": "
+                               t.add params[i].mtype.tpl_class(ctx, main)
+                       end
+               end
+               t.add ")"
+               if return_mtype != null then
+                       t.add ": "
+                       t.add return_mtype.tpl_class(ctx, main)
+               end
+               return t
+       end
+end
+
+redef class MAttribute
+       redef fun tpl_class(ctx, main) do
+               var tpl = new Template
+               tpl.add visibility.tpl_class
+               tpl.add " "
+               tpl.add name
+               tpl.add ": "
+               tpl.add intro.static_mtype.tpl_class(ctx, main)
+               return tpl
+       end
+end
+
+redef class MVisibility
+       # Returns the visibility as a UML token
+       #
+       #    assert public_visibility.tpl_class == "+"
+       #    assert private_visibility.tpl_class == "-"
+       fun tpl_class: Streamable do
+               if self == private_visibility then
+                       return "-"
+               else if self == protected_visibility then
+                       return "#"
+               else if self == public_visibility then
+                       return "+"
+               else
+                       return "+"
+               end
+       end
+end
+
+redef class MClassType
+       redef fun tpl_class(c, m) do
+               return name
+       end
+end
+
+redef class MGenericType
+       redef fun tpl_class(c, m) do
+               var t = new Template
+               t.add name.substring(0, name.index_of('['))
+               t.add "["
+               t.add arguments.first.tpl_class(c, m)
+               for i in [1 .. arguments.length[ do
+                       t.add ", "
+                       t.add arguments[i].tpl_class(c, m)
+               end
+               t.add "]"
+               return t
+       end
+end
+
+redef class MParameterType
+       redef fun tpl_class(c, m) do
+               var n = mclass.intro.parameter_names
+               return n[rank]
+       end
+end
+
+redef class MVirtualType
+       redef fun tpl_class(c, m) do
+               return name
+       end
+end
+
+redef class MNullableType
+       redef fun tpl_class(c, m) do
+               var t = new Template
+               t.add "nullable "
+               t.add mtype.tpl_class(c, m)
+               return t
+       end
+end
diff --git a/src/uml/uml_module.nit b/src/uml/uml_module.nit
new file mode 100644 (file)
index 0000000..55d16f3
--- /dev/null
@@ -0,0 +1,158 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services for generation of a UML package diagram based on a `Model`
+module uml_module
+
+import uml_base
+import uml_class
+
+redef class UMLModel
+       # Generates a UML package diagram from a `Model`
+       fun generate_package_uml: Streamable do
+               var tpl = new Template
+               tpl.add "digraph G \{\n"
+               tpl.add """     fontname = "Bitstream Vera Sans"
+                       fontsize = 8
+                       node [
+                               fontname = "Bitstream Vera Sans"
+                               fontsize = 8
+                               shape = "record"
+                       ]
+                       edge [
+                               fontname = "Bitstream Vera Sans"
+                               fontsize = 8
+                       ]\n"""
+               tpl.add model.tpl_module(ctx, mainmodule)
+               tpl.add "\}"
+               return tpl
+       end
+end
+
+redef class Model
+       # Returns a UML package diagram of `main`
+       fun tpl_module(ctx: ToolContext, main: MModule): Streamable do
+               return main.tpl_module(ctx, main)
+       end
+end
+
+redef class MModule
+       redef fun tpl_module(ctx, main) do
+               var t = new Template
+               t.add "subgraph cluster{name} \{\n"
+               t.add "label = \"{name}\"\n"
+               for i in mclassdefs do
+                       if not ctx.private_gen and i.mclass.visibility != public_visibility then continue
+                       t.add i.tpl_module(ctx, main)
+               end
+               t.add "\}\n"
+               return t
+       end
+end
+
+redef class MEntity
+       # Builds a dot UML package diagram entity from `self`
+       fun tpl_module(ctx: ToolContext, main: MModule): Streamable is abstract
+end
+
+redef class MClassDef
+
+       # Colour for the border of a class when first introduced
+       #
+       # Defaults to a shade of green
+       var intro_colour = "#58B26A"
+
+       # Colour for the border of a class when refined
+       #
+       # Defaults to a shade of red
+       var redef_colour = "#B24758"
+
+       redef fun tpl_module(ctx, main) do
+               var t = new Template
+               t.add "{mmodule}{name} [\n\tlabel = \"\{"
+               if mclass.kind == abstract_kind then
+                       t.add "abstract\\n{name}"
+               else if mclass.kind == interface_kind then
+                       t.add "interface\\n{name}"
+               else
+                       t.add "{name}"
+               end
+               if mclass.arity > 0 then
+                       t.add "["
+                       var formal = mclass.intro.parameter_names
+                       t.add formal.first
+                       for i in [1 .. formal.length[ do
+                               t.add ", "
+                               t.add formal[i]
+                       end
+                       t.add "]"
+               end
+               t.add "|"
+               for i in mpropdefs do
+                       if not i isa MAttributeDef then continue
+                       if not ctx.private_gen and i.mproperty.visibility != public_visibility then continue
+                       t.add i.tpl_module(ctx, main)
+                       t.add "\\l"
+               end
+               t.add "|"
+               for i in mpropdefs do
+                       if not i isa MMethodDef then continue
+                       if not ctx.private_gen and i.mproperty.visibility != public_visibility then continue
+                       t.add i.tpl_module(ctx, main)
+                       t.add "\\l"
+               end
+               t.add "\}\""
+               if is_intro then
+                       t.add "color=\"{intro_colour}\""
+               else
+                       t.add "color=\"{redef_colour}\""
+               end
+               t.add "\n]\n"
+               var supers = in_hierarchy.direct_greaters
+               for i in supers do
+                       if i.mmodule != mmodule then continue
+                       t.add "{i.mmodule}{i.name} -> {mmodule}{name} [dir=back"
+                       if i.mclass.kind == interface_kind then
+                               t.add " arrowtail=open style=dashed"
+                       else
+                               t.add " arrowtail=empty"
+                       end
+                       t.add "]\n"
+               end
+               return t
+       end
+end
+
+redef class MMethodDef
+       redef fun tpl_module(ctx, main) do
+               var t = new Template
+               t.add mproperty.visibility.tpl_class
+               t.add " "
+               t.add name.escape_to_dot
+               t.add msignature.tpl_class(ctx, main)
+               return t
+       end
+end
+
+redef class MAttributeDef
+       redef fun tpl_module(ctx, main) do
+               var t = new Template
+               t.add mproperty.visibility.tpl_class
+               t.add " "
+               t.add name
+               t.add ": "
+               t.add static_mtype.tpl_class(ctx, main)
+               return t
+       end
+end
diff --git a/tests/nituml.args b/tests/nituml.args
new file mode 100644 (file)
index 0000000..2add9e5
--- /dev/null
@@ -0,0 +1,4 @@
+--diagram package --private ./base_prot_sig2.nit -I ../lib/standard
+--diagram package ./base_prot_sig2.nit -I ../lib/standard
+--diagram class --private ./base_prot_sig2.nit -I ../lib/standard
+--diagram class ./base_prot_sig2.nit -I ../lib/standard
diff --git a/tests/sav/nituml.res b/tests/sav/nituml.res
new file mode 100644 (file)
index 0000000..e50ad70
--- /dev/null
@@ -0,0 +1,2 @@
+Usage: [OPTION]... [ARG]...
+Use --help for help
diff --git a/tests/sav/nituml_args1.res b/tests/sav/nituml_args1.res
new file mode 100644 (file)
index 0000000..ecae7b8
--- /dev/null
@@ -0,0 +1,22 @@
+digraph G {
+       fontname = "Bitstream Vera Sans"
+                       fontsize = 8
+                       node [
+                               fontname = "Bitstream Vera Sans"
+                               fontsize = 8
+                               shape = "record"
+                       ]
+                       edge [
+                               fontname = "Bitstream Vera Sans"
+                               fontsize = 8
+                       ]
+subgraph clusterbase_prot_sig2 {
+label = "base_prot_sig2"
+base_prot_sig2C [
+       label = "{C|- _vpriA: nullable A\l- _vpriA2: A\l|- priA(a: A)\l- vpriA(): nullable A\l- vpriA=(vpriA: nullable A)\l- vpriA2(): A\l- vpriA2=(vpriA2: A)\l+ init()\l}"color="#58B26A"
+]
+base_prot_sig2D [
+       label = "{D|- _vpubA: nullable A\l- _vpriA: nullable A\l- _vpubA2: A\l- _vpriA2: A\l|- pubA(a: A)\l- priA(a: A)\l- vpubA(): nullable A\l- vpubA=(vpubA: nullable A)\l- vpriA(): nullable A\l- vpriA=(vpriA: nullable A)\l- vpubA2(): A\l- vpubA2=(vpubA2: A)\l- vpriA2(): A\l- vpriA2=(vpriA2: A)\l+ init()\l}"color="#58B26A"
+]
+}
+}
diff --git a/tests/sav/nituml_args2.res b/tests/sav/nituml_args2.res
new file mode 100644 (file)
index 0000000..5a9488d
--- /dev/null
@@ -0,0 +1,19 @@
+digraph G {
+       fontname = "Bitstream Vera Sans"
+                       fontsize = 8
+                       node [
+                               fontname = "Bitstream Vera Sans"
+                               fontsize = 8
+                               shape = "record"
+                       ]
+                       edge [
+                               fontname = "Bitstream Vera Sans"
+                               fontsize = 8
+                       ]
+subgraph clusterbase_prot_sig2 {
+label = "base_prot_sig2"
+base_prot_sig2C [
+       label = "{C||+ init()\l}"color="#58B26A"
+]
+}
+}
diff --git a/tests/sav/nituml_args3.res b/tests/sav/nituml_args3.res
new file mode 100644 (file)
index 0000000..af28f14
--- /dev/null
@@ -0,0 +1,84 @@
+digraph G {
+       fontname = "Bitstream Vera Sans"
+                               fontsize = 8
+                               node [
+                                       fontname = "Bitstream Vera Sans"
+                                       fontsize = 8
+                                       shape = "record"
+                               ]
+
+                               edge [
+                                       fontname = "Bitstream Vera Sans"
+                                       fontsize = 8
+                               ]
+Object [
+ label = "{interface\nObject||+ object_id(): Int\l+ is_same_type(other: Object): Bool\l+ is_same_instance(other: nullable Object): Bool\l+ ==(other: nullable Object): Bool\l+ !=(other: nullable Object): Bool\l+ output()\l+ output_class_name()\l+ hash(): Int\l+ exit(exit_value: Int)\l+ sys(): Sys\l}"
+]
+
+Sys [
+ label = "{Sys||+ main()\l+ run()\l+ errno(): Int\l}"
+]
+Object -> Sys [dir=back arrowtail=open style=dashed];
+
+Comparable [
+ label = "{interface\nComparable||+ \<(other: OTHER): Bool\l+ \<=(other: OTHER): Bool\l+ \>=(other: OTHER): Bool\l+ \>(other: OTHER): Bool\l+ \<=\>(other: OTHER): Int\l+ is_between(c: OTHER, d: OTHER): Bool\l+ max(other: OTHER): OTHER\l+ min(c: OTHER): OTHER\l}"
+]
+Object -> Comparable [dir=back arrowtail=open style=dashed];
+
+Discrete [
+ label = "{interface\nDiscrete||+ successor(i: Int): OTHER\l+ predecessor(i: Int): OTHER\l+ distance(d: OTHER): Int\l}"
+]
+Comparable -> Discrete [dir=back arrowtail=open style=dashed];
+
+Numeric [
+ label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
+]
+Comparable -> Numeric [dir=back arrowtail=open style=dashed];
+
+Bool [
+ label = "{Bool||+ to_i(): Int\l}"
+]
+Object -> Bool [dir=back arrowtail=open style=dashed];
+
+Float [
+ label = "{Float||}"
+]
+Numeric -> Float [dir=back arrowtail=open style=dashed];
+
+Int [
+ label = "{Int||+ %(i: Int): Int\l+ lshift(i: Int): Int\l+ rshift(i: Int): Int\l+ ascii(): Char\l+ digit_count(b: Int): Int\l+ digit_count_base_10(): Int\l+ to_c(): Char\l+ abs(): Int\l}"
+]
+Discrete -> Int [dir=back arrowtail=open style=dashed];
+Numeric -> Int [dir=back arrowtail=open style=dashed];
+
+Char [
+ label = "{Char||+ to_i(): Int\l+ ascii(): Int\l+ to_lower(): Char\l+ to_upper(): Char\l+ is_digit(): Bool\l+ is_lower(): Bool\l+ is_upper(): Bool\l+ is_letter(): Bool\l}"
+]
+Discrete -> Char [dir=back arrowtail=open style=dashed];
+
+Pointer [
+ label = "{Pointer||+ address_is_null(): Bool\l+ free()\l}"
+]
+Object -> Pointer [dir=back arrowtail=open style=dashed];
+
+A [
+ label = "{A|- _vpubA: nullable A\l- _vproA: nullable A\l- _vpriA: nullable A\l- _vpubA2: A\l- _vproA2: A\l- _vpriA2: A\l- _vpriB: nullable B\l- _vpriB2: B\l|+ pubA(a: A)\l# proA(a: A)\l- priA(a: A)\l+ vpubA(): nullable A\l+ vpubA=(vpubA: nullable A)\l# vproA(): nullable A\l# vproA=(vproA: nullable A)\l- vpriA(): nullable A\l- vpriA=(vpriA: nullable A)\l+ vpubA2(): A\l+ vpubA2=(vpubA2: A)\l# vproA2(): A\l# vproA2=(vproA2: A)\l- vpriA2(): A\l- vpriA2=(vpriA2: A)\l- priB(a: B)\l- vpriB(): nullable B\l- vpriB=(vpriB: nullable B)\l- vpriB2(): B\l- vpriB2=(vpriB2: B)\l}"
+]
+Object -> A [dir=back arrowtail=open style=dashed];
+
+B [
+ label = "{B|- _vpubA: nullable A\l- _vpriA: nullable A\l- _vpubA2: A\l- _vpriA2: A\l- _vpubB: nullable B\l- _vpriB: nullable B\l- _vpubB2: B\l- _vpriB2: B\l|- pubA(a: A)\l- priA(a: A)\l- vpubA(): nullable A\l- vpubA=(vpubA: nullable A)\l- vpriA(): nullable A\l- vpriA=(vpriA: nullable A)\l- vpubA2(): A\l- vpubA2=(vpubA2: A)\l- vpriA2(): A\l- vpriA2=(vpriA2: A)\l- pubB(a: B)\l- priB(a: B)\l- vpubB(): nullable B\l- vpubB=(vpubB: nullable B)\l- vpriB(): nullable B\l- vpriB=(vpriB: nullable B)\l- vpubB2(): B\l- vpubB2=(vpubB2: B)\l- vpriB2(): B\l- vpriB2=(vpriB2: B)\l}"
+]
+Object -> B [dir=back arrowtail=open style=dashed];
+
+C [
+ label = "{C|- _vpriA: nullable A\l- _vpriA2: A\l|- priA(a: A)\l- vpriA(): nullable A\l- vpriA=(vpriA: nullable A)\l- vpriA2(): A\l- vpriA2=(vpriA2: A)\l}"
+]
+Object -> C [dir=back arrowtail=open style=dashed];
+
+D [
+ label = "{D|- _vpubA: nullable A\l- _vpriA: nullable A\l- _vpubA2: A\l- _vpriA2: A\l|- pubA(a: A)\l- priA(a: A)\l- vpubA(): nullable A\l- vpubA=(vpubA: nullable A)\l- vpriA(): nullable A\l- vpriA=(vpriA: nullable A)\l- vpubA2(): A\l- vpubA2=(vpubA2: A)\l- vpriA2(): A\l- vpriA2=(vpriA2: A)\l}"
+]
+Object -> D [dir=back arrowtail=open style=dashed];
+
+}
diff --git a/tests/sav/nituml_args4.res b/tests/sav/nituml_args4.res
new file mode 100644 (file)
index 0000000..6189897
--- /dev/null
@@ -0,0 +1,74 @@
+digraph G {
+       fontname = "Bitstream Vera Sans"
+                               fontsize = 8
+                               node [
+                                       fontname = "Bitstream Vera Sans"
+                                       fontsize = 8
+                                       shape = "record"
+                               ]
+
+                               edge [
+                                       fontname = "Bitstream Vera Sans"
+                                       fontsize = 8
+                               ]
+Object [
+ label = "{interface\nObject||+ object_id(): Int\l+ is_same_type(other: Object): Bool\l+ is_same_instance(other: nullable Object): Bool\l+ ==(other: nullable Object): Bool\l+ !=(other: nullable Object): Bool\l+ output()\l+ output_class_name()\l+ hash(): Int\l+ exit(exit_value: Int)\l+ sys(): Sys\l}"
+]
+
+Sys [
+ label = "{Sys||+ main()\l+ run()\l+ errno(): Int\l}"
+]
+Object -> Sys [dir=back arrowtail=open style=dashed];
+
+Comparable [
+ label = "{interface\nComparable||+ \<(other: OTHER): Bool\l+ \<=(other: OTHER): Bool\l+ \>=(other: OTHER): Bool\l+ \>(other: OTHER): Bool\l+ \<=\>(other: OTHER): Int\l+ is_between(c: OTHER, d: OTHER): Bool\l+ max(other: OTHER): OTHER\l+ min(c: OTHER): OTHER\l}"
+]
+Object -> Comparable [dir=back arrowtail=open style=dashed];
+
+Discrete [
+ label = "{interface\nDiscrete||+ successor(i: Int): OTHER\l+ predecessor(i: Int): OTHER\l+ distance(d: OTHER): Int\l}"
+]
+Comparable -> Discrete [dir=back arrowtail=open style=dashed];
+
+Numeric [
+ label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
+]
+Comparable -> Numeric [dir=back arrowtail=open style=dashed];
+
+Bool [
+ label = "{Bool||+ to_i(): Int\l}"
+]
+Object -> Bool [dir=back arrowtail=open style=dashed];
+
+Float [
+ label = "{Float||}"
+]
+Numeric -> Float [dir=back arrowtail=open style=dashed];
+
+Int [
+ label = "{Int||+ %(i: Int): Int\l+ lshift(i: Int): Int\l+ rshift(i: Int): Int\l+ ascii(): Char\l+ digit_count(b: Int): Int\l+ digit_count_base_10(): Int\l+ to_c(): Char\l+ abs(): Int\l}"
+]
+Discrete -> Int [dir=back arrowtail=open style=dashed];
+Numeric -> Int [dir=back arrowtail=open style=dashed];
+
+Char [
+ label = "{Char||+ to_i(): Int\l+ ascii(): Int\l+ to_lower(): Char\l+ to_upper(): Char\l+ is_digit(): Bool\l+ is_lower(): Bool\l+ is_upper(): Bool\l+ is_letter(): Bool\l}"
+]
+Discrete -> Char [dir=back arrowtail=open style=dashed];
+
+Pointer [
+ label = "{Pointer||+ address_is_null(): Bool\l+ free()\l}"
+]
+Object -> Pointer [dir=back arrowtail=open style=dashed];
+
+A [
+ label = "{A||+ pubA(a: A)\l+ vpubA(): nullable A\l+ vpubA=(vpubA: nullable A)\l+ vpubA2(): A\l+ vpubA2=(vpubA2: A)\l}"
+]
+Object -> A [dir=back arrowtail=open style=dashed];
+
+C [
+ label = "{C||}"
+]
+Object -> C [dir=back arrowtail=open style=dashed];
+
+}