From faca28cfd3c20d66fc084b2ec7eeb6850bb117ba Mon Sep 17 00:00:00 2001 From: Dmitry Selyutin Date: Mon, 8 Jan 2024 01:57:51 +0300 Subject: [PATCH] oppc: introduce openpower pseudocode --- src/openpower/oppc/.gitignore | 2 + src/openpower/oppc/__main__.py | 68 ++++ src/openpower/oppc/pc_ast.py | 550 ++++++++++++++++++++++++++++ src/openpower/oppc/pc_lexer.py | 569 +++++++++++++++++++++++++++++ src/openpower/oppc/pc_parser.py | 615 ++++++++++++++++++++++++++++++++ src/openpower/oppc/pc_util.py | 406 +++++++++++++++++++++ 6 files changed, 2210 insertions(+) create mode 100644 src/openpower/oppc/.gitignore create mode 100644 src/openpower/oppc/__main__.py create mode 100644 src/openpower/oppc/pc_ast.py create mode 100644 src/openpower/oppc/pc_lexer.py create mode 100644 src/openpower/oppc/pc_parser.py create mode 100644 src/openpower/oppc/pc_util.py diff --git a/src/openpower/oppc/.gitignore b/src/openpower/oppc/.gitignore new file mode 100644 index 00000000..b10f64eb --- /dev/null +++ b/src/openpower/oppc/.gitignore @@ -0,0 +1,2 @@ +parser.out +yacctab.py diff --git a/src/openpower/oppc/__main__.py b/src/openpower/oppc/__main__.py new file mode 100644 index 00000000..420b4d84 --- /dev/null +++ b/src/openpower/oppc/__main__.py @@ -0,0 +1,68 @@ +import glob +import io +import re + +from openpower.decoder.power_enums import ( + find_wiki_dir, +) + +import openpower.oppc.pc_lexer as pc_lexer +import openpower.oppc.pc_parser as pc_parser +import openpower.oppc.pc_util as pc_util + + +def dedent(line): + if line.startswith(" "): + return line[4:].rstrip() + return line.rstrip() + + +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): + print(f"{' ' * 4 * level}{line}", file=stream) + stream.seek(0) + target = tuple(stream) + return (origin, target) + + +lexer = pc_lexer.IndentLexer(debug=False) +parser = pc_parser.Parser(lexer=lexer) +pattern = re.compile(r"Pseudo-code:(.*?)(?:Special Registers Altered|Description):", re.DOTALL) +for path in []: # glob.glob(f"{find_wiki_dir()}/../isa/*.mdwn"): + with open(path, "r", encoding="UTF-8") as stream: + data = stream.read() + for origin in pattern.findall(data): + try: + (stage0, stage1) = parse(parser, map(dedent, origin.split("\n"))) + (stage2, stage3) = parse(parser, map(dedent, stage1)) + stage1 = tuple(map(dedent, stage1)) + stage3 = tuple(map(dedent, stage3)) + assert stage1 == stage2 and stage2 == stage3 + except AssertionError as exc: + print(stage0) + print(stage1) + print(stage3) + raise exc + except Exception as exc: + print(path) + print(origin) + raise exc + +code = """ +src <- [0]*64 +src[64-XLEN:63] <- (RS) +result <- [0]*64 +do i = 0 to 1 + n <- i * 32 + result[n+0:n+7] <- 0 + result[n+8:n+19] <- DPD_TO_BCD(src[n+12:n+21]) + result[n+20:n+31] <- DPD_TO_BCD(src[n+22:n+31]) +RA <- result[64-XLEN:63] +""" +tree = parser.parse(code=code) +print(tree) +for (level, line) in pc_util.pseudocode(tree): + print(f"{' ' * 4 * level}{line}") diff --git a/src/openpower/oppc/pc_ast.py b/src/openpower/oppc/pc_ast.py new file mode 100644 index 00000000..0a51e62f --- /dev/null +++ b/src/openpower/oppc/pc_ast.py @@ -0,0 +1,550 @@ +import copy +import dataclasses + + +class NodeMeta(type): + pass + + +class Node(metaclass=NodeMeta): + def __repr__(self): + return f"{hex(id(self))}@{self.__class__.__name__}()" + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (hex(id(self)) == id(other)) + + def clone(self): + return copy.deepcopy(self) + + +class TokenMeta(NodeMeta): + pass + + +class Token(Node, str, metaclass=TokenMeta): + def __new__(cls, value): + if isinstance(value, cls): + value = str(value) + if not isinstance(value, str): + raise ValueError(value) + + return super().__new__(cls, value) + + def __str__(self): + return super(Node, self).__str__() + + def __hash__(self): + return super(Node, self).__hash__() + + def __repr__(self): + return f"{hex(id(self))}@{self.__class__.__name__}({str(self)})" + + +class SequenceMeta(NodeMeta): + __typeid__ = Node + + def __new__(metacls, clsname, bases, ns, *, typeid=Node): + ns.setdefault("__typeid__", typeid) + + return super().__new__(metacls, clsname, bases, ns) + + +class Sequence(Node, tuple, metaclass=SequenceMeta): + def __new__(cls, iterable=tuple()): + def validate(item): + if not isinstance(item, cls.__typeid__): + raise ValueError(cls, item) + return item + + return super().__new__(cls, map(validate, iterable)) + + def __hash__(self): + return super(Node, self).__hash__() + + def __repr__(self): + return f"{hex(id(self))}@{self.__class__.__name__}({repr(list(self))})" + + +class Arguments(Sequence): + pass + + +class Scope(Sequence): + pass + + +class Module(Sequence): + pass + + +class DataclassMeta(NodeMeta): + def __new__(metacls, clsname, bases, ns): + cls = super().__new__(metacls, clsname, bases, ns) + wrap = dataclasses.dataclass(init=True, eq=False, unsafe_hash=True, frozen=False) + datacls = wrap(cls) + origin = datacls.__repr__ + datacls.__repr__ = lambda self: f"{hex(id(self))}@{origin(self)}" + + return datacls + + +class Dataclass(Node, metaclass=DataclassMeta): + def __post_init__(self): + for field in dataclasses.fields(self): + key = field.name + value = getattr(self, key) + if not isinstance(value, field.type): + raise ValueError(f"{self.__class__.__name__}.{key}: {value!r}") + + def clone(self, **kwargs): + return copy.deepcopy(dataclasses.replace(self, **kwargs)) + + +class LiteralMeta(TokenMeta): + def __new__(metacls, clsname, bases, ns, *, choices=()): + ns.setdefault("__choices__", choices) + + return super().__new__(metacls, clsname, bases, ns) + + def __iter__(cls): + yield from cls.__choices__ + + +class Literal(Token, metaclass=LiteralMeta): + __choices__ = () + + def __new__(cls, value): + choices = cls.__choices__ + if isinstance(value, Token): + value = str(value) + if choices and value not in choices: + raise ValueError(value) + + return super().__new__(cls, value) + + +class GPR(Literal, choices=("RA", "RA0", "RB", "RB0", "RC", "RC0", "RS", "RSp", "RT", "RTp")): + pass + + +class FPR(Literal, choices=("FPR", "FRA", "FRAp", "FRB", "FRBp", "FRC", "FRS", "FRSp", "FRT", "FRTp")): + pass + + +class CR3(Literal, choices=("BF", "BFA")): + pass + + +class CR5(Literal, choices=("BA", "BB", "BC", "BI", "BT")): + pass + + +class XER(Literal, choices=("OV", "OV32", "CA", "CA32", "SO")): + pass + + +class IntLiteral(Literal): + pass + + +class BinLiteral(IntLiteral): + r"""0b[01_]+""" + pass + + +class HexLiteral(IntLiteral): + r"""0x[0-9A-Fa-f_]+""" + pass + + +class DecLiteral(IntLiteral): + r"""(\d+(\.\d*)?|\.\d+)([eE][-+]? \d+)?""" + pass + + +class Symbol(Token): + r"""[A-Za-z_]+[A-Za-z0-9_]*""" + pass + + +class Attribute(Dataclass): + name: Symbol + subject: Node = Node() + + +class StringLiteral(Literal): + __STRING_ESCAPE = r"""(\\[0-9a-zA-Z._~!=&\^\-\\?'"])""" + __STRING_CHAR = (r"""([^"\\\n]|""" + __STRING_ESCAPE + ")") + __STRING = ("[\"']" + __STRING_CHAR + "*" + "[\"']") + + __doc__ = __STRING + + def __repr__(self): + return f"{hex(id(self))}@{self.__class__.__name__}({self.__str__()!r})" + + +class Whitespace(Token): + r"""[^\S\r\n]+""" + + +class Linebreak(Token): + r"""[\r\n]+""" + + +class Comment(Token): + r"""[ ]*(?:\043|