• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <ccr@tnsp.org>
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_compute_cap",
50    "pipe_video_cap",
51    "pipe_video_profile",
52    "pipe_video_entrypoint",
53    "pipe_video_vpp_orientation",
54    "pipe_video_vpp_blend_mode",
55    "pipe_resource_param",
56    "pipe_fd_type",
57    "pipe_blendfactor",
58    "pipe_blend_func",
59    "pipe_logicop",
60]
61
62
63###
64### Utility functions
65###
66## Fatal error handler
67def pkk_fatal(smsg):
68    print("ERROR: "+ smsg)
69    sys.exit(1)
70
71
72## Handler for SIGINT signals
73def pkk_signal_handler(signal, frame):
74    print("\nQuitting due to SIGINT / Ctrl+C!")
75    sys.exit(1)
76
77
78## Argument parser subclass
79class PKKArgumentParser(argparse.ArgumentParser):
80    def print_help(self):
81        print("enums2names - Parse and convert enums to translator code\n"
82        "(C) Copyright 2021 Matti 'ccr' Hämäläinen <ccr@tnsp.org>\n")
83        super().print_help()
84
85    def error(self, msg):
86        self.print_help()
87        print(f"\nERROR: {msg}", file=sys.stderr)
88        sys.exit(2)
89
90
91def pkk_get_argparser():
92    optparser = PKKArgumentParser(
93        usage="%(prog)s [options] <infile|->\n"
94        "example: %(prog)s ../../include/pipe/p_defines.h -C tr_util.c -H tr_util.h"
95        )
96
97    optparser.add_argument("in_files",
98        type=str,
99        metavar="infiles",
100        nargs="+",
101        help="path to input header files (or '-' for stdin)")
102
103    optparser.add_argument("-C",
104        type=str,
105        metavar="outfile",
106        dest="out_source",
107        help="output C source file")
108
109    optparser.add_argument("-H",
110        type=str,
111        metavar="outfile",
112        dest="out_header",
113        help="output C header file")
114
115    optparser.add_argument("-I",
116        type=str,
117        metavar="include",
118        dest="include_file",
119        help="include file / path used for C source output")
120
121    return optparser
122
123
124class PKKHeaderParser:
125
126    def __init__(self, nfilename):
127        self.filename = nfilename
128        self.enums = {}
129        self.state = 0
130        self.nline = 0
131        self.mdata = []
132        self.start = 0
133        self.name = None
134        self.in_multiline_comment = False
135
136    def error(self, msg):
137        pkk_fatal(f"{self.filename}:{self.nline} : {msg}")
138
139    def parse_line(self, sline: str):
140        start = sline.find('/*')
141        end = sline.find('*/')
142        if not self.in_multiline_comment and start >= 0:
143            if end >= 0:
144                assert end > start
145                sline = sline[:start] + sline[end + 2:]
146            else:
147                sline = sline[:start]
148                self.in_multiline_comment = True
149        elif self.in_multiline_comment and end >= 0:
150            self.in_multiline_comment = False
151            sline = sline[end + 2:]
152        elif self.in_multiline_comment:
153            return
154        # A kingdom for Py3.8 := operator ...
155        smatch = re.match(r'^enum\s+([A-Za-z0-9_]+)\s+.*;', sline)
156        if smatch:
157            pass
158        else:
159            smatch = re.match(r'^enum\s+([A-Za-z0-9_]+)', sline)
160            if smatch:
161                stmp = smatch.group(1)
162
163                if self.state != 0:
164                    self.error(f"enum '{stmp}' starting inside another enum '{self.name}'")
165
166                self.name = stmp
167                self.state = 1
168                self.start = self.nline
169                self.mdata = []
170            else:
171                smatch = re.match(r'^}(\s*|\s*[A-Z][A-Z_]+\s*);', sline)
172                if smatch:
173                    if self.state == 1:
174                        if self.name in self.enums:
175                            self.error("duplicate enum definition '{}', lines {} - {} vs {} - {}".format(
176                            self.name, self.enums[self.name]["start"], self.enums[self.name]["end"],
177                            self.start, self.nline))
178
179                        self.enums[self.name] = {
180                            "data": self.mdata,
181                            "start": self.start,
182                            "end": self.nline
183                        }
184
185                    self.state = 0
186
187                elif self.state == 1:
188                    smatch = re.match(r'([A-Za-z0-9_]+)\s*=\s*(.+)\s*,?', sline)
189                    if smatch:
190                        self.mdata.append(smatch.group(1))
191                    else:
192                        smatch = re.match(r'([A-Za-z0-9_]+)\s*,?', sline)
193                        if smatch:
194                            self.mdata.append(smatch.group(1))
195
196    def parse_file(self, fh):
197        self.nline = 0
198        for line in fh:
199            self.nline += 1
200            self.parse_line(line.strip())
201
202        return self.enums
203
204
205def pkk_output_header(fh):
206    prototypes = [f"const char *\n"
207        f"tr_util_{name}_name(enum {name} value);\n" for name in lst_enum_include]
208
209    print(textwrap.dedent("""\
210        /*
211         * File generated with {program}, please do not edit manually.
212         */
213        #ifndef {include_header_guard}
214        #define {include_header_guard}
215
216
217        #include "pipe/p_defines.h"
218        #include "pipe/p_video_enums.h"
219
220
221        #ifdef __cplusplus
222        extern "C" {{
223        #endif
224
225        {prototypes}
226
227        #ifdef __cplusplus
228        }}
229        #endif
230
231        #endif /* {include_header_guard} */\
232        """).format(
233            program=pkk_progname,
234            include_header_guard=re.sub(r'[^A-Z]', '_', os.path.basename(pkk_cfg.out_header).upper()),
235            prototypes="".join(prototypes)
236            ), file=fh)
237
238
239def pkk_output_source(fh):
240    if pkk_cfg.include_file == None:
241        pkk_fatal("Output C source enabled, but include file is not set (-I option).")
242
243    print(textwrap.dedent("""\
244        /*
245         * File generated with {program}, please do not edit manually.
246         */
247        #include "{include_file}"
248        """).format(
249            program=pkk_progname,
250            include_file=pkk_cfg.include_file,
251            ), file=fh)
252
253    for name in lst_enum_include:
254        cases = [f"      case {eid}: return \"{eid}\";\n"
255            for eid in enums[name]["data"]]
256
257        print(textwrap.dedent("""\
258
259            const char *
260            tr_util_{name}_name(enum {name} value)
261            {{
262               switch (value) {{
263            {cases}
264                  default: return "{ucname}_UNKNOWN";
265               }}
266            }}
267            """).format(
268                name=name,
269                ucname=name.upper(),
270                cases="".join(cases)
271                ), file=fh)
272
273###
274### Main program starts
275###
276if __name__ == "__main__":
277    signal.signal(signal.SIGINT, pkk_signal_handler)
278
279    ### Parse arguments
280    pkk_progname = sys.argv[0]
281    optparser = pkk_get_argparser()
282    pkk_cfg = optparser.parse_args()
283
284    ### Parse input files
285    enums = {}
286    for file in pkk_cfg.in_files:
287        hdrparser = PKKHeaderParser(file)
288
289        try:
290            if file != "-":
291                with open(file, "r", encoding="UTF-8") as fh:
292                    enums.update(hdrparser.parse_file(fh))
293            else:
294                enums.update(hdrparser.parse_file(sys.stdin))
295                break
296        except OSError as e:
297            pkk_fatal(str(e))
298
299    ### Check if any of the required enums are missing
300    errors = False
301    for name in lst_enum_include:
302        if name not in enums:
303            print(f"ERROR: Missing enum '{name}'!")
304            errors = True
305
306    if errors:
307        pkk_fatal(f"Errors in input. Edit this script ({pkk_progname}) to add/remove included enums.")
308
309    ### Perform output
310    if pkk_cfg.out_header:
311        with open(pkk_cfg.out_header, "w", encoding="UTF-8") as fh:
312            pkk_output_header(fh)
313
314    if pkk_cfg.out_source:
315        with open(pkk_cfg.out_source, "w", encoding="UTF-8") as fh:
316            pkk_output_source(fh)
317