#! /usr/bin/env python3 # This script generates crates/sre_engine/src/constants.rs from Lib/re/_constants.py. SCRIPT_NAME = "scripts/generate_sre_constants.py" def update_file(file, content): try: with open(file, "r") as fobj: if fobj.read() == content: return False except (OSError, ValueError): pass with open(file, "w") as fobj: fobj.write(content) return True sre_constants_header = f"""\ /* * Secret Labs' Regular Expression Engine * * regular expression matching engine * * Auto-generated by {SCRIPT_NAME} from * Lib/re/_constants.py. * * Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved. * * See the sre.c file for information on usage and redistribution. */ """ def dump_enum(d, enum_name, derives, strip_prefix=""): """Generate Rust enum definitions from a Python dictionary. Args: d (list): The list containing the enum variants. enum_name (str): The name of the enum to generate. derives (str): The derive attributes to include. strip_prefix (str, optional): A prefix to strip from the variant names. Defaults to "". Returns: list: A list of strings representing the enum definition. """ items = sorted(d) print(f"items is {items}") content = [f"{derives}\n"] content.append("#[repr(u32)]\n") content.append("#[allow(non_camel_case_types, clippy::upper_case_acronyms)]\n") content.append(f"pub enum {enum_name} {{\n") for i, item in enumerate(items): name = str(item).removeprefix(strip_prefix) content.append(f" {name} = {i},\n") content.append("}\n\n") return content def dump_bitflags(d, prefix, derives, struct_name, int_t): """Generate Rust bitflags definitions from a Python dictionary. Args: d (dict): The dictionary containing the bitflag variants. prefix (str): The prefix to strip from the variant names. derives (str): The derive attributes to include. struct_name (str): The name of the struct to generate. int_t (str): The integer type to use for the bitflags. Returns: list: A list of strings representing the bitflags definition. """ items = [(value, name) for name, value in d.items() if name.startswith(prefix)] content = ["bitflags! {\n"] content.append(f"{derives}\n") if derives else None content.append(f" pub struct {struct_name}: {int_t} {{\n") for value, name in sorted(items): name = str(name).removeprefix(prefix) content.append(f" const {name} = {value};\n") content.append(" }\n") content.append("}\n\n") return content def main( infile="Lib/re/_constants.py", outfile_constants="crates/sre_engine/src/constants.rs", ): ns = {} with open(infile) as fp: code = fp.read() exec(code, ns) content = [sre_constants_header] content.append("use bitflags::bitflags;\n\n") content.append(f"pub const SRE_MAGIC: usize = {ns['MAGIC']};\n") content.extend( dump_enum( ns["OPCODES"], "SreOpcode", "#[derive(num_enum::TryFromPrimitive, Debug, PartialEq, Eq)]", ) ) content.extend( dump_enum( ns["ATCODES"], "SreAtCode", "#[derive(num_enum::TryFromPrimitive, Debug, PartialEq, Eq)]", "AT_", ) ) content.extend( dump_enum( ns["CHCODES"], "SreCatCode", "#[derive(num_enum::TryFromPrimitive, Debug)]", "CATEGORY_", ) ) content.extend( dump_bitflags( ns, "SRE_FLAG_", "#[derive(Debug, PartialEq, Eq, Clone, Copy)]", "SreFlag", "u16", ) ) content.extend(dump_bitflags(ns, "SRE_INFO_", "", "SreInfo", "u32")) update_file(outfile_constants, "".join(content)) if __name__ == "__main__": import sys main(*sys.argv[1:])