blob: 91e1caffa626182c9ab5f384b4de7f9b7cec2508 [file] [log] [blame]
Chirayu Desai2248c172013-11-06 20:43:21 +05301#!/usr/bin/env python
Elliott Hughes0e57ccb2012-04-03 16:04:52 -07002#
Elliott Hughesd320a9a2013-10-06 21:48:46 -07003# Copyright (C) 2012 The Android Open Source Project
Elliott Hughes0e57ccb2012-04-03 16:04:52 -07004#
Elliott Hughesd320a9a2013-10-06 21:48:46 -07005# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
Elliott Hughes0e57ccb2012-04-03 16:04:52 -07008#
Elliott Hughesd320a9a2013-10-06 21:48:46 -07009# http://www.apache.org/licenses/LICENSE-2.0
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070010#
Elliott Hughesd320a9a2013-10-06 21:48:46 -070011# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070016
17"""Generates default implementations of operator<< for enum types."""
18
19import codecs
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070020import re
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070021import sys
22
23
Andreas Gamped3b06eb2019-06-27 14:26:00 -070024_ENUM_START_RE = re.compile(
25 r'\benum\b\s+(class\s+)?(\S+)\s+:?.*\{(\s+// private)?')
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070026_ENUM_VALUE_RE = re.compile(r'([A-Za-z0-9_]+)(.*)')
27_ENUM_END_RE = re.compile(r'^\s*\};$')
28_ENUMS = {}
Elliott Hughes460384f2012-04-04 16:53:10 -070029_NAMESPACES = {}
Andreas Gampe833a4852014-05-21 18:46:59 -070030_ENUM_CLASSES = {}
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070031
Andreas Gamped3b06eb2019-06-27 14:26:00 -070032
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070033def Confused(filename, line_number, line):
Andreas Gamped3b06eb2019-06-27 14:26:00 -070034 sys.stderr.write('%s:%d: confused by:\n%s\n' %
35 (filename, line_number, line))
36 raise Exception("giving up!")
37 sys.exit(1)
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070038
39
40def ProcessFile(filename):
Andreas Gamped3b06eb2019-06-27 14:26:00 -070041 lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
Elliott Hughes460384f2012-04-04 16:53:10 -070042
Andreas Gampe2ad19bc2019-06-27 15:11:59 -070043 class EnumLines:
44 def __init__(self, ns, ec):
45 self.namespaces = ns
46 self.enclosing_classes = ec
47 self.lines = []
Elliott Hughes460384f2012-04-04 16:53:10 -070048
Andreas Gampe2ad19bc2019-06-27 15:11:59 -070049 def generate_enum_lines(l):
50 line_number = 0
51 enum_lines = None
52 namespaces = []
53 enclosing_classes = []
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070054
Andreas Gampe2ad19bc2019-06-27 15:11:59 -070055 for raw_line in l:
56 line_number += 1
57
58 if enum_lines is None:
59 # Is this the start of a new enum?
60 m = _ENUM_START_RE.search(raw_line)
61 if m:
62 # Yes, so create new line list.
63 enum_lines = EnumLines(namespaces[:], enclosing_classes[:])
64 enum_lines.lines.append((raw_line, line_number))
65 continue
66
67 # Is this the start or end of a namespace?
Vladimir Marko0a516052019-10-14 13:00:44 +000068 m = re.search(r'^namespace (\S+) \{', raw_line)
Andreas Gampe2ad19bc2019-06-27 15:11:59 -070069 if m:
70 namespaces.append(m.group(1))
71 continue
72 m = re.search(r'^\}\s+// namespace', raw_line)
73 if m:
74 namespaces = namespaces[0:len(namespaces) - 1]
75 continue
76
77 # Is this the start or end of an enclosing class or struct?
78 m = re.search(
79 r'^\s*(?:class|struct)(?: MANAGED)?(?: PACKED\([0-9]\))? (\S+).* \{', raw_line)
80 if m:
81 enclosing_classes.append(m.group(1))
82 continue
83
84 # End of class/struct -- be careful not to match "do { ... } while" constructs by accident
85 m = re.search(r'^\s*\}(\s+)?(while)?(.+)?;', raw_line)
86 if m and not m.group(2):
87 enclosing_classes = enclosing_classes[0:len(enclosing_classes) - 1]
88 continue
89
90 continue
91
92 # Is this the end of the current enum?
93 m = _ENUM_END_RE.search(raw_line)
Andreas Gamped3b06eb2019-06-27 14:26:00 -070094 if m:
Andreas Gampe2ad19bc2019-06-27 15:11:59 -070095 if enum_lines is None:
96 Confused(filename, line_number, raw_line)
97 yield enum_lines
98 enum_lines = None
Andreas Gamped3b06eb2019-06-27 14:26:00 -070099 continue
100
Andreas Gampe2ad19bc2019-06-27 15:11:59 -0700101 # Append the line
102 enum_lines.lines.append((raw_line, line_number))
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700103
Andreas Gampe2ad19bc2019-06-27 15:11:59 -0700104 for enum_lines in generate_enum_lines(lines):
105 m = _ENUM_START_RE.search(enum_lines.lines[0][0])
106 if m.group(3) is not None:
107 # Skip private enums.
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700108 continue
109
Andreas Gampe2ad19bc2019-06-27 15:11:59 -0700110 # Add an empty entry to _ENUMS for this enum.
111 is_enum_class = m.group(1) is not None
112 enum_name = m.group(2)
113 if len(enum_lines.enclosing_classes) > 0:
114 enum_name = '::'.join(enum_lines.enclosing_classes) + '::' + enum_name
115 _ENUMS[enum_name] = []
116 _NAMESPACES[enum_name] = '::'.join(enum_lines.namespaces)
117 _ENUM_CLASSES[enum_name] = is_enum_class
118
119 def generate_non_empty_line(lines):
120 for raw_line, line_number in lines:
121 # Strip // comments.
122 line = re.sub(r'//.*', '', raw_line)
123 # Strip whitespace.
124 line = line.strip()
125 # Skip blank lines.
126 if len(line) == 0:
127 continue
128
129 # The only useful thing in comments is the <<alternate text>> syntax for
130 # overriding the default enum value names. Pull that out...
131 enum_text = None
132 m_comment = re.search(r'// <<(.*?)>>', raw_line)
133 if m_comment:
134 enum_text = m_comment.group(1)
135
136 yield (line, enum_text, raw_line, line_number)
137
138 for line, enum_text, raw_line, line_number in generate_non_empty_line(enum_lines.lines[1:]):
139 # Since we know we're in an enum type, and we're not looking at a comment
140 # or a blank line, this line should be the next enum value...
141 m = _ENUM_VALUE_RE.search(line)
142 if not m:
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700143 Confused(filename, line_number, raw_line)
Andreas Gampe2ad19bc2019-06-27 15:11:59 -0700144 enum_value = m.group(1)
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700145
Andreas Gampe2ad19bc2019-06-27 15:11:59 -0700146 # By default, we turn "kSomeValue" into "SomeValue".
147 if enum_text is None:
148 enum_text = enum_value
149 if enum_text.startswith('k'):
150 enum_text = enum_text[1:]
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700151
Andreas Gampeb839fbb2019-06-27 16:26:55 -0700152 # Check that we understand the line (and hopefully do not parse incorrectly), or should
153 # filter.
Andreas Gampe2ad19bc2019-06-27 15:11:59 -0700154 rest = m.group(2).strip()
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700155
Andreas Gampe2ad19bc2019-06-27 15:11:59 -0700156 # With "kSomeValue = kOtherValue," we take the original and skip later synonyms.
157 # TODO: check that the rhs is actually an existing value.
158 if rest.startswith('= k'):
159 continue
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700160
Andreas Gampeb839fbb2019-06-27 16:26:55 -0700161 # Remove trailing comma.
162 if rest.endswith(','):
163 rest = rest[:-1]
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700164
Andreas Gampeb839fbb2019-06-27 16:26:55 -0700165 # We now expect rest to be empty, or an assignment to an "expression."
Andreas Gampe2ad19bc2019-06-27 15:11:59 -0700166 if len(rest):
Andreas Gampeb839fbb2019-06-27 16:26:55 -0700167 # We want to lose the expression "= [exp]". As we do not have a real C parser, just
168 # assume anything without a comma is valid.
169 m_exp = re.match('= [^,]+$', rest)
170 if m_exp is None:
171 sys.stderr.write('%s\n' % (rest))
172 Confused(filename, line_number, raw_line)
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700173
Andreas Gampe2ad19bc2019-06-27 15:11:59 -0700174 # If the enum is scoped, we must prefix enum value with enum name (which is already prefixed
175 # by enclosing classes).
176 if is_enum_class:
177 enum_value = enum_name + '::' + enum_value
178 else:
179 if len(enum_lines.enclosing_classes) > 0:
180 enum_value = '::'.join(enum_lines.enclosing_classes) + '::' + enum_value
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700181
Andreas Gampe2ad19bc2019-06-27 15:11:59 -0700182 _ENUMS[enum_name].append((enum_value, enum_text))
Elliott Hughes460384f2012-04-04 16:53:10 -0700183
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700184
185def main():
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700186 local_path = sys.argv[1]
187 header_files = []
188 for header_file in sys.argv[2:]:
189 header_files.append(header_file)
190 ProcessFile(header_file)
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700191
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700192 print('#include <iostream>')
Bernhard Rosenkränzerc2e02602014-07-14 13:30:58 +0200193 print('')
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700194
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700195 for header_file in header_files:
196 header_file = header_file.replace(local_path + '/', '')
197 print('#include "%s"' % header_file)
198
199 print('')
200
201 for enum_name in _ENUMS:
202 print('// This was automatically generated by art/tools/generate_operator_out.py --- do not edit!')
203
204 namespaces = _NAMESPACES[enum_name].split('::')
205 for namespace in namespaces:
206 print('namespace %s {' % namespace)
207
208 print(
Vladimir Marko9974e3c2020-06-10 16:27:06 +0100209 'std::ostream& operator<<(std::ostream& os, %s rhs) {' % enum_name)
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700210 print(' switch (rhs) {')
211 for (enum_value, enum_text) in _ENUMS[enum_name]:
212 print(' case %s: os << "%s"; break;' % (enum_value, enum_text))
213 if not _ENUM_CLASSES[enum_name]:
214 print(
215 ' default: os << "%s[" << static_cast<int>(rhs) << "]"; break;' % enum_name)
216 print(' }')
217 print(' return os;')
218 print('}')
219
220 for namespace in reversed(namespaces):
221 print('} // namespace %s' % namespace)
222 print('')
223
224 sys.exit(0)
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700225
226
227if __name__ == '__main__':
Andreas Gamped3b06eb2019-06-27 14:26:00 -0700228 main()