1fd4e5da5Sopenharmony_ci#!/usr/bin/env python3
2fd4e5da5Sopenharmony_ci# Copyright (c) 2017 Google Inc.
3fd4e5da5Sopenharmony_ci
4fd4e5da5Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
5fd4e5da5Sopenharmony_ci# you may not use this file except in compliance with the License.
6fd4e5da5Sopenharmony_ci# You may obtain a copy of the License at
7fd4e5da5Sopenharmony_ci#
8fd4e5da5Sopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
9fd4e5da5Sopenharmony_ci#
10fd4e5da5Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
11fd4e5da5Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
12fd4e5da5Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fd4e5da5Sopenharmony_ci# See the License for the specific language governing permissions and
14fd4e5da5Sopenharmony_ci# limitations under the License.
15fd4e5da5Sopenharmony_ci"""Generates language headers from a JSON grammar file"""
16fd4e5da5Sopenharmony_ci
17fd4e5da5Sopenharmony_ciimport errno
18fd4e5da5Sopenharmony_ciimport json
19fd4e5da5Sopenharmony_ciimport os.path
20fd4e5da5Sopenharmony_ciimport re
21fd4e5da5Sopenharmony_ci
22fd4e5da5Sopenharmony_ci
23fd4e5da5Sopenharmony_cidef make_path_to_file(f):
24fd4e5da5Sopenharmony_ci    """Makes all ancestor directories to the given file, if they
25fd4e5da5Sopenharmony_ci    don't yet exist.
26fd4e5da5Sopenharmony_ci
27fd4e5da5Sopenharmony_ci    Arguments:
28fd4e5da5Sopenharmony_ci        f: The file whose ancestor directories are to be created.
29fd4e5da5Sopenharmony_ci    """
30fd4e5da5Sopenharmony_ci    dir = os.path.dirname(os.path.abspath(f))
31fd4e5da5Sopenharmony_ci    try:
32fd4e5da5Sopenharmony_ci        os.makedirs(dir)
33fd4e5da5Sopenharmony_ci    except OSError as e:
34fd4e5da5Sopenharmony_ci        if e.errno == errno.EEXIST and os.path.isdir(dir):
35fd4e5da5Sopenharmony_ci            pass
36fd4e5da5Sopenharmony_ci        else:
37fd4e5da5Sopenharmony_ci            raise
38fd4e5da5Sopenharmony_ci
39fd4e5da5Sopenharmony_ciclass ExtInstGrammar:
40fd4e5da5Sopenharmony_ci    """The grammar for an extended instruction set"""
41fd4e5da5Sopenharmony_ci
42fd4e5da5Sopenharmony_ci    def __init__(self, name, copyright, instructions, operand_kinds, version = None, revision = None):
43fd4e5da5Sopenharmony_ci       self.name = name
44fd4e5da5Sopenharmony_ci       self.copyright = copyright
45fd4e5da5Sopenharmony_ci       self.instructions = instructions
46fd4e5da5Sopenharmony_ci       self.operand_kinds = operand_kinds
47fd4e5da5Sopenharmony_ci       self.version = version
48fd4e5da5Sopenharmony_ci       self.revision = revision
49fd4e5da5Sopenharmony_ci
50fd4e5da5Sopenharmony_ci
51fd4e5da5Sopenharmony_ciclass LangGenerator:
52fd4e5da5Sopenharmony_ci    """A language-specific generator"""
53fd4e5da5Sopenharmony_ci
54fd4e5da5Sopenharmony_ci    def __init__(self):
55fd4e5da5Sopenharmony_ci        self.upper_case_initial = re.compile('^[A-Z]')
56fd4e5da5Sopenharmony_ci        pass
57fd4e5da5Sopenharmony_ci
58fd4e5da5Sopenharmony_ci    def comment_prefix(self):
59fd4e5da5Sopenharmony_ci        return ""
60fd4e5da5Sopenharmony_ci
61fd4e5da5Sopenharmony_ci    def namespace_prefix(self):
62fd4e5da5Sopenharmony_ci        return ""
63fd4e5da5Sopenharmony_ci
64fd4e5da5Sopenharmony_ci    def uses_guards(self):
65fd4e5da5Sopenharmony_ci        return False
66fd4e5da5Sopenharmony_ci
67fd4e5da5Sopenharmony_ci    def cpp_guard_preamble(self):
68fd4e5da5Sopenharmony_ci        return ""
69fd4e5da5Sopenharmony_ci
70fd4e5da5Sopenharmony_ci    def cpp_guard_postamble(self):
71fd4e5da5Sopenharmony_ci        return ""
72fd4e5da5Sopenharmony_ci
73fd4e5da5Sopenharmony_ci    def enum_value(self, prefix, name, value):
74fd4e5da5Sopenharmony_ci        if self.upper_case_initial.match(name):
75fd4e5da5Sopenharmony_ci            use_name = name
76fd4e5da5Sopenharmony_ci        else:
77fd4e5da5Sopenharmony_ci            use_name = '_' + name
78fd4e5da5Sopenharmony_ci
79fd4e5da5Sopenharmony_ci        return "    {}{} = {},".format(prefix, use_name, value)
80fd4e5da5Sopenharmony_ci
81fd4e5da5Sopenharmony_ci    def generate(self, grammar):
82fd4e5da5Sopenharmony_ci        """Returns a string that is the language-specific header for the given grammar"""
83fd4e5da5Sopenharmony_ci
84fd4e5da5Sopenharmony_ci        parts = []
85fd4e5da5Sopenharmony_ci        if grammar.copyright:
86fd4e5da5Sopenharmony_ci            parts.extend(["{}{}".format(self.comment_prefix(), f) for f in grammar.copyright])
87fd4e5da5Sopenharmony_ci        parts.append('')
88fd4e5da5Sopenharmony_ci
89fd4e5da5Sopenharmony_ci        guard = 'SPIRV_EXTINST_{}_H_'.format(grammar.name)
90fd4e5da5Sopenharmony_ci        if self.uses_guards:
91fd4e5da5Sopenharmony_ci            parts.append('#ifndef {}'.format(guard))
92fd4e5da5Sopenharmony_ci            parts.append('#define {}'.format(guard))
93fd4e5da5Sopenharmony_ci        parts.append('')
94fd4e5da5Sopenharmony_ci
95fd4e5da5Sopenharmony_ci        parts.append(self.cpp_guard_preamble())
96fd4e5da5Sopenharmony_ci
97fd4e5da5Sopenharmony_ci        if grammar.version:
98fd4e5da5Sopenharmony_ci            parts.append(self.const_definition(grammar.name, 'Version', grammar.version))
99fd4e5da5Sopenharmony_ci
100fd4e5da5Sopenharmony_ci        if grammar.revision is not None:
101fd4e5da5Sopenharmony_ci            parts.append(self.const_definition(grammar.name, 'Revision', grammar.revision))
102fd4e5da5Sopenharmony_ci
103fd4e5da5Sopenharmony_ci        parts.append('')
104fd4e5da5Sopenharmony_ci
105fd4e5da5Sopenharmony_ci        if grammar.instructions:
106fd4e5da5Sopenharmony_ci            parts.append(self.enum_prefix(grammar.name, 'Instructions'))
107fd4e5da5Sopenharmony_ci            for inst in grammar.instructions:
108fd4e5da5Sopenharmony_ci                parts.append(self.enum_value(grammar.name, inst['opname'], inst['opcode']))
109fd4e5da5Sopenharmony_ci            parts.append(self.enum_end(grammar.name, 'Instructions'))
110fd4e5da5Sopenharmony_ci            parts.append('')
111fd4e5da5Sopenharmony_ci
112fd4e5da5Sopenharmony_ci        if grammar.operand_kinds:
113fd4e5da5Sopenharmony_ci            for kind in grammar.operand_kinds:
114fd4e5da5Sopenharmony_ci                parts.append(self.enum_prefix(grammar.name, kind['kind']))
115fd4e5da5Sopenharmony_ci                for e in kind['enumerants']:
116fd4e5da5Sopenharmony_ci                    parts.append(self.enum_value(grammar.name, e['enumerant'], e['value']))
117fd4e5da5Sopenharmony_ci                parts.append(self.enum_end(grammar.name, kind['kind']))
118fd4e5da5Sopenharmony_ci            parts.append('')
119fd4e5da5Sopenharmony_ci
120fd4e5da5Sopenharmony_ci        parts.append(self.cpp_guard_postamble())
121fd4e5da5Sopenharmony_ci
122fd4e5da5Sopenharmony_ci        if self.uses_guards:
123fd4e5da5Sopenharmony_ci            parts.append('#endif // {}'.format(guard))
124fd4e5da5Sopenharmony_ci
125fd4e5da5Sopenharmony_ci        return '\n'.join(parts)
126fd4e5da5Sopenharmony_ci
127fd4e5da5Sopenharmony_ci
128fd4e5da5Sopenharmony_ciclass CLikeGenerator(LangGenerator):
129fd4e5da5Sopenharmony_ci    def uses_guards(self):
130fd4e5da5Sopenharmony_ci        return True
131fd4e5da5Sopenharmony_ci
132fd4e5da5Sopenharmony_ci    def comment_prefix(self):
133fd4e5da5Sopenharmony_ci        return "// "
134fd4e5da5Sopenharmony_ci
135fd4e5da5Sopenharmony_ci    def const_definition(self, prefix, var, value):
136fd4e5da5Sopenharmony_ci        # Use an anonymous enum.  Don't use a static const int variable because
137fd4e5da5Sopenharmony_ci        # that can bloat binary size.
138fd4e5da5Sopenharmony_ci        return 'enum {0} {1}{2} = {3}, {1}{2}_BitWidthPadding = 0x7fffffff {4};'.format(
139fd4e5da5Sopenharmony_ci               '{', prefix, var, value, '}')
140fd4e5da5Sopenharmony_ci
141fd4e5da5Sopenharmony_ci    def enum_prefix(self, prefix, name):
142fd4e5da5Sopenharmony_ci        return 'enum {}{} {}'.format(prefix, name, '{')
143fd4e5da5Sopenharmony_ci
144fd4e5da5Sopenharmony_ci    def enum_end(self, prefix, enum):
145fd4e5da5Sopenharmony_ci        return '    {}{}Max = 0x7ffffff\n{};\n'.format(prefix, enum, '}')
146fd4e5da5Sopenharmony_ci
147fd4e5da5Sopenharmony_ci    def cpp_guard_preamble(self):
148fd4e5da5Sopenharmony_ci        return '#ifdef __cplusplus\nextern "C" {\n#endif\n'
149fd4e5da5Sopenharmony_ci
150fd4e5da5Sopenharmony_ci    def cpp_guard_postamble(self):
151fd4e5da5Sopenharmony_ci        return '#ifdef __cplusplus\n}\n#endif\n'
152fd4e5da5Sopenharmony_ci
153fd4e5da5Sopenharmony_ci
154fd4e5da5Sopenharmony_ciclass CGenerator(CLikeGenerator):
155fd4e5da5Sopenharmony_ci    pass
156fd4e5da5Sopenharmony_ci
157fd4e5da5Sopenharmony_ci
158fd4e5da5Sopenharmony_cidef main():
159fd4e5da5Sopenharmony_ci    import argparse
160fd4e5da5Sopenharmony_ci    parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar')
161fd4e5da5Sopenharmony_ci
162fd4e5da5Sopenharmony_ci    parser.add_argument('--extinst-grammar', metavar='<path>',
163fd4e5da5Sopenharmony_ci                        type=str, required=True,
164fd4e5da5Sopenharmony_ci                        help='input JSON grammar file for extended instruction set')
165fd4e5da5Sopenharmony_ci    parser.add_argument('--extinst-output-path', metavar='<path>',
166fd4e5da5Sopenharmony_ci                        type=str, required=True,
167fd4e5da5Sopenharmony_ci                        help='Path of the language-specific output file.')
168fd4e5da5Sopenharmony_ci    args = parser.parse_args()
169fd4e5da5Sopenharmony_ci
170fd4e5da5Sopenharmony_ci    with open(args.extinst_grammar) as json_file:
171fd4e5da5Sopenharmony_ci        grammar_json = json.loads(json_file.read())
172fd4e5da5Sopenharmony_ci        grammar_name = os.path.splitext(os.path.basename(args.extinst_output_path))[0]
173fd4e5da5Sopenharmony_ci        grammar = ExtInstGrammar(name = grammar_name,
174fd4e5da5Sopenharmony_ci                                 copyright = grammar_json['copyright'],
175fd4e5da5Sopenharmony_ci                                 instructions = grammar_json['instructions'],
176fd4e5da5Sopenharmony_ci                                 operand_kinds = grammar_json['operand_kinds'],
177fd4e5da5Sopenharmony_ci                                 version = grammar_json['version'],
178fd4e5da5Sopenharmony_ci                                 revision = grammar_json['revision'])
179fd4e5da5Sopenharmony_ci        make_path_to_file(args.extinst_output_path)
180fd4e5da5Sopenharmony_ci        with open(args.extinst_output_path, 'w') as f:
181fd4e5da5Sopenharmony_ci            f.write(CGenerator().generate(grammar))
182fd4e5da5Sopenharmony_ci
183fd4e5da5Sopenharmony_ci
184fd4e5da5Sopenharmony_ciif __name__ == '__main__':
185fd4e5da5Sopenharmony_ci    main()
186