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