mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
Thoroughly refactor the cases generator (#107151)
This mostly extracts a whole bunch of stuff out of generate_cases.py into separate files, but there are a few other things going on here. - analysis.py: `Analyzer` etc. - instructions.py: `Instruction` etc. - flags.py: `InstructionFlags`, `variable_used`, `variable_used_unspecialized` - formatting.py: `Formatter` etc. - Rename parser.py to parsing.py, to avoid conflict with stdlib parser.py - Blackify most things - Fix most mypy errors - Remove output filenames from Generator state, add them to `write_instructions()` etc. - Fix unit tests
This commit is contained in:
parent
ff5f94b72c
commit
032f480909
7 changed files with 1304 additions and 1169 deletions
102
Tools/cases_generator/flags.py
Normal file
102
Tools/cases_generator/flags.py
Normal file
|
@ -0,0 +1,102 @@
|
|||
import dataclasses
|
||||
|
||||
from formatting import Formatter
|
||||
import lexer as lx
|
||||
import parsing
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class InstructionFlags:
|
||||
"""Construct and manipulate instruction flags"""
|
||||
|
||||
HAS_ARG_FLAG: bool
|
||||
HAS_CONST_FLAG: bool
|
||||
HAS_NAME_FLAG: bool
|
||||
HAS_JUMP_FLAG: bool
|
||||
HAS_FREE_FLAG: bool
|
||||
HAS_LOCAL_FLAG: bool
|
||||
|
||||
def __post_init__(self):
|
||||
self.bitmask = {name: (1 << i) for i, name in enumerate(self.names())}
|
||||
|
||||
@staticmethod
|
||||
def fromInstruction(instr: parsing.Node):
|
||||
|
||||
has_free = (
|
||||
variable_used(instr, "PyCell_New")
|
||||
or variable_used(instr, "PyCell_GET")
|
||||
or variable_used(instr, "PyCell_SET")
|
||||
)
|
||||
|
||||
return InstructionFlags(
|
||||
HAS_ARG_FLAG=variable_used(instr, "oparg"),
|
||||
HAS_CONST_FLAG=variable_used(instr, "FRAME_CO_CONSTS"),
|
||||
HAS_NAME_FLAG=variable_used(instr, "FRAME_CO_NAMES"),
|
||||
HAS_JUMP_FLAG=variable_used(instr, "JUMPBY"),
|
||||
HAS_FREE_FLAG=has_free,
|
||||
HAS_LOCAL_FLAG=(
|
||||
variable_used(instr, "GETLOCAL") or variable_used(instr, "SETLOCAL")
|
||||
)
|
||||
and not has_free,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def newEmpty():
|
||||
return InstructionFlags(False, False, False, False, False, False)
|
||||
|
||||
def add(self, other: "InstructionFlags") -> None:
|
||||
for name, value in dataclasses.asdict(other).items():
|
||||
if value:
|
||||
setattr(self, name, value)
|
||||
|
||||
def names(self, value=None):
|
||||
if value is None:
|
||||
return dataclasses.asdict(self).keys()
|
||||
return [n for n, v in dataclasses.asdict(self).items() if v == value]
|
||||
|
||||
def bitmap(self) -> int:
|
||||
flags = 0
|
||||
for name in self.names():
|
||||
if getattr(self, name):
|
||||
flags |= self.bitmask[name]
|
||||
return flags
|
||||
|
||||
@classmethod
|
||||
def emit_macros(cls, out: Formatter):
|
||||
flags = cls.newEmpty()
|
||||
for name, value in flags.bitmask.items():
|
||||
out.emit(f"#define {name} ({value})")
|
||||
|
||||
for name, value in flags.bitmask.items():
|
||||
out.emit(
|
||||
f"#define OPCODE_{name[:-len('_FLAG')]}(OP) "
|
||||
f"(_PyOpcode_opcode_metadata[OP].flags & ({name}))"
|
||||
)
|
||||
|
||||
|
||||
def variable_used(node: parsing.Node, name: str) -> bool:
|
||||
"""Determine whether a variable with a given name is used in a node."""
|
||||
return any(
|
||||
token.kind == "IDENTIFIER" and token.text == name for token in node.tokens
|
||||
)
|
||||
|
||||
|
||||
def variable_used_unspecialized(node: parsing.Node, name: str) -> bool:
|
||||
"""Like variable_used(), but skips #if ENABLE_SPECIALIZATION blocks."""
|
||||
tokens: list[lx.Token] = []
|
||||
skipping = False
|
||||
for i, token in enumerate(node.tokens):
|
||||
if token.kind == "MACRO":
|
||||
text = "".join(token.text.split())
|
||||
# TODO: Handle nested #if
|
||||
if text == "#if":
|
||||
if (
|
||||
i + 1 < len(node.tokens)
|
||||
and node.tokens[i + 1].text == "ENABLE_SPECIALIZATION"
|
||||
):
|
||||
skipping = True
|
||||
elif text in ("#else", "#endif"):
|
||||
skipping = False
|
||||
if not skipping:
|
||||
tokens.append(token)
|
||||
return any(token.kind == "IDENTIFIER" and token.text == name for token in tokens)
|
Loading…
Add table
Add a link
Reference in a new issue