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