1#!/usr/bin/env python3 2# coding=utf-8 3########################################################################## 4# 5# enums2names - Parse and convert enums to translator code 6# (C) Copyright 2021 Matti 'ccr' Hämäläinen <[email protected]> 7# 8# Permission is hereby granted, free of charge, to any person obtaining a 9# copy of this software and associated documentation files (the 10# "Software"), to deal in the Software without restriction, including 11# without limitation the rights to use, copy, modify, merge, publish, 12# distribute, sub license, and/or sell copies of the Software, and to 13# permit persons to whom the Software is furnished to do so, subject to 14# the following conditions: 15# 16# The above copyright notice and this permission notice (including the 17# next paragraph) shall be included in all copies or substantial portions 18# of the Software. 19# 20# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 23# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 24# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27# 28########################################################################## 29 30import sys 31import os.path 32import re 33import signal 34import argparse 35import textwrap 36 37assert sys.version_info >= (3, 6) 38 39 40# 41# List of enums we wish to include in output. 42# NOTE: This needs to be updated if such enums are added. 43# 44lst_enum_include = [ 45 "pipe_texture_target", 46 "pipe_shader_cap", 47 "pipe_shader_ir", 48 "pipe_map_flags", 49 "pipe_cap", 50 "pipe_capf", 51 "pipe_compute_cap", 52 "pipe_video_cap", 53 "pipe_video_profile", 54 "pipe_video_entrypoint", 55 "pipe_video_vpp_orientation", 56 "pipe_video_vpp_blend_mode", 57 "pipe_resource_param", 58 "pipe_fd_type", 59 "pipe_blendfactor", 60 "pipe_blend_func", 61 "pipe_logicop", 62] 63 64 65### 66### Utility functions 67### 68## Fatal error handler 69def pkk_fatal(smsg): 70 print("ERROR: "+ smsg) 71 sys.exit(1) 72 73 74## Handler for SIGINT signals 75def pkk_signal_handler(signal, frame): 76 print("\nQuitting due to SIGINT / Ctrl+C!") 77 sys.exit(1) 78 79 80## Argument parser subclass 81class PKKArgumentParser(argparse.ArgumentParser): 82 def print_help(self): 83 print("enums2names - Parse and convert enums to translator code\n" 84 "(C) Copyright 2021 Matti 'ccr' Hämäläinen <[email protected]>\n") 85 super().print_help() 86 87 def error(self, msg): 88 self.print_help() 89 print(f"\nERROR: {msg}", file=sys.stderr) 90 sys.exit(2) 91 92 93def pkk_get_argparser(): 94 optparser = PKKArgumentParser( 95 usage="%(prog)s [options] <infile|->\n" 96 "example: %(prog)s ../../include/pipe/p_defines.h -C tr_util.c -H tr_util.h" 97 ) 98 99 optparser.add_argument("in_files", 100 type=str, 101 metavar="infiles", 102 nargs="+", 103 help="path to input header files (or '-' for stdin)") 104 105 optparser.add_argument("-C", 106 type=str, 107 metavar="outfile", 108 dest="out_source", 109 help="output C source file") 110 111 optparser.add_argument("-H", 112 type=str, 113 metavar="outfile", 114 dest="out_header", 115 help="output C header file") 116 117 optparser.add_argument("-I", 118 type=str, 119 metavar="include", 120 dest="include_file", 121 help="include file / path used for C source output") 122 123 return optparser 124 125 126class PKKHeaderParser: 127 128 def __init__(self, nfilename): 129 self.filename = nfilename 130 self.enums = {} 131 self.state = 0 132 self.nline = 0 133 self.mdata = [] 134 self.start = 0 135 self.name = None 136 self.in_multiline_comment = False 137 138 def error(self, msg): 139 pkk_fatal(f"{self.filename}:{self.nline} : {msg}") 140 141 def parse_line(self, sline: str): 142 start = sline.find('/*') 143 end = sline.find('*/') 144 if not self.in_multiline_comment and start >= 0: 145 if end >= 0: 146 assert end > start 147 sline = sline[:start] + sline[end + 2:] 148 else: 149 sline = sline[:start] 150 self.in_multiline_comment = True 151 elif self.in_multiline_comment and end >= 0: 152 self.in_multiline_comment = False 153 sline = sline[end + 2:] 154 elif self.in_multiline_comment: 155 return 156 # A kingdom for Py3.8 := operator ... 157 smatch = re.match(r'^enum\s+([A-Za-z0-9_]+)\s+.*;', sline) 158 if smatch: 159 pass 160 else: 161 smatch = re.match(r'^enum\s+([A-Za-z0-9_]+)', sline) 162 if smatch: 163 stmp = smatch.group(1) 164 165 if self.state != 0: 166 self.error(f"enum '{stmp}' starting inside another enum '{self.name}'") 167 168 self.name = stmp 169 self.state = 1 170 self.start = self.nline 171 self.mdata = [] 172 else: 173 smatch = re.match(r'^}(\s*|\s*[A-Z][A-Z_]+\s*);', sline) 174 if smatch: 175 if self.state == 1: 176 if self.name in self.enums: 177 self.error("duplicate enum definition '{}', lines {} - {} vs {} - {}".format( 178 self.name, self.enums[self.name]["start"], self.enums[self.name]["end"], 179 self.start, self.nline)) 180 181 self.enums[self.name] = { 182 "data": self.mdata, 183 "start": self.start, 184 "end": self.nline 185 } 186 187 self.state = 0 188 189 elif self.state == 1: 190 smatch = re.match(r'([A-Za-z0-9_]+)\s*=\s*(.+)\s*,?', sline) 191 if smatch: 192 self.mdata.append(smatch.group(1)) 193 else: 194 smatch = re.match(r'([A-Za-z0-9_]+)\s*,?', sline) 195 if smatch: 196 self.mdata.append(smatch.group(1)) 197 198 def parse_file(self, fh): 199 self.nline = 0 200 for line in fh: 201 self.nline += 1 202 self.parse_line(line.strip()) 203 204 return self.enums 205 206 207def pkk_output_header(fh): 208 prototypes = [f"const char *\n" 209 f"tr_util_{name}_name(enum {name} value);\n" for name in lst_enum_include] 210 211 print(textwrap.dedent("""\ 212 /* 213 * File generated with {program}, please do not edit manually. 214 */ 215 #ifndef {include_header_guard} 216 #define {include_header_guard} 217 218 219 #include "pipe/p_defines.h" 220 #include "pipe/p_video_enums.h" 221 222 223 #ifdef __cplusplus 224 extern "C" {{ 225 #endif 226 227 {prototypes} 228 229 #ifdef __cplusplus 230 }} 231 #endif 232 233 #endif /* {include_header_guard} */\ 234 """).format( 235 program=pkk_progname, 236 include_header_guard=re.sub(r'[^A-Z]', '_', os.path.basename(pkk_cfg.out_header).upper()), 237 prototypes="".join(prototypes) 238 ), file=fh) 239 240 241def pkk_output_source(fh): 242 if pkk_cfg.include_file == None: 243 pkk_fatal("Output C source enabled, but include file is not set (-I option).") 244 245 print(textwrap.dedent("""\ 246 /* 247 * File generated with {program}, please do not edit manually. 248 */ 249 #include "{include_file}" 250 """).format( 251 program=pkk_progname, 252 include_file=pkk_cfg.include_file, 253 ), file=fh) 254 255 for name in lst_enum_include: 256 cases = [f" case {eid}: return \"{eid}\";\n" 257 for eid in enums[name]["data"]] 258 259 print(textwrap.dedent("""\ 260 261 const char * 262 tr_util_{name}_name(enum {name} value) 263 {{ 264 switch (value) {{ 265 {cases} 266 default: return "{ucname}_UNKNOWN"; 267 }} 268 }} 269 """).format( 270 name=name, 271 ucname=name.upper(), 272 cases="".join(cases) 273 ), file=fh) 274 275### 276### Main program starts 277### 278if __name__ == "__main__": 279 signal.signal(signal.SIGINT, pkk_signal_handler) 280 281 ### Parse arguments 282 pkk_progname = sys.argv[0] 283 optparser = pkk_get_argparser() 284 pkk_cfg = optparser.parse_args() 285 286 ### Parse input files 287 enums = {} 288 for file in pkk_cfg.in_files: 289 hdrparser = PKKHeaderParser(file) 290 291 try: 292 if file != "-": 293 with open(file, "r", encoding="UTF-8") as fh: 294 enums.update(hdrparser.parse_file(fh)) 295 else: 296 enums.update(hdrparser.parse_file(sys.stdin)) 297 break 298 except OSError as e: 299 pkk_fatal(str(e)) 300 301 ### Check if any of the required enums are missing 302 errors = False 303 for name in lst_enum_include: 304 if name not in enums: 305 print(f"ERROR: Missing enum '{name}'!") 306 errors = True 307 308 if errors: 309 pkk_fatal(f"Errors in input. Edit this script ({pkk_progname}) to add/remove included enums.") 310 311 ### Perform output 312 if pkk_cfg.out_header: 313 with open(pkk_cfg.out_header, "w", encoding="UTF-8") as fh: 314 pkk_output_header(fh) 315 316 if pkk_cfg.out_source: 317 with open(pkk_cfg.out_source, "w", encoding="UTF-8") as fh: 318 pkk_output_source(fh) 319