blob: 19266b4f64a2be954db457143418c227261665af [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
20import os
21import re
22import string
23import sys
24
25
26_ENUM_START_RE = re.compile(r'\benum\b\s+(\S+)\s+\{')
27_ENUM_VALUE_RE = re.compile(r'([A-Za-z0-9_]+)(.*)')
28_ENUM_END_RE = re.compile(r'^\s*\};$')
29_ENUMS = {}
Elliott Hughes460384f2012-04-04 16:53:10 -070030_NAMESPACES = {}
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070031
32def Confused(filename, line_number, line):
33 sys.stderr.write('%s:%d: confused by:\n%s\n' % (filename, line_number, line))
Elliott Hughes48257562012-06-06 17:42:44 -070034 raise Exception("giving up!")
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070035 sys.exit(1)
36
37
38def ProcessFile(filename):
39 lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
40 in_enum = False
41 line_number = 0
Elliott Hughes460384f2012-04-04 16:53:10 -070042
43 namespaces = []
44 enclosing_classes = []
45
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070046 for raw_line in lines:
47 line_number += 1
48
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070049 if not in_enum:
Elliott Hughes460384f2012-04-04 16:53:10 -070050 # Is this the start of a new enum?
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070051 m = _ENUM_START_RE.search(raw_line)
52 if m:
53 # Yes, so add an empty entry to _ENUMS for this enum.
54 enum_name = m.group(1)
Elliott Hughes460384f2012-04-04 16:53:10 -070055 if len(enclosing_classes) > 0:
56 enum_name = '::'.join(enclosing_classes) + '::' + enum_name
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070057 _ENUMS[enum_name] = []
Elliott Hughes460384f2012-04-04 16:53:10 -070058 _NAMESPACES[enum_name] = '::'.join(namespaces)
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070059 in_enum = True
Elliott Hughes460384f2012-04-04 16:53:10 -070060 continue
61
62 # Is this the start or end of a namespace?
63 m = re.compile(r'^namespace (\S+) \{').search(raw_line)
64 if m:
65 namespaces.append(m.group(1))
66 continue
67 m = re.compile(r'^\}\s+// namespace').search(raw_line)
68 if m:
69 namespaces = namespaces[0:len(namespaces) - 1]
70 continue
71
72 # Is this the start or end of an enclosing class or struct?
Elliott Hughes48257562012-06-06 17:42:44 -070073 m = re.compile(r'^(?:class|struct)(?: MANAGED)? (\S+).* \{').search(raw_line)
Elliott Hughes460384f2012-04-04 16:53:10 -070074 if m:
75 enclosing_classes.append(m.group(1))
76 continue
77 m = re.compile(r'^\};').search(raw_line)
78 if m:
79 enclosing_classes = enclosing_classes[0:len(enclosing_classes) - 1]
80 continue
81
Elliott Hughes0e57ccb2012-04-03 16:04:52 -070082 continue
83
84 # Is this the end of the current enum?
85 m = _ENUM_END_RE.search(raw_line)
86 if m:
87 if not in_enum:
88 Confused(filename, line_number, raw_line)
89 in_enum = False
90 continue
91
Elliott Hughesa9719eb2012-06-07 11:09:48 -070092 # The only useful thing in comments is the <<alternate text>> syntax for
93 # overriding the default enum value names. Pull that out...
94 enum_text = None
95 m_comment = re.compile(r'// <<(.*?)>>').search(raw_line)
96 if m_comment:
97 enum_text = m_comment.group(1)
98 # ...and then strip // comments.
99 line = re.sub(r'//.*', '', raw_line)
Elliott Hughes48257562012-06-06 17:42:44 -0700100
101 # Strip whitespace.
102 line = line.strip()
103
104 # Skip blank lines.
Elliott Hughes460384f2012-04-04 16:53:10 -0700105 if len(line) == 0:
106 continue
107
Elliott Hughesa9719eb2012-06-07 11:09:48 -0700108 # Since we know we're in an enum type, and we're not looking at a comment
109 # or a blank line, this line should be the next enum value...
Elliott Hughes460384f2012-04-04 16:53:10 -0700110 m = _ENUM_VALUE_RE.search(line)
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700111 if not m:
112 Confused(filename, line_number, raw_line)
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700113 enum_value = m.group(1)
114
115 # By default, we turn "kSomeValue" into "SomeValue".
Elliott Hughesa9719eb2012-06-07 11:09:48 -0700116 if enum_text == None:
117 enum_text = enum_value
118 if enum_text.startswith('k'):
119 enum_text = enum_text[1:]
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700120
121 # Lose literal values because we don't care; turn "= 123, // blah" into ", // blah".
122 rest = m.group(2).strip()
Elliott Hughes460384f2012-04-04 16:53:10 -0700123 m_literal = re.compile(r'= (0x[0-9a-f]+|-?[0-9]+|\'.\')').search(rest)
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700124 if m_literal:
125 rest = rest[(len(m_literal.group(0))):]
126
127 # With "kSomeValue = kOtherValue," we take the original and skip later synonyms.
128 # TODO: check that the rhs is actually an existing value.
129 if rest.startswith('= k'):
130 continue
131
132 # Remove any trailing comma and whitespace
133 if rest.startswith(','):
134 rest = rest[1:]
135 rest = rest.strip()
136
Elliott Hughesa9719eb2012-06-07 11:09:48 -0700137 # There shouldn't be anything left.
138 if len(rest):
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700139 Confused(filename, line_number, raw_line)
140
Elliott Hughes460384f2012-04-04 16:53:10 -0700141 if len(enclosing_classes) > 0:
142 enum_value = '::'.join(enclosing_classes) + '::' + enum_value
143
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700144 _ENUMS[enum_name].append((enum_value, enum_text))
145
146def main():
Brian Carlstrom7940e442013-07-12 13:46:57 -0700147 local_path = sys.argv[1]
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700148 header_files = []
Brian Carlstrom7940e442013-07-12 13:46:57 -0700149 for header_file in sys.argv[2:]:
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700150 header_files.append(header_file)
151 ProcessFile(header_file)
152
153 print '#include <iostream>'
154 print
155
156 for header_file in header_files:
Brian Carlstrom7940e442013-07-12 13:46:57 -0700157 header_file = header_file.replace(local_path + '/', '')
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700158 print '#include "%s"' % header_file
159
160 print
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700161
162 for enum_name in _ENUMS:
163 print '// This was automatically generated by %s --- do not edit!' % sys.argv[0]
Elliott Hughes460384f2012-04-04 16:53:10 -0700164
165 namespaces = _NAMESPACES[enum_name].split('::')
166 for namespace in namespaces:
167 print 'namespace %s {' % namespace
168
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700169 print 'std::ostream& operator<<(std::ostream& os, const %s& rhs) {' % enum_name
170 print ' switch (rhs) {'
171 for (enum_value, enum_text) in _ENUMS[enum_name]:
172 print ' case %s: os << "%s"; break;' % (enum_value, enum_text)
173 print ' default: os << "%s[" << static_cast<int>(rhs) << "]"; break;' % enum_name
174 print ' }'
175 print ' return os;'
176 print '}'
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700177
Elliott Hughes460384f2012-04-04 16:53:10 -0700178 for namespace in reversed(namespaces):
179 print '} // namespace %s' % namespace
180 print
Elliott Hughes0e57ccb2012-04-03 16:04:52 -0700181
182 sys.exit(0)
183
184
185if __name__ == '__main__':
186 main()