""" Generate Lib/_opcode_metadata.py for RustPython bytecode. This file generates opcode metadata that is compatible with CPython 3.13. """ import itertools import pathlib import re import typing ROOT = pathlib.Path(__file__).parents[1] BYTECODE_FILE = ( ROOT / "crates" / "compiler-core" / "src" / "bytecode" / "instruction.rs" ) OPCODE_METADATA_FILE = ROOT / "Lib" / "_opcode_metadata.py" class Opcode(typing.NamedTuple): rust_name: str id: int @property def cpython_name(self) -> str: name = re.sub(r"(?<=[a-z0-9])([A-Z])", r"_\1", self.rust_name) return re.sub(r"(\D)(\d+)$", r"\1_\2", name).upper() @classmethod def from_str(cls, body: str): raw_variants = re.split(r"(\d+),", body.strip()) raw_variants.remove("") for raw_name, raw_id in itertools.batched(raw_variants, 2): name = re.findall(r"\b[A-Z][A-Za-z]*\d*\b(?=\s*[\({=])", raw_name)[0] yield cls(rust_name=name.strip(), id=int(raw_id)) def __lt__(self, other: typing.Self) -> bool: return self.id < other.id def extract_enum_body(contents: str, enum_name: str) -> str: res = re.search(f"pub enum {enum_name} " + r"\{(.+?)\n\}", contents, re.DOTALL) if not res: raise ValueError(f"Could not find {enum_name} enum") return "\n".join( line.split("//")[0].strip() # Remove any comment. i.e. "foo // some comment" for line in res.group(1).splitlines() if not line.strip().startswith("//") # Ignore comment lines ) contents = BYTECODE_FILE.read_text() enum_body = "\n".join( extract_enum_body(contents, enum_name) for enum_name in ("Instruction", "PseudoInstruction") ) opcodes = list(Opcode.from_str(enum_body)) # Generate the output file output = """# This file is generated by scripts/generate_opcode_metadata.py # for RustPython bytecode format (CPython 3.13 compatible opcode numbers). # Do not edit! _specializations = {} _specialized_opmap = {} opmap = { """ for opcode in sorted(opcodes): output += f" '{opcode.cpython_name}': {opcode.id},\n" output += """} # CPython 3.13 compatible: opcodes < 44 have no argument HAVE_ARGUMENT = 44 MIN_INSTRUMENTED_OPCODE = 236 """ OPCODE_METADATA_FILE.write_text(output)