oppc: decouple pseudocode module
authorDmitry Selyutin <ghostmansd@gmail.com>
Tue, 9 Jan 2024 18:08:41 +0000 (21:08 +0300)
committerDmitry Selyutin <ghostmansd@gmail.com>
Tue, 9 Jan 2024 18:08:41 +0000 (21:08 +0300)
src/openpower/oppc/__main__.py
src/openpower/oppc/pc_pseudocode.py [new file with mode: 0644]
src/openpower/oppc/pc_util.py [deleted file]

index 420b4d84b4001b3df38d1bf670a6ca23e901ae52..deb165a2722d70ec5f0c53a912ba3bcb83351662 100644 (file)
@@ -8,7 +8,7 @@ from openpower.decoder.power_enums import (
 
 import openpower.oppc.pc_lexer as pc_lexer
 import openpower.oppc.pc_parser as pc_parser
-import openpower.oppc.pc_util as pc_util
+import openpower.oppc.pc_pseudocode as pc_pseudocode
 
 
 def dedent(line):
@@ -21,7 +21,7 @@ def parse(parser, origin):
     origin = tuple(origin)
     tree = parser.parse(code="\n".join(origin))
     stream = io.StringIO()
-    for (level, line) in pc_util.pseudocode(tree):
+    for (level, line) in pc_pseudocode.pseudocode(tree):
         print(f"{' ' * 4 * level}{line}", file=stream)
     stream.seek(0)
     target = tuple(stream)
@@ -64,5 +64,5 @@ RA <- result[64-XLEN:63]
 """
 tree = parser.parse(code=code)
 print(tree)
-for (level, line) in pc_util.pseudocode(tree):
+for (level, line) in pc_pseudocode.pseudocode(tree):
     print(f"{' ' * 4 * level}{line}")
diff --git a/src/openpower/oppc/pc_pseudocode.py b/src/openpower/oppc/pc_pseudocode.py
new file mode 100644 (file)
index 0000000..76058ec
--- /dev/null
@@ -0,0 +1,406 @@
+import collections
+import contextlib
+import functools
+
+import mdis.dispatcher
+import mdis.visitor
+import mdis.walker
+
+import openpower.oppc.pc_ast as pc_ast
+
+
+class Hook(mdis.dispatcher.Hook):
+    def __call__(self, call):
+        hook = super().__call__(call)
+
+        class ConcreteHook(hook.__class__):
+            @functools.wraps(hook.__call__)
+            @contextlib.contextmanager
+            def __call__(self, dispatcher, node, *args, **kwargs):
+                return hook(dispatcher, node, *args, **kwargs)
+
+        return ConcreteHook(*tuple(self))
+
+
+class Code(list):
+    def __init__(self):
+        self.__level = 0
+        return super().__init__()
+
+    def __enter__(self):
+        self.__level += 1
+        return self
+
+    def __exit__(self, exc_type, exc_value, exc_traceback):
+        self.__level -= 1
+
+    def __str__(self):
+        if len(self) == 0:
+            raise ValueError("empty code")
+
+        lines = []
+        for (level, stmt) in self:
+            line = ((" " * level * 4) + stmt)
+            lines.append(line)
+
+        return "\n".join(lines)
+
+    def emit(self, stmt, level=0):
+        item = ((level + self.__level), stmt)
+        self.append(item)
+
+
+class PseudocodeVisitor(mdis.visitor.ContextVisitor):
+    def __init__(self, root):
+        self.__root = root
+        self.__code = collections.defaultdict(lambda: Code())
+
+        return super().__init__()
+
+    def __iter__(self):
+        yield from self.__code.items()
+
+    def __getitem__(self, node):
+        return self.__code[node]
+
+    @Hook(pc_ast.Scope)
+    def Scope(self, node):
+        yield node
+        if node is not self.__root:
+            with self[node]:
+                for subnode in node:
+                    for (level, stmt) in self[subnode]:
+                        self[node].emit(stmt=stmt, level=level)
+        else:
+            for subnode in node:
+                for (level, stmt) in self[subnode]:
+                    self[node].emit(stmt=stmt, level=level)
+
+    @Hook(pc_ast.Call)
+    def Call(self, node):
+        yield node
+        args = []
+        for subnode in node.args:
+            for (level, stmt) in self[subnode]:
+                assert level == 0
+                args.append(stmt)
+        args = ", ".join(args)
+        stmt = f"{node.name}({args})"
+        self[node].emit(stmt=stmt)
+
+    @Hook(pc_ast.AssignExpr, pc_ast.AssignIEAExpr)
+    def AssignExpr(self, node):
+        mapping = {
+            pc_ast.AssignExpr: "<-",
+            pc_ast.AssignIEAExpr: "<-iea",
+        }
+        yield node
+        lvalue = str(self[node.lvalue])
+        if (isinstance(node.lvalue, (pc_ast.GPR, pc_ast.FPR)) or
+                (isinstance(node.lvalue, (pc_ast.Subscript, pc_ast.RangeSubscript)) and
+                    isinstance(node.lvalue.subject, (pc_ast.GPR, pc_ast.FPR)))):
+            lvalue = lvalue.replace("(", "").replace(")", "")
+        rvalue = str(self[node.rvalue])
+
+        if isinstance(node.rvalue, pc_ast.IfExpr):
+            # All right, this deserves an explanation.
+            # We basically convert T <- C ? A : B into this code:
+            #
+            # if C then
+            #     T <- A
+            # else
+            #     T <- B
+            #
+            # To make things work, we must ensure that objects are unique.
+            # Otherwise we'll reuse the bogus code already produced before.
+            (body, orelse) = map(lambda node: node.clone(),
+                (node.rvalue.body[0], node.rvalue.orelse[0]))
+            body = pc_ast.Scope([node.__class__(lvalue=node.lvalue.clone(), rvalue=body)])
+            orelse = pc_ast.Scope([node.__class__(lvalue=node.lvalue.clone(), rvalue=orelse)])
+            tmpnode = node.rvalue.clone(body=body, orelse=orelse)
+            walker = mdis.walker.Walker()
+            traverse(root=tmpnode, visitor=self, walker=walker)
+            for (level, stmt) in self[tmpnode]:
+                self[node].emit(stmt=stmt, level=level)
+        else:
+            stmt = " ".join([
+                lvalue,
+                mapping[node.__class__],
+                rvalue,
+            ])
+            self[node].emit(stmt=stmt)
+
+    @Hook(pc_ast.BinaryExpr)
+    def BinaryExpr(self, node):
+        yield node
+        stmt = " ".join([
+            str(self[node.left]),
+            str(self[node.op]),
+            str(self[node.right]),
+        ])
+        self[node].emit(stmt=f"({stmt})")
+
+    @Hook(pc_ast.IfExpr)
+    def IfExpr(self, node):
+        yield node
+        stmt = " ".join([
+            "if",
+            str(self[node.test]),
+            "then",
+        ])
+        self[node].emit(stmt=stmt)
+        for (level, stmt) in self[node.body]:
+            self[node].emit(stmt=stmt, level=level)
+        if node.orelse:
+            self[node].emit("else")
+            for (level, stmt) in self[node.orelse]:
+                self[node].emit(stmt=stmt, level=level)
+
+    @Hook(pc_ast.ForExpr)
+    def ForExpr(self, node):
+        yield node
+        stmt = " ".join([
+            "for",
+            str(self[node.subject]),
+            "=",
+            str(self[node.start]),
+            "to",
+            str(self[node.end]),
+        ])
+        self[node].emit(stmt=stmt)
+        for (level, stmt) in self[node.body]:
+            self[node].emit(stmt=stmt, level=level)
+
+    @Hook(pc_ast.WhileExpr)
+    def WhileExpr(self, node):
+        yield node
+        stmt = " ".join([
+            "do",
+            "while",
+            str(self[node.test]),
+        ])
+        self[node].emit(stmt=stmt)
+        for (level, stmt) in self[node.body]:
+            self[node].emit(stmt=stmt, level=level)
+        if node.orelse:
+            self[node].emit("else")
+            for (level, stmt) in self[node.orelse]:
+                self[node].emit(stmt=stmt, level=level)
+
+    @Hook(pc_ast.RepeatExpr)
+    def RepeatExpr(self, node):
+        yield node
+        stmt = " ".join([
+            f"[{str(self[node.subject])}]",
+            "*",
+            str(self[node.times]),
+        ])
+        self[node].emit(stmt=f"({stmt})")
+
+    @Hook(pc_ast.SwitchExpr)
+    def SwitchExpr(self, node):
+        yield node
+        self[node].emit(f"switch({str(self[node.subject])})")
+        with self[node]:
+            for (level, stmt) in self[node.cases]:
+                self[node].emit(stmt=stmt, level=level)
+
+    @Hook(pc_ast.Cases)
+    def Cases(self, node):
+        yield node
+        for subnode in node:
+            for (level, stmt) in self[subnode]:
+                self[node].emit(stmt=stmt, level=level)
+
+    @Hook(pc_ast.Case)
+    def Case(self, node):
+        yield node
+        for (level, stmt) in self[node.labels]:
+            self[node].emit(stmt=stmt, level=level)
+        for (level, stmt) in self[node.body]:
+            self[node].emit(stmt=stmt, level=level)
+
+    @Hook(pc_ast.Labels)
+    def Labels(self, node):
+        yield node
+        if ((len(node) == 1) and isinstance(node[-1], pc_ast.DefaultLabel)):
+            stmt = "default:"
+        else:
+            labels = ", ".join(map(lambda label: str(self[label]), node))
+            stmt = f"case ({labels}):"
+        self[node].emit(stmt=stmt)
+
+    @Hook(pc_ast.Label)
+    def Label(self, node):
+        yield node
+        self[node].emit(stmt=str(node))
+
+    @Hook(pc_ast.DefaultLabel)
+    def DefaultLabel(self, node):
+        yield node
+        self[node].emit(stmt="default:")
+
+    @Hook(pc_ast.UnaryExpr)
+    def UnaryExpr(self, node):
+        yield node
+        stmt = "".join([
+            str(self[node.op]),
+            f"({str(self[node.value])})",
+        ])
+        self[node].emit(stmt=stmt)
+
+    @Hook(pc_ast.BinLiteral, pc_ast.DecLiteral, pc_ast.HexLiteral)
+    def Integer(self, node):
+        yield node
+        self[node].emit(stmt=str(node))
+
+    @Hook(pc_ast.StringLiteral)
+    def StringLiteral(self, node):
+        yield node
+        self[node].emit(stmt=f"'{str(node)}'")
+
+    @Hook(pc_ast.Symbol)
+    def Symbol(self, node):
+        yield node
+        self[node].emit(stmt=str(node))
+
+    @Hook(pc_ast.Attribute)
+    def Attribute(self, node):
+        yield node
+        stmt = ".".join([
+            str(self[node.subject]),
+            str(self[node.name]),
+        ])
+        self[node].emit(stmt=stmt)
+
+    @Hook(pc_ast.Not, pc_ast.Add, pc_ast.Sub,
+            pc_ast.Mul, pc_ast.MulS, pc_ast.MulU,
+            pc_ast.Div, pc_ast.DivT, pc_ast.Mod,
+            pc_ast.Sqrt,
+            pc_ast.Eq, pc_ast.NotEq,
+            pc_ast.Lt, pc_ast.Le, pc_ast.LtU,
+            pc_ast.Gt, pc_ast.Ge, pc_ast.GtU,
+            pc_ast.LShift, pc_ast.RShift,
+            pc_ast.AssignOp, pc_ast.AssignIEAOp,
+            pc_ast.BitAnd, pc_ast.BitOr, pc_ast.BitXor,
+            pc_ast.BitConcat)
+    def Op(self, node):
+        yield node
+        mapping = {
+            pc_ast.Not: "¬",
+            pc_ast.Add: "+",
+            pc_ast.Sub: "-",
+            pc_ast.Mul: "*",
+            pc_ast.MulS: "*si",
+            pc_ast.MulU: "*ui",
+            pc_ast.Div: "/",
+            pc_ast.DivT: "÷",
+            pc_ast.Mod: "%",
+            pc_ast.Sqrt: "√",
+            pc_ast.Eq: "=",
+            pc_ast.NotEq: "!=",
+            pc_ast.Lt: "<",
+            pc_ast.Le: "<=",
+            pc_ast.LtU: "<u",
+            pc_ast.Gt: ">",
+            pc_ast.Ge: ">=",
+            pc_ast.GtU: ">u",
+            pc_ast.LShift: "<<",
+            pc_ast.RShift: ">>",
+            pc_ast.AssignOp: "<-",
+            pc_ast.AssignIEAOp: "<-iea",
+            pc_ast.BitAnd: "&",
+            pc_ast.BitOr: "|",
+            pc_ast.BitXor: "^",
+            pc_ast.BitConcat: "||",
+        }
+        stmt = mapping[node.__class__]
+        self[node].emit(stmt=stmt)
+
+    @Hook(pc_ast.LParenthesis, pc_ast.RParenthesis,
+            pc_ast.LBracket, pc_ast.RBracket)
+    def BracketOrParenthesis(self, node):
+        yield node
+        mapping = {
+            pc_ast.LParenthesis: "(",
+            pc_ast.RParenthesis: ")",
+            pc_ast.LBracket: "[",
+            pc_ast.RBracket: "]",
+        }
+        stmt = mapping[node.__class__]
+        self[node].emit(stmt=stmt)
+
+    @Hook(pc_ast.Subscript)
+    def Subscript(self, node):
+        yield node
+        stmt = "".join([
+            str(self[node.subject]),
+            "[",
+            str(self[node.index]),
+            "]",
+        ])
+        self[node].emit(stmt=stmt)
+
+    @Hook(pc_ast.RangeSubscript)
+    def RangeSubscript(self, node):
+        yield node
+        stmt = "".join([
+            str(self[node.subject]),
+            "[",
+            str(self[node.start]),
+            ":",
+            str(self[node.end]),
+            "]",
+        ])
+        self[node].emit(stmt=stmt)
+
+    @Hook(pc_ast.Colon)
+    def Colon(self, node):
+        yield node
+        self[node].emit(stmt=":")
+
+    @Hook(pc_ast.Linebreak, pc_ast.Endmarker)
+    def Ignore(self, node):
+        yield node
+
+    @Hook(pc_ast.Keyword)
+    def Keyword(self, node):
+        yield node
+        self[node].emit(stmt=node.__doc__)
+
+    @Hook(pc_ast.Sequence)
+    def Sequence(self, node):
+        yield node
+        stmt = ",".join(map(lambda subnode: str(self[subnode]), node))
+        self[node].emit(stmt=f"({stmt})")
+
+    @Hook(pc_ast.Literal)
+    def Literal(self, node):
+        yield node
+        self[node].emit(stmt=str(node))
+
+    @Hook(pc_ast.GPR, pc_ast.FPR, pc_ast.GPRZero)
+    def Reg(self, node):
+        yield node
+        if isinstance(node, pc_ast.GPRZero):
+            self[node].emit(stmt=f"({str(node)}|0)")
+        else:
+            self[node].emit(stmt=f"({str(node)})")
+
+    @Hook(pc_ast.Node)
+    def Node(self, node):
+        raise NotImplementedError(type(node))
+
+
+def traverse(root, visitor, walker):
+    with visitor(root):
+        for node in walker(root):
+            traverse(root=node, visitor=visitor, walker=walker)
+
+
+def pseudocode(root):
+    walker = mdis.walker.Walker()
+    visitor = PseudocodeVisitor(root=root)
+    traverse(root=root, visitor=visitor, walker=walker)
+    for (level, stmt) in visitor[root]:
+        yield (level, stmt)
diff --git a/src/openpower/oppc/pc_util.py b/src/openpower/oppc/pc_util.py
deleted file mode 100644 (file)
index 76058ec..0000000
+++ /dev/null
@@ -1,406 +0,0 @@
-import collections
-import contextlib
-import functools
-
-import mdis.dispatcher
-import mdis.visitor
-import mdis.walker
-
-import openpower.oppc.pc_ast as pc_ast
-
-
-class Hook(mdis.dispatcher.Hook):
-    def __call__(self, call):
-        hook = super().__call__(call)
-
-        class ConcreteHook(hook.__class__):
-            @functools.wraps(hook.__call__)
-            @contextlib.contextmanager
-            def __call__(self, dispatcher, node, *args, **kwargs):
-                return hook(dispatcher, node, *args, **kwargs)
-
-        return ConcreteHook(*tuple(self))
-
-
-class Code(list):
-    def __init__(self):
-        self.__level = 0
-        return super().__init__()
-
-    def __enter__(self):
-        self.__level += 1
-        return self
-
-    def __exit__(self, exc_type, exc_value, exc_traceback):
-        self.__level -= 1
-
-    def __str__(self):
-        if len(self) == 0:
-            raise ValueError("empty code")
-
-        lines = []
-        for (level, stmt) in self:
-            line = ((" " * level * 4) + stmt)
-            lines.append(line)
-
-        return "\n".join(lines)
-
-    def emit(self, stmt, level=0):
-        item = ((level + self.__level), stmt)
-        self.append(item)
-
-
-class PseudocodeVisitor(mdis.visitor.ContextVisitor):
-    def __init__(self, root):
-        self.__root = root
-        self.__code = collections.defaultdict(lambda: Code())
-
-        return super().__init__()
-
-    def __iter__(self):
-        yield from self.__code.items()
-
-    def __getitem__(self, node):
-        return self.__code[node]
-
-    @Hook(pc_ast.Scope)
-    def Scope(self, node):
-        yield node
-        if node is not self.__root:
-            with self[node]:
-                for subnode in node:
-                    for (level, stmt) in self[subnode]:
-                        self[node].emit(stmt=stmt, level=level)
-        else:
-            for subnode in node:
-                for (level, stmt) in self[subnode]:
-                    self[node].emit(stmt=stmt, level=level)
-
-    @Hook(pc_ast.Call)
-    def Call(self, node):
-        yield node
-        args = []
-        for subnode in node.args:
-            for (level, stmt) in self[subnode]:
-                assert level == 0
-                args.append(stmt)
-        args = ", ".join(args)
-        stmt = f"{node.name}({args})"
-        self[node].emit(stmt=stmt)
-
-    @Hook(pc_ast.AssignExpr, pc_ast.AssignIEAExpr)
-    def AssignExpr(self, node):
-        mapping = {
-            pc_ast.AssignExpr: "<-",
-            pc_ast.AssignIEAExpr: "<-iea",
-        }
-        yield node
-        lvalue = str(self[node.lvalue])
-        if (isinstance(node.lvalue, (pc_ast.GPR, pc_ast.FPR)) or
-                (isinstance(node.lvalue, (pc_ast.Subscript, pc_ast.RangeSubscript)) and
-                    isinstance(node.lvalue.subject, (pc_ast.GPR, pc_ast.FPR)))):
-            lvalue = lvalue.replace("(", "").replace(")", "")
-        rvalue = str(self[node.rvalue])
-
-        if isinstance(node.rvalue, pc_ast.IfExpr):
-            # All right, this deserves an explanation.
-            # We basically convert T <- C ? A : B into this code:
-            #
-            # if C then
-            #     T <- A
-            # else
-            #     T <- B
-            #
-            # To make things work, we must ensure that objects are unique.
-            # Otherwise we'll reuse the bogus code already produced before.
-            (body, orelse) = map(lambda node: node.clone(),
-                (node.rvalue.body[0], node.rvalue.orelse[0]))
-            body = pc_ast.Scope([node.__class__(lvalue=node.lvalue.clone(), rvalue=body)])
-            orelse = pc_ast.Scope([node.__class__(lvalue=node.lvalue.clone(), rvalue=orelse)])
-            tmpnode = node.rvalue.clone(body=body, orelse=orelse)
-            walker = mdis.walker.Walker()
-            traverse(root=tmpnode, visitor=self, walker=walker)
-            for (level, stmt) in self[tmpnode]:
-                self[node].emit(stmt=stmt, level=level)
-        else:
-            stmt = " ".join([
-                lvalue,
-                mapping[node.__class__],
-                rvalue,
-            ])
-            self[node].emit(stmt=stmt)
-
-    @Hook(pc_ast.BinaryExpr)
-    def BinaryExpr(self, node):
-        yield node
-        stmt = " ".join([
-            str(self[node.left]),
-            str(self[node.op]),
-            str(self[node.right]),
-        ])
-        self[node].emit(stmt=f"({stmt})")
-
-    @Hook(pc_ast.IfExpr)
-    def IfExpr(self, node):
-        yield node
-        stmt = " ".join([
-            "if",
-            str(self[node.test]),
-            "then",
-        ])
-        self[node].emit(stmt=stmt)
-        for (level, stmt) in self[node.body]:
-            self[node].emit(stmt=stmt, level=level)
-        if node.orelse:
-            self[node].emit("else")
-            for (level, stmt) in self[node.orelse]:
-                self[node].emit(stmt=stmt, level=level)
-
-    @Hook(pc_ast.ForExpr)
-    def ForExpr(self, node):
-        yield node
-        stmt = " ".join([
-            "for",
-            str(self[node.subject]),
-            "=",
-            str(self[node.start]),
-            "to",
-            str(self[node.end]),
-        ])
-        self[node].emit(stmt=stmt)
-        for (level, stmt) in self[node.body]:
-            self[node].emit(stmt=stmt, level=level)
-
-    @Hook(pc_ast.WhileExpr)
-    def WhileExpr(self, node):
-        yield node
-        stmt = " ".join([
-            "do",
-            "while",
-            str(self[node.test]),
-        ])
-        self[node].emit(stmt=stmt)
-        for (level, stmt) in self[node.body]:
-            self[node].emit(stmt=stmt, level=level)
-        if node.orelse:
-            self[node].emit("else")
-            for (level, stmt) in self[node.orelse]:
-                self[node].emit(stmt=stmt, level=level)
-
-    @Hook(pc_ast.RepeatExpr)
-    def RepeatExpr(self, node):
-        yield node
-        stmt = " ".join([
-            f"[{str(self[node.subject])}]",
-            "*",
-            str(self[node.times]),
-        ])
-        self[node].emit(stmt=f"({stmt})")
-
-    @Hook(pc_ast.SwitchExpr)
-    def SwitchExpr(self, node):
-        yield node
-        self[node].emit(f"switch({str(self[node.subject])})")
-        with self[node]:
-            for (level, stmt) in self[node.cases]:
-                self[node].emit(stmt=stmt, level=level)
-
-    @Hook(pc_ast.Cases)
-    def Cases(self, node):
-        yield node
-        for subnode in node:
-            for (level, stmt) in self[subnode]:
-                self[node].emit(stmt=stmt, level=level)
-
-    @Hook(pc_ast.Case)
-    def Case(self, node):
-        yield node
-        for (level, stmt) in self[node.labels]:
-            self[node].emit(stmt=stmt, level=level)
-        for (level, stmt) in self[node.body]:
-            self[node].emit(stmt=stmt, level=level)
-
-    @Hook(pc_ast.Labels)
-    def Labels(self, node):
-        yield node
-        if ((len(node) == 1) and isinstance(node[-1], pc_ast.DefaultLabel)):
-            stmt = "default:"
-        else:
-            labels = ", ".join(map(lambda label: str(self[label]), node))
-            stmt = f"case ({labels}):"
-        self[node].emit(stmt=stmt)
-
-    @Hook(pc_ast.Label)
-    def Label(self, node):
-        yield node
-        self[node].emit(stmt=str(node))
-
-    @Hook(pc_ast.DefaultLabel)
-    def DefaultLabel(self, node):
-        yield node
-        self[node].emit(stmt="default:")
-
-    @Hook(pc_ast.UnaryExpr)
-    def UnaryExpr(self, node):
-        yield node
-        stmt = "".join([
-            str(self[node.op]),
-            f"({str(self[node.value])})",
-        ])
-        self[node].emit(stmt=stmt)
-
-    @Hook(pc_ast.BinLiteral, pc_ast.DecLiteral, pc_ast.HexLiteral)
-    def Integer(self, node):
-        yield node
-        self[node].emit(stmt=str(node))
-
-    @Hook(pc_ast.StringLiteral)
-    def StringLiteral(self, node):
-        yield node
-        self[node].emit(stmt=f"'{str(node)}'")
-
-    @Hook(pc_ast.Symbol)
-    def Symbol(self, node):
-        yield node
-        self[node].emit(stmt=str(node))
-
-    @Hook(pc_ast.Attribute)
-    def Attribute(self, node):
-        yield node
-        stmt = ".".join([
-            str(self[node.subject]),
-            str(self[node.name]),
-        ])
-        self[node].emit(stmt=stmt)
-
-    @Hook(pc_ast.Not, pc_ast.Add, pc_ast.Sub,
-            pc_ast.Mul, pc_ast.MulS, pc_ast.MulU,
-            pc_ast.Div, pc_ast.DivT, pc_ast.Mod,
-            pc_ast.Sqrt,
-            pc_ast.Eq, pc_ast.NotEq,
-            pc_ast.Lt, pc_ast.Le, pc_ast.LtU,
-            pc_ast.Gt, pc_ast.Ge, pc_ast.GtU,
-            pc_ast.LShift, pc_ast.RShift,
-            pc_ast.AssignOp, pc_ast.AssignIEAOp,
-            pc_ast.BitAnd, pc_ast.BitOr, pc_ast.BitXor,
-            pc_ast.BitConcat)
-    def Op(self, node):
-        yield node
-        mapping = {
-            pc_ast.Not: "¬",
-            pc_ast.Add: "+",
-            pc_ast.Sub: "-",
-            pc_ast.Mul: "*",
-            pc_ast.MulS: "*si",
-            pc_ast.MulU: "*ui",
-            pc_ast.Div: "/",
-            pc_ast.DivT: "÷",
-            pc_ast.Mod: "%",
-            pc_ast.Sqrt: "√",
-            pc_ast.Eq: "=",
-            pc_ast.NotEq: "!=",
-            pc_ast.Lt: "<",
-            pc_ast.Le: "<=",
-            pc_ast.LtU: "<u",
-            pc_ast.Gt: ">",
-            pc_ast.Ge: ">=",
-            pc_ast.GtU: ">u",
-            pc_ast.LShift: "<<",
-            pc_ast.RShift: ">>",
-            pc_ast.AssignOp: "<-",
-            pc_ast.AssignIEAOp: "<-iea",
-            pc_ast.BitAnd: "&",
-            pc_ast.BitOr: "|",
-            pc_ast.BitXor: "^",
-            pc_ast.BitConcat: "||",
-        }
-        stmt = mapping[node.__class__]
-        self[node].emit(stmt=stmt)
-
-    @Hook(pc_ast.LParenthesis, pc_ast.RParenthesis,
-            pc_ast.LBracket, pc_ast.RBracket)
-    def BracketOrParenthesis(self, node):
-        yield node
-        mapping = {
-            pc_ast.LParenthesis: "(",
-            pc_ast.RParenthesis: ")",
-            pc_ast.LBracket: "[",
-            pc_ast.RBracket: "]",
-        }
-        stmt = mapping[node.__class__]
-        self[node].emit(stmt=stmt)
-
-    @Hook(pc_ast.Subscript)
-    def Subscript(self, node):
-        yield node
-        stmt = "".join([
-            str(self[node.subject]),
-            "[",
-            str(self[node.index]),
-            "]",
-        ])
-        self[node].emit(stmt=stmt)
-
-    @Hook(pc_ast.RangeSubscript)
-    def RangeSubscript(self, node):
-        yield node
-        stmt = "".join([
-            str(self[node.subject]),
-            "[",
-            str(self[node.start]),
-            ":",
-            str(self[node.end]),
-            "]",
-        ])
-        self[node].emit(stmt=stmt)
-
-    @Hook(pc_ast.Colon)
-    def Colon(self, node):
-        yield node
-        self[node].emit(stmt=":")
-
-    @Hook(pc_ast.Linebreak, pc_ast.Endmarker)
-    def Ignore(self, node):
-        yield node
-
-    @Hook(pc_ast.Keyword)
-    def Keyword(self, node):
-        yield node
-        self[node].emit(stmt=node.__doc__)
-
-    @Hook(pc_ast.Sequence)
-    def Sequence(self, node):
-        yield node
-        stmt = ",".join(map(lambda subnode: str(self[subnode]), node))
-        self[node].emit(stmt=f"({stmt})")
-
-    @Hook(pc_ast.Literal)
-    def Literal(self, node):
-        yield node
-        self[node].emit(stmt=str(node))
-
-    @Hook(pc_ast.GPR, pc_ast.FPR, pc_ast.GPRZero)
-    def Reg(self, node):
-        yield node
-        if isinstance(node, pc_ast.GPRZero):
-            self[node].emit(stmt=f"({str(node)}|0)")
-        else:
-            self[node].emit(stmt=f"({str(node)})")
-
-    @Hook(pc_ast.Node)
-    def Node(self, node):
-        raise NotImplementedError(type(node))
-
-
-def traverse(root, visitor, walker):
-    with visitor(root):
-        for node in walker(root):
-            traverse(root=node, visitor=visitor, walker=walker)
-
-
-def pseudocode(root):
-    walker = mdis.walker.Walker()
-    visitor = PseudocodeVisitor(root=root)
-    traverse(root=root, visitor=visitor, walker=walker)
-    for (level, stmt) in visitor[root]:
-        yield (level, stmt)