xref: /aosp_15_r20/art/tools/generate_operator_out.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*795d594fSAndroid Build Coastguard Worker#
3*795d594fSAndroid Build Coastguard Worker# Copyright (C) 2012 The Android Open Source Project
4*795d594fSAndroid Build Coastguard Worker#
5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*795d594fSAndroid Build Coastguard Worker#
9*795d594fSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*795d594fSAndroid Build Coastguard Worker#
11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*795d594fSAndroid Build Coastguard Worker# limitations under the License.
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker"""Generates default implementations of operator<< for enum types."""
18*795d594fSAndroid Build Coastguard Worker
19*795d594fSAndroid Build Coastguard Workerimport codecs
20*795d594fSAndroid Build Coastguard Workerimport re
21*795d594fSAndroid Build Coastguard Workerimport sys
22*795d594fSAndroid Build Coastguard Worker
23*795d594fSAndroid Build Coastguard Worker
24*795d594fSAndroid Build Coastguard Worker_ENUM_START_RE = re.compile(
25*795d594fSAndroid Build Coastguard Worker    r'\benum\b\s+(class\s+)?(?:HIDDEN |EXPORT )?(\S+)\s+:?.*\{(\s+// private)?')
26*795d594fSAndroid Build Coastguard Worker_ENUM_VALUE_RE = re.compile(r'([A-Za-z0-9_]+)(.*)')
27*795d594fSAndroid Build Coastguard Worker_ENUM_END_RE = re.compile(r'^\s*\};$')
28*795d594fSAndroid Build Coastguard Worker_ENUMS = {}
29*795d594fSAndroid Build Coastguard Worker_NAMESPACES = {}
30*795d594fSAndroid Build Coastguard Worker_ENUM_CLASSES = {}
31*795d594fSAndroid Build Coastguard Worker
32*795d594fSAndroid Build Coastguard Worker
33*795d594fSAndroid Build Coastguard Workerdef Confused(filename, line_number, line):
34*795d594fSAndroid Build Coastguard Worker    sys.stderr.write('%s:%d: confused by:\n%s\n' %
35*795d594fSAndroid Build Coastguard Worker                     (filename, line_number, line))
36*795d594fSAndroid Build Coastguard Worker    raise Exception("giving up!")
37*795d594fSAndroid Build Coastguard Worker    sys.exit(1)
38*795d594fSAndroid Build Coastguard Worker
39*795d594fSAndroid Build Coastguard Worker
40*795d594fSAndroid Build Coastguard Workerdef ProcessFile(filename):
41*795d594fSAndroid Build Coastguard Worker    lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
42*795d594fSAndroid Build Coastguard Worker
43*795d594fSAndroid Build Coastguard Worker    class EnumLines:
44*795d594fSAndroid Build Coastguard Worker        def __init__(self, ns, ec):
45*795d594fSAndroid Build Coastguard Worker            self.namespaces = ns
46*795d594fSAndroid Build Coastguard Worker            self.enclosing_classes = ec
47*795d594fSAndroid Build Coastguard Worker            self.lines = []
48*795d594fSAndroid Build Coastguard Worker
49*795d594fSAndroid Build Coastguard Worker    def generate_enum_lines(l):
50*795d594fSAndroid Build Coastguard Worker        line_number = 0
51*795d594fSAndroid Build Coastguard Worker        enum_lines = None
52*795d594fSAndroid Build Coastguard Worker        namespaces = []
53*795d594fSAndroid Build Coastguard Worker        enclosing_classes = []
54*795d594fSAndroid Build Coastguard Worker
55*795d594fSAndroid Build Coastguard Worker        for raw_line in l:
56*795d594fSAndroid Build Coastguard Worker            line_number += 1
57*795d594fSAndroid Build Coastguard Worker
58*795d594fSAndroid Build Coastguard Worker            if enum_lines is None:
59*795d594fSAndroid Build Coastguard Worker                # Is this the start of a new enum?
60*795d594fSAndroid Build Coastguard Worker                m = _ENUM_START_RE.search(raw_line)
61*795d594fSAndroid Build Coastguard Worker                if m:
62*795d594fSAndroid Build Coastguard Worker                    # Yes, so create new line list.
63*795d594fSAndroid Build Coastguard Worker                    enum_lines = EnumLines(namespaces[:], enclosing_classes[:])
64*795d594fSAndroid Build Coastguard Worker                    enum_lines.lines.append((raw_line, line_number))
65*795d594fSAndroid Build Coastguard Worker                    continue
66*795d594fSAndroid Build Coastguard Worker
67*795d594fSAndroid Build Coastguard Worker                # Is this the start or end of a namespace?
68*795d594fSAndroid Build Coastguard Worker                m = re.search(r'^namespace (\S+) (HIDDEN |EXPORT )?\{', raw_line)
69*795d594fSAndroid Build Coastguard Worker                if m:
70*795d594fSAndroid Build Coastguard Worker                    namespaces.append(m.group(1))
71*795d594fSAndroid Build Coastguard Worker                    continue
72*795d594fSAndroid Build Coastguard Worker                m = re.search(r'^\}\s+// namespace', raw_line)
73*795d594fSAndroid Build Coastguard Worker                if m:
74*795d594fSAndroid Build Coastguard Worker                    namespaces = namespaces[0:len(namespaces) - 1]
75*795d594fSAndroid Build Coastguard Worker                    continue
76*795d594fSAndroid Build Coastguard Worker
77*795d594fSAndroid Build Coastguard Worker                # Is this the start or end of an enclosing class or struct?
78*795d594fSAndroid Build Coastguard Worker                m = re.search(
79*795d594fSAndroid Build Coastguard Worker                    r'^\s*(?:class|struct)(?: HIDDEN| EXPORT)?(?: MANAGED)?(?: PACKED\([0-9]\))? (\S+).* \{', raw_line)
80*795d594fSAndroid Build Coastguard Worker                if m:
81*795d594fSAndroid Build Coastguard Worker                    enclosing_classes.append(m.group(1))
82*795d594fSAndroid Build Coastguard Worker                    continue
83*795d594fSAndroid Build Coastguard Worker
84*795d594fSAndroid Build Coastguard Worker                # End of class/struct -- be careful not to match "do { ... } while" constructs by accident
85*795d594fSAndroid Build Coastguard Worker                m = re.search(r'^\s*\}(\s+)?(while)?(.+)?;', raw_line)
86*795d594fSAndroid Build Coastguard Worker                if m and not m.group(2):
87*795d594fSAndroid Build Coastguard Worker                    enclosing_classes = enclosing_classes[0:len(enclosing_classes) - 1]
88*795d594fSAndroid Build Coastguard Worker                    continue
89*795d594fSAndroid Build Coastguard Worker
90*795d594fSAndroid Build Coastguard Worker                continue
91*795d594fSAndroid Build Coastguard Worker
92*795d594fSAndroid Build Coastguard Worker            # Is this the end of the current enum?
93*795d594fSAndroid Build Coastguard Worker            m = _ENUM_END_RE.search(raw_line)
94*795d594fSAndroid Build Coastguard Worker            if m:
95*795d594fSAndroid Build Coastguard Worker                if enum_lines is None:
96*795d594fSAndroid Build Coastguard Worker                    Confused(filename, line_number, raw_line)
97*795d594fSAndroid Build Coastguard Worker                yield enum_lines
98*795d594fSAndroid Build Coastguard Worker                enum_lines = None
99*795d594fSAndroid Build Coastguard Worker                continue
100*795d594fSAndroid Build Coastguard Worker
101*795d594fSAndroid Build Coastguard Worker            # Append the line
102*795d594fSAndroid Build Coastguard Worker            enum_lines.lines.append((raw_line, line_number))
103*795d594fSAndroid Build Coastguard Worker
104*795d594fSAndroid Build Coastguard Worker    for enum_lines in generate_enum_lines(lines):
105*795d594fSAndroid Build Coastguard Worker        m = _ENUM_START_RE.search(enum_lines.lines[0][0])
106*795d594fSAndroid Build Coastguard Worker        if m.group(3) is not None:
107*795d594fSAndroid Build Coastguard Worker            # Skip private enums.
108*795d594fSAndroid Build Coastguard Worker            continue
109*795d594fSAndroid Build Coastguard Worker
110*795d594fSAndroid Build Coastguard Worker        # Add an empty entry to _ENUMS for this enum.
111*795d594fSAndroid Build Coastguard Worker        is_enum_class = m.group(1) is not None
112*795d594fSAndroid Build Coastguard Worker        enum_name = m.group(2)
113*795d594fSAndroid Build Coastguard Worker        if len(enum_lines.enclosing_classes) > 0:
114*795d594fSAndroid Build Coastguard Worker            enum_name = '::'.join(enum_lines.enclosing_classes) + '::' + enum_name
115*795d594fSAndroid Build Coastguard Worker        _ENUMS[enum_name] = []
116*795d594fSAndroid Build Coastguard Worker        _NAMESPACES[enum_name] = '::'.join(enum_lines.namespaces)
117*795d594fSAndroid Build Coastguard Worker        _ENUM_CLASSES[enum_name] = is_enum_class
118*795d594fSAndroid Build Coastguard Worker
119*795d594fSAndroid Build Coastguard Worker        def generate_non_empty_line(lines):
120*795d594fSAndroid Build Coastguard Worker            for raw_line, line_number in lines:
121*795d594fSAndroid Build Coastguard Worker                # Strip // comments.
122*795d594fSAndroid Build Coastguard Worker                line = re.sub(r'//.*', '', raw_line)
123*795d594fSAndroid Build Coastguard Worker                # Strip whitespace.
124*795d594fSAndroid Build Coastguard Worker                line = line.strip()
125*795d594fSAndroid Build Coastguard Worker                # Skip blank lines.
126*795d594fSAndroid Build Coastguard Worker                if len(line) == 0:
127*795d594fSAndroid Build Coastguard Worker                    continue
128*795d594fSAndroid Build Coastguard Worker
129*795d594fSAndroid Build Coastguard Worker                # The only useful thing in comments is the <<alternate text>> syntax for
130*795d594fSAndroid Build Coastguard Worker                # overriding the default enum value names. Pull that out...
131*795d594fSAndroid Build Coastguard Worker                enum_text = None
132*795d594fSAndroid Build Coastguard Worker                m_comment = re.search(r'// <<(.*?)>>', raw_line)
133*795d594fSAndroid Build Coastguard Worker                if m_comment:
134*795d594fSAndroid Build Coastguard Worker                    enum_text = m_comment.group(1)
135*795d594fSAndroid Build Coastguard Worker
136*795d594fSAndroid Build Coastguard Worker                yield (line, enum_text, raw_line, line_number)
137*795d594fSAndroid Build Coastguard Worker
138*795d594fSAndroid Build Coastguard Worker        for line, enum_text, raw_line, line_number in generate_non_empty_line(enum_lines.lines[1:]):
139*795d594fSAndroid Build Coastguard Worker            # Since we know we're in an enum type, and we're not looking at a comment
140*795d594fSAndroid Build Coastguard Worker            # or a blank line, this line should be the next enum value...
141*795d594fSAndroid Build Coastguard Worker            m = _ENUM_VALUE_RE.search(line)
142*795d594fSAndroid Build Coastguard Worker            if not m:
143*795d594fSAndroid Build Coastguard Worker                Confused(filename, line_number, raw_line)
144*795d594fSAndroid Build Coastguard Worker            enum_value = m.group(1)
145*795d594fSAndroid Build Coastguard Worker
146*795d594fSAndroid Build Coastguard Worker            # By default, we turn "kSomeValue" into "SomeValue".
147*795d594fSAndroid Build Coastguard Worker            if enum_text is None:
148*795d594fSAndroid Build Coastguard Worker                enum_text = enum_value
149*795d594fSAndroid Build Coastguard Worker                if enum_text.startswith('k'):
150*795d594fSAndroid Build Coastguard Worker                    enum_text = enum_text[1:]
151*795d594fSAndroid Build Coastguard Worker
152*795d594fSAndroid Build Coastguard Worker            # Check that we understand the line (and hopefully do not parse incorrectly), or should
153*795d594fSAndroid Build Coastguard Worker            # filter.
154*795d594fSAndroid Build Coastguard Worker            rest = m.group(2).strip()
155*795d594fSAndroid Build Coastguard Worker
156*795d594fSAndroid Build Coastguard Worker            # With "kSomeValue = kOtherValue," we take the original and skip later synonyms.
157*795d594fSAndroid Build Coastguard Worker            # TODO: check that the rhs is actually an existing value.
158*795d594fSAndroid Build Coastguard Worker            if rest.startswith('= k'):
159*795d594fSAndroid Build Coastguard Worker                continue
160*795d594fSAndroid Build Coastguard Worker
161*795d594fSAndroid Build Coastguard Worker            # Remove trailing comma.
162*795d594fSAndroid Build Coastguard Worker            if rest.endswith(','):
163*795d594fSAndroid Build Coastguard Worker                rest = rest[:-1]
164*795d594fSAndroid Build Coastguard Worker
165*795d594fSAndroid Build Coastguard Worker            # We now expect rest to be empty, or an assignment to an "expression."
166*795d594fSAndroid Build Coastguard Worker            if len(rest):
167*795d594fSAndroid Build Coastguard Worker                # We want to lose the expression "= [exp]". As we do not have a real C parser, just
168*795d594fSAndroid Build Coastguard Worker                # assume anything without a comma is valid.
169*795d594fSAndroid Build Coastguard Worker                m_exp = re.match('= [^,]+$', rest)
170*795d594fSAndroid Build Coastguard Worker                if m_exp is None:
171*795d594fSAndroid Build Coastguard Worker                    sys.stderr.write('%s\n' % (rest))
172*795d594fSAndroid Build Coastguard Worker                    Confused(filename, line_number, raw_line)
173*795d594fSAndroid Build Coastguard Worker
174*795d594fSAndroid Build Coastguard Worker            # If the enum is scoped, we must prefix enum value with enum name (which is already prefixed
175*795d594fSAndroid Build Coastguard Worker            # by enclosing classes).
176*795d594fSAndroid Build Coastguard Worker            if is_enum_class:
177*795d594fSAndroid Build Coastguard Worker                enum_value = enum_name + '::' + enum_value
178*795d594fSAndroid Build Coastguard Worker            else:
179*795d594fSAndroid Build Coastguard Worker                if len(enum_lines.enclosing_classes) > 0:
180*795d594fSAndroid Build Coastguard Worker                    enum_value = '::'.join(enum_lines.enclosing_classes) + '::' + enum_value
181*795d594fSAndroid Build Coastguard Worker
182*795d594fSAndroid Build Coastguard Worker            _ENUMS[enum_name].append((enum_value, enum_text))
183*795d594fSAndroid Build Coastguard Worker
184*795d594fSAndroid Build Coastguard Worker
185*795d594fSAndroid Build Coastguard Workerdef main():
186*795d594fSAndroid Build Coastguard Worker    local_path = sys.argv[1]
187*795d594fSAndroid Build Coastguard Worker    header_files = []
188*795d594fSAndroid Build Coastguard Worker    for header_file in sys.argv[2:]:
189*795d594fSAndroid Build Coastguard Worker        header_files.append(header_file)
190*795d594fSAndroid Build Coastguard Worker        ProcessFile(header_file)
191*795d594fSAndroid Build Coastguard Worker
192*795d594fSAndroid Build Coastguard Worker    print('#include <iostream>')
193*795d594fSAndroid Build Coastguard Worker    print('')
194*795d594fSAndroid Build Coastguard Worker
195*795d594fSAndroid Build Coastguard Worker    for header_file in header_files:
196*795d594fSAndroid Build Coastguard Worker        header_file = header_file.replace(local_path + '/', '')
197*795d594fSAndroid Build Coastguard Worker        print('#include "%s"' % header_file)
198*795d594fSAndroid Build Coastguard Worker
199*795d594fSAndroid Build Coastguard Worker    print('')
200*795d594fSAndroid Build Coastguard Worker
201*795d594fSAndroid Build Coastguard Worker    for enum_name in _ENUMS:
202*795d594fSAndroid Build Coastguard Worker        print('// This was automatically generated by art/tools/generate_operator_out.py --- do not edit!')
203*795d594fSAndroid Build Coastguard Worker
204*795d594fSAndroid Build Coastguard Worker        namespaces = _NAMESPACES[enum_name].split('::')
205*795d594fSAndroid Build Coastguard Worker        for namespace in namespaces:
206*795d594fSAndroid Build Coastguard Worker            print('namespace %s {' % namespace)
207*795d594fSAndroid Build Coastguard Worker
208*795d594fSAndroid Build Coastguard Worker        print(
209*795d594fSAndroid Build Coastguard Worker            'std::ostream& operator<<(std::ostream& os, %s rhs) {' % enum_name)
210*795d594fSAndroid Build Coastguard Worker        print('  switch (rhs) {')
211*795d594fSAndroid Build Coastguard Worker        for (enum_value, enum_text) in _ENUMS[enum_name]:
212*795d594fSAndroid Build Coastguard Worker            print('    case %s: os << "%s"; break;' % (enum_value, enum_text))
213*795d594fSAndroid Build Coastguard Worker        if not _ENUM_CLASSES[enum_name]:
214*795d594fSAndroid Build Coastguard Worker            print(
215*795d594fSAndroid Build Coastguard Worker                '    default: os << "%s[" << static_cast<int>(rhs) << "]"; break;' % enum_name)
216*795d594fSAndroid Build Coastguard Worker        print('  }')
217*795d594fSAndroid Build Coastguard Worker        print('  return os;')
218*795d594fSAndroid Build Coastguard Worker        print('}')
219*795d594fSAndroid Build Coastguard Worker
220*795d594fSAndroid Build Coastguard Worker        for namespace in reversed(namespaces):
221*795d594fSAndroid Build Coastguard Worker            print('}  // namespace %s' % namespace)
222*795d594fSAndroid Build Coastguard Worker        print('')
223*795d594fSAndroid Build Coastguard Worker
224*795d594fSAndroid Build Coastguard Worker    sys.exit(0)
225*795d594fSAndroid Build Coastguard Worker
226*795d594fSAndroid Build Coastguard Worker
227*795d594fSAndroid Build Coastguard Workerif __name__ == '__main__':
228*795d594fSAndroid Build Coastguard Worker    main()
229