insndb: decouple visitors and walking
authorDmitry Selyutin <ghostmansd@gmail.com>
Wed, 7 Jun 2023 19:57:16 +0000 (22:57 +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 ddf2564fa7ee5ed49251a4d640aa9e4ec7b9fa23..2876e787ec851fee5af3446cbf8284f7b0cf03e1 100644 (file)
@@ -56,46 +56,32 @@ from openpower.decoder.power_fields import (
 from openpower.decoder.pseudo.pagereader import ISA as _ISA
 
 
-class Node:
-    def visit(self, handler, matcher, depth):
-        if matcher(node=self, depth=depth):
-            with handler(node=self, depth=depth):
-                pass
-
-
 class Visitor:
-    def __init__(self, **parameters):
-        self.__parameters = _types.MappingProxyType(parameters)
-        return super().__init__()
-
-    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):
+    def __call__(self, node):
         method = node.__class__.__name__
         method = getattr(self, method, self.Node)
-        return method(node=node, depth=depth)
-
+        return method(node=node)
 
-class Matcher(Visitor):
-    def Node(self, node, depth):
-        return True
-
-
-class Handler(Visitor):
     @_contextlib.contextmanager
-    def Node(self, node, depth):
+    def Node(self, node):
+        for subnode in node.subnodes:
+            with self(subnode):
+                pass
         yield node
 
 
-def visit(node, handler, matcher=Matcher()):
-    node.visit(handler=handler, matcher=matcher, depth=0)
+class Node:
+    @property
+    def subnodes(self):
+        yield from ()
+
+
+def walk(root):
+    nodes = _collections.deque([root])
+    while nodes:
+        node = nodes.popleft()
+        nodes.extend(node.subnodes)
+        yield node
 
 
 @_functools.total_ordering
@@ -873,13 +859,10 @@ class Record(Node):
     mdwn: MarkdownRecord
     svp64: SVP64Record = None
 
-    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 subnodes(self):
+        for (name, fields) in self.extras.items():
+            yield Extra(name=name, **fields)
 
     @property
     def extras(self):
@@ -3762,17 +3745,9 @@ 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:
-            yield record
+        yield from self
 
     def __repr__(self):
         return repr(self.__db)
index 522d96501184bd9cb42b050253a5e39b4e638713..748f60abb139f108f59110e9dc2f05868751ac74 100644 (file)
@@ -8,9 +8,7 @@ from openpower.decoder.power_enums import (
 )
 from openpower.insndb.core import (
     Database,
-    Handler,
-    Matcher,
-    visit,
+    Visitor,
 )
 
 
@@ -37,33 +35,40 @@ class SVP64Instruction(Instruction):
         return self
 
 
-class ListHandler(Handler):
+class RecordNameVisitor(Visitor):
+    def __init__(self, name):
+        self.__name = name
+        self.__records = set()
+        return super().__init__()
+
     @contextlib.contextmanager
-    def Record(self, node, depth):
-        print(node.name)
+    def Record(self, node):
+        if node.name == self.__name:
+            self.__records.add(node)
         yield node
 
-
-class InstructionMatcher(Matcher):
-    def Record(self, node, depth):
-        return (node.name == self["insn"])
+    def __iter__(self):
+        yield from self.__records
 
 
-class SVP64InstructionMatcher(InstructionMatcher):
-    pass
+class ListVisitor(Visitor):
+    @contextlib.contextmanager
+    def Record(self, node):
+        print(node.name)
+        yield node
 
 
-class OpcodesHandler(Handler):
+class OpcodesVisitor(Visitor):
     @contextlib.contextmanager
-    def Record(self, node, depth):
+    def Record(self, node):
         for opcode in node.opcodes:
             print(opcode)
         yield node
 
 
-class OperandsHandler(Handler):
+class OperandsVisitor(Visitor):
     @contextlib.contextmanager
-    def Record(self, node, depth):
+    def Record(self, node):
         for operand in node.dynamic_operands:
             print(operand.name, ",".join(map(str, operand.span)))
         for operand in node.static_operands:
@@ -73,17 +78,17 @@ class OperandsHandler(Handler):
         yield node
 
 
-class PCodeHandler(Handler):
+class PCodeVisitor(Visitor):
     @contextlib.contextmanager
-    def Record(self, node, depth):
+    def Record(self, node):
         for line in node.pcode:
             print(line)
         yield node
 
 
-class ExtrasHandler(Handler):
+class ExtrasVisitor(Visitor):
     @contextlib.contextmanager
-    def Extra(self, node, depth):
+    def Extra(self, node):
         print(node.name)
         print("    sel", node.sel)
         print("    reg", node.reg)
@@ -95,28 +100,23 @@ class ExtrasHandler(Handler):
 def main():
     commands = {
         "list": (
-            ListHandler,
-            Matcher,
+            ListVisitor,
             "list available instructions",
         ),
         "opcodes": (
-            OpcodesHandler,
-            InstructionMatcher,
+            OpcodesVisitor,
             "print instruction opcodes",
         ),
         "operands": (
-            OperandsHandler,
-            InstructionMatcher,
+            OperandsVisitor,
             "print instruction operands",
         ),
         "pcode": (
-            PCodeHandler,
-            InstructionMatcher,
+            PCodeVisitor,
             "print instruction pseudocode",
         ),
         "extras": (
-            ExtrasHandler,
-            InstructionMatcher,
+            ExtrasVisitor,
             "print instruction extras (SVP64)",
         ),
     }
@@ -128,10 +128,10 @@ def main():
         default=False)
     main_subparser = main_parser.add_subparsers(dest="command", required=True)
 
-    for (command, (handler, matcher, help)) in commands.items():
-        parser = main_subparser.add_parser(command, help=help)
-        if issubclass(matcher, InstructionMatcher):
-            if issubclass(matcher, SVP64InstructionMatcher):
+    for (command, (visitor, helper)) in commands.items():
+        parser = main_subparser.add_parser(command, help=helper)
+        if command not in ("list",):
+            if command in ("extras",):
                 arg_cls = SVP64Instruction
             else:
                 arg_cls = Instruction
@@ -143,11 +143,19 @@ def main():
     log = args.pop("log")
     if not log:
         os.environ["SILENCELOG"] = "true"
-    handler = commands[command][0](**args)
-    matcher = commands[command][1](**args)
+    visitor = commands[command][0]()
 
     db = Database(find_wiki_dir())
-    visit(handler=handler, matcher=matcher, node=db)
+    if command in ("list",):
+        nodes = (db,)
+    else:
+        match = RecordNameVisitor(name=args["insn"])
+        with match(node=db):
+            nodes = frozenset(match)
+
+    for node in nodes:
+        with visitor(node=node):
+            pass
 
 
 if __name__ == "__main__":