insndb: refactor visitors (again)
authorDmitry Selyutin <ghostmansd@gmail.com>
Wed, 7 Jun 2023 13:16:57 +0000 (16:16 +0300)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 22 Dec 2023 19:26:19 +0000 (19:26 +0000)
src/openpower/insndb/core.py
src/openpower/insndb/db.py

index 042b938d7dd3705dce783f049f1a3933235fd849..ddf2564fa7ee5ed49251a4d640aa9e4ec7b9fa23 100644 (file)
@@ -57,33 +57,45 @@ from openpower.decoder.pseudo.pagereader import ISA as _ISA
 
 
 class Node:
-    @property
-    def subnodes(self):
-        yield from ()
+    def visit(self, handler, matcher, depth):
+        if matcher(node=self, depth=depth):
+            with handler(node=self, depth=depth):
+                pass
 
 
 class Visitor:
-    @_contextlib.contextmanager
-    def Node(self, node, depth):
-        yield node
-        for subnode in node.subnodes:
-            manager = subnode.__class__.__name__
-            manager = getattr(self, manager, self.Node)
-            with manager(node=subnode, depth=(depth + 1)):
-                pass
+    def __init__(self, **parameters):
+        self.__parameters = _types.MappingProxyType(parameters)
+        return super().__init__()
 
-    def __getattr__(self, attr):
-        return self.Node
+    def __contains__(self, key):
+        return self.__parameters.__contains__(key)
+
+    def __getitem__(self, key):
+        return self.__parameters.__getitem__(key)
+
+    def Node(self, node, depth):
+        raise NotImplementedError()
 
     def __call__(self, node, depth):
-        manager = node.__class__.__name__
-        manager = getattr(self, manager, self.Node)
-        return manager(node=node, depth=depth)
+        method = node.__class__.__name__
+        method = getattr(self, method, self.Node)
+        return method(node=node, depth=depth)
 
 
-def visit(visitor, node):
-    with visitor(node=node, depth=0):
-        pass
+class Matcher(Visitor):
+    def Node(self, node, depth):
+        return True
+
+
+class Handler(Visitor):
+    @_contextlib.contextmanager
+    def Node(self, node, depth):
+        yield node
+
+
+def visit(node, handler, matcher=Matcher()):
+    node.visit(handler=handler, matcher=matcher, depth=0)
 
 
 @_functools.total_ordering
@@ -850,10 +862,6 @@ class Extra(Node):
     seltype: _SelType
     idx: _SVExtra
 
-    def visit(self, visitor):
-        with visitor.extra(extra=self) as extra:
-            pass
-
 
 @_functools.total_ordering
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -865,10 +873,13 @@ class Record(Node):
     mdwn: MarkdownRecord
     svp64: SVP64Record = None
 
-    @property
-    def subnodes(self):
-        for (name, fields) in self.extras.items():
-            yield Extra(name=name, **fields)
+    def visit(self, handler, matcher, depth):
+        if matcher(node=self, depth=depth):
+            with handler(node=self, depth=depth):
+                for (name, fields) in self.extras.items():
+                    extra = Extra(name=name, **fields)
+                    extra.visit(depth=(depth + 1),
+                        handler=handler, matcher=matcher)
 
     @property
     def extras(self):
@@ -3751,6 +3762,13 @@ class Database(Node):
 
         return super().__init__()
 
+    def visit(self, handler, matcher, depth):
+        if matcher(node=self, depth=depth):
+            with handler(node=self, depth=depth):
+                for record in self:
+                    record.visit(depth=(depth + 1),
+                        handler=handler, matcher=matcher)
+
     @property
     def subnodes(self):
         for record in self.__db:
index 73f4abe2a278a78622fa988119ea66338927c755..522d96501184bd9cb42b050253a5e39b4e638713 100644 (file)
@@ -8,7 +8,8 @@ from openpower.decoder.power_enums import (
 )
 from openpower.insndb.core import (
     Database,
-    Visitor,
+    Handler,
+    Matcher,
     visit,
 )
 
@@ -36,37 +37,23 @@ class SVP64Instruction(Instruction):
         return self
 
 
-class BaseVisitor(Visitor):
-    def __init__(self, **arguments):
-        self.__arguments = types.MappingProxyType(arguments)
-        return super().__init__()
-
-    def __getitem__(self, argument):
-        return self.__arguments[argument]
-
-
-class ListVisitor(BaseVisitor):
+class ListHandler(Handler):
     @contextlib.contextmanager
     def Record(self, node, depth):
         print(node.name)
         yield node
 
 
-class InstructionVisitor(BaseVisitor):
-    @contextlib.contextmanager
-    def Database(self, node, depth):
-        yield node
-        for subnode in node.subnodes:
-            if subnode.name == self["insn"]:
-                with self(node=subnode, depth=(depth + 1)):
-                    pass
+class InstructionMatcher(Matcher):
+    def Record(self, node, depth):
+        return (node.name == self["insn"])
 
 
-class SVP64InstructionVisitor(InstructionVisitor):
+class SVP64InstructionMatcher(InstructionMatcher):
     pass
 
 
-class OpcodesVisitor(InstructionVisitor):
+class OpcodesHandler(Handler):
     @contextlib.contextmanager
     def Record(self, node, depth):
         for opcode in node.opcodes:
@@ -74,7 +61,7 @@ class OpcodesVisitor(InstructionVisitor):
         yield node
 
 
-class OperandsVisitor(InstructionVisitor):
+class OperandsHandler(Handler):
     @contextlib.contextmanager
     def Record(self, node, depth):
         for operand in node.dynamic_operands:
@@ -86,7 +73,7 @@ class OperandsVisitor(InstructionVisitor):
         yield node
 
 
-class PCodeVisitor(InstructionVisitor):
+class PCodeHandler(Handler):
     @contextlib.contextmanager
     def Record(self, node, depth):
         for line in node.pcode:
@@ -94,7 +81,7 @@ class PCodeVisitor(InstructionVisitor):
         yield node
 
 
-class ExtrasVisitor(SVP64InstructionVisitor):
+class ExtrasHandler(Handler):
     @contextlib.contextmanager
     def Extra(self, node, depth):
         print(node.name)
@@ -108,23 +95,28 @@ class ExtrasVisitor(SVP64InstructionVisitor):
 def main():
     commands = {
         "list": (
-            ListVisitor,
+            ListHandler,
+            Matcher,
             "list available instructions",
         ),
         "opcodes": (
-            OpcodesVisitor,
+            OpcodesHandler,
+            InstructionMatcher,
             "print instruction opcodes",
         ),
         "operands": (
-            OperandsVisitor,
+            OperandsHandler,
+            InstructionMatcher,
             "print instruction operands",
         ),
         "pcode": (
-            PCodeVisitor,
+            PCodeHandler,
+            InstructionMatcher,
             "print instruction pseudocode",
         ),
         "extras": (
-            ExtrasVisitor,
+            ExtrasHandler,
+            InstructionMatcher,
             "print instruction extras (SVP64)",
         ),
     }
@@ -136,10 +128,10 @@ def main():
         default=False)
     main_subparser = main_parser.add_subparsers(dest="command", required=True)
 
-    for (command, (visitor, help)) in commands.items():
+    for (command, (handler, matcher, help)) in commands.items():
         parser = main_subparser.add_parser(command, help=help)
-        if issubclass(visitor, InstructionVisitor):
-            if issubclass(visitor, SVP64InstructionVisitor):
+        if issubclass(matcher, InstructionMatcher):
+            if issubclass(matcher, SVP64InstructionMatcher):
                 arg_cls = SVP64Instruction
             else:
                 arg_cls = Instruction
@@ -151,10 +143,11 @@ def main():
     log = args.pop("log")
     if not log:
         os.environ["SILENCELOG"] = "true"
-    visitor = commands[command][0](**args)
+    handler = commands[command][0](**args)
+    matcher = commands[command][1](**args)
 
     db = Database(find_wiki_dir())
-    visit(visitor=visitor, node=db)
+    visit(handler=handler, matcher=matcher, node=db)
 
 
 if __name__ == "__main__":