model: introduce `model_visitor` as a basic model visiting framework.
authorJean Privat <jean@pryen.org>
Tue, 20 Oct 2015 18:10:08 +0000 (14:10 -0400)
committerJean Privat <jean@pryen.org>
Wed, 21 Oct 2015 15:19:00 +0000 (11:19 -0400)
Signed-off-by: Jean Privat <jean@pryen.org>

src/model/model_visitor.nit [new file with mode: 0644]

diff --git a/src/model/model_visitor.nit b/src/model/model_visitor.nit
new file mode 100644 (file)
index 0000000..3d8a4c7
--- /dev/null
@@ -0,0 +1,126 @@
+# 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.
+
+# Simple visitor framework for Nit models.
+#
+# This module provides the `ModelVisitor` abstract class and
+# refines the classes of the `MEntity` hierarchy to add visiting related methods.
+#
+# A standard approach on complex visitor is to dispatch the `ModelVisitor::visit` on the
+# entities to do specific things.
+#
+# ~~~nitish
+# class FooVisitor
+#   super ModelVisitor
+#   redef fun visit(e) do e.foo_visit(self)
+# end
+#
+# redef class MEntity
+# do
+#   fun foo_vist(v: FooVisitor) do visit_all(v)
+# end
+#
+# redef class MClass
+# do
+#   redef fun foo_visit(v) do
+#      print self
+#      super
+#   end
+# end
+# ~~~
+module model_visitor
+
+import model
+
+# The abstract model visitor template.
+#
+# Specific visitor must implement the `visit` method to perform the work.
+abstract class ModelVisitor
+       # Visit the entity `e`.
+       #
+       # This method setups `current_entity` and call `visit`.
+       # If `e` is null, nothing is done.
+       fun enter_visit(e: nullable MEntity) do
+               if e == null then return
+               var old_entity = current_entity
+               current_entity = e
+               visit(e)
+               current_entity = old_entity
+       end
+
+       # The current visited entity
+       var current_entity: nullable MEntity = null
+
+       # Method to define in specific visitor.
+       #
+       # It should not be called directly but used by `enter_visit`
+       protected fun visit(e: MEntity) is abstract
+end
+
+redef class MEntity
+       # Call `v.enter_visit` on all nested entities.
+       #
+       # See the specific implementation in the subclasses.
+       fun visit_all(v: ModelVisitor) do end
+end
+
+redef class Model
+       # Visit all the packages of the model.
+       redef fun visit_all(v) do
+               for x in mpackages do v.enter_visit(x)
+       end
+end
+
+redef class MPackage
+       # Visit the root group of the package.
+       redef fun visit_all(v) do
+               v.enter_visit(root)
+       end
+end
+
+redef class MGroup
+       # Visit all the subgroups and modules of the group.
+       redef fun visit_all(v) do
+               for x in in_nesting.direct_smallers do v.enter_visit(x)
+               for x in mmodules do v.enter_visit(x)
+       end
+end
+
+redef class MModule
+       # Visit all the classes and class definitions of the module.
+       #
+       # On class introduction, the `MClass` then the `MClassDef` are visited.
+       # On class refinement, only the `MClassDef` is visited (the `MClass` is visited in an imported module).
+       # On class importation, nothing is visited (the `MClass` and the `MClassDef` are visited in imported modules).
+       redef fun visit_all(v) do
+               for x in mclassdefs do
+                       if x.is_intro then v.enter_visit(x.mclass)
+                       v.enter_visit(x)
+               end
+       end
+end
+
+redef class MClassDef
+       # Visit all the classes and class definitions of the module.
+       #
+       # On property introduction, the `MProperty` then the `MPropDef` are visited.
+       # On property redefinition, only the `MPropDef` is visited (the `MProperty` is visited in an inherited class).
+       # On property inheritance, nothing is visited (the `MProperty` and the `MPropDef` are visited in inherited classes).
+       redef fun visit_all(v) do
+               for x in mpropdefs do
+                       if x.is_intro then v.enter_visit(x.mproperty)
+                       v.enter_visit(x)
+               end
+       end
+end