1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5from __future__ import absolute_import
6import re
7import textwrap
8import string
9
10comment_re = re.compile(r"//[^\n]*\n|/\*.*\*/", re.S)
11decl_re = re.compile(
12    r"""^(.+)\s+        # type
13                         (\w+)\s*        # name
14                         (?:\((.*)\))?$  # optional param tys
15                         """,
16    re.X | re.S,
17)
18
19
20def read_decls(filename):
21    """Parse & yield C-style decls from an input file"""
22    with open(filename, "r") as fd:
23        # Strip comments from the source text.
24        text = comment_re.sub("", fd.read())
25
26        # Parse individual declarations.
27        raw_decls = [d.strip() for d in text.split(";") if d.strip()]
28        for raw in raw_decls:
29            match = decl_re.match(raw)
30            if match is None:
31                raise "Invalid decl: %s" % raw
32
33            ty, name, params = match.groups()
34            if params is not None:
35                params = [a.strip() for a in params.split(",") if a.strip()]
36            yield ty, name, params
37
38
39def generate(fd, consts_path, unicodes_path, template_path, compiler):
40    # Parse the template
41    with open(template_path, "r") as template_fd:
42        template = string.Template(template_fd.read())
43
44    decls = ""
45
46    # Each constant should be saved to a temporary, and then re-assigned to a
47    # constant with the correct name, allowing the value to be determined by
48    # the actual definition.
49    for ty, name, args in read_decls(consts_path):
50        assert args is None, "parameters in const decl!"
51
52        decls += textwrap.dedent(
53            """
54            #ifdef {name}
55            constexpr {ty} _tmp_{name} = {name};
56            #undef {name}
57            constexpr {ty} {name} = _tmp_{name};
58            #endif
59            """.format(
60                ty=ty, name=name
61            )
62        )
63
64    # Each unicode declaration defines a static inline function with the
65    # correct types which calls the 'A' or 'W'-suffixed versions of the
66    # function. Full types are required here to ensure that '0' to 'nullptr'
67    # coersions are preserved.
68    for ty, name, args in read_decls(unicodes_path):
69        assert args is not None, "argument list required for unicode decl"
70
71        # Parameter & argument string list
72        params = ", ".join("%s a%d" % (ty, i) for i, ty in enumerate(args))
73        args = ", ".join("a%d" % i for i in range(len(args)))
74
75        decls += textwrap.dedent(
76            """
77            #ifdef {name}
78            #undef {name}
79            static inline {ty} WINAPI
80            {name}({params})
81            #ifdef UNICODE
82            {{
83              return {name}W({args});
84            }}
85            #else
86            = delete;
87            #endif
88            #endif
89            """.format(
90                ty=ty, name=name, params=params, args=args
91            )
92        )
93
94    # Write out the resulting file
95    fd.write(template.substitute(decls=decls))
96